{
  "name": "ai-prompt",
  "type": "registry:component",
  "dependencies": [
    "lucide-react",
    "motion"
  ],
  "registryDependencies": [
    "textarea",
    "button",
    "dropdown-menu"
  ],
  "files": [
    {
      "type": "registry:component",
      "content": "\"use client\";\n\n/**\n * @author: @kokonutui\n * @description: AI Prompt Input\n * @version: 1.0.0\n * @date: 2025-06-26\n * @license: MIT\n * @website: https://kokonutui.com\n * @github: https://github.com/kokonut-labs/kokonutui\n */\n\nimport { ArrowRight, Bot, Check, ChevronDown, Paperclip } from \"lucide-react\";\nimport { AnimatePresence, motion } from \"motion/react\";\nimport { useState } from \"react\";\nimport Anthropic from \"@/components/icons/anthropic\";\nimport AnthropicDark from \"@/components/icons/anthropic-dark\";\nimport { Button } from \"@/components/ui/button\";\nimport {\n  DropdownMenu,\n  DropdownMenuContent,\n  DropdownMenuItem,\n  DropdownMenuTrigger,\n} from \"@/components/ui/dropdown-menu\";\nimport { Textarea } from \"@/components/ui/textarea\";\nimport { useAutoResizeTextarea } from \"@/hooks/use-auto-resize-textarea\";\nimport { cn } from \"@/lib/utils\";\n\nconst OPENAI_SVG = (\n  <div>\n    <svg\n      aria-label=\"o3-mini icon\"\n      className=\"block dark:hidden\"\n      height=\"260\"\n      preserveAspectRatio=\"xMidYMid\"\n      viewBox=\"0 0 256 260\"\n      width=\"256\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <title>OpenAI Icon Light</title>\n      <path d=\"M239.184 106.203a64.716 64.716 0 0 0-5.576-53.103C219.452 28.459 191 15.784 163.213 21.74A65.586 65.586 0 0 0 52.096 45.22a64.716 64.716 0 0 0-43.23 31.36c-14.31 24.602-11.061 55.634 8.033 76.74a64.665 64.665 0 0 0 5.525 53.102c14.174 24.65 42.644 37.324 70.446 31.36a64.72 64.72 0 0 0 48.754 21.744c28.481.025 53.714-18.361 62.414-45.481a64.767 64.767 0 0 0 43.229-31.36c14.137-24.558 10.875-55.423-8.083-76.483Zm-97.56 136.338a48.397 48.397 0 0 1-31.105-11.255l1.535-.87 51.67-29.825a8.595 8.595 0 0 0 4.247-7.367v-72.85l21.845 12.636c.218.111.37.32.409.563v60.367c-.056 26.818-21.783 48.545-48.601 48.601Zm-104.466-44.61a48.345 48.345 0 0 1-5.781-32.589l1.534.921 51.722 29.826a8.339 8.339 0 0 0 8.441 0l63.181-36.425v25.221a.87.87 0 0 1-.358.665l-52.335 30.184c-23.257 13.398-52.97 5.431-66.404-17.803ZM23.549 85.38a48.499 48.499 0 0 1 25.58-21.333v61.39a8.288 8.288 0 0 0 4.195 7.316l62.874 36.272-21.845 12.636a.819.819 0 0 1-.767 0L41.353 151.53c-23.211-13.454-31.171-43.144-17.804-66.405v.256Zm179.466 41.695-63.08-36.63L161.73 77.86a.819.819 0 0 1 .768 0l52.233 30.184a48.6 48.6 0 0 1-7.316 87.635v-61.391a8.544 8.544 0 0 0-4.4-7.213Zm21.742-32.69-1.535-.922-51.619-30.081a8.39 8.39 0 0 0-8.492 0L99.98 99.808V74.587a.716.716 0 0 1 .307-.665l52.233-30.133a48.652 48.652 0 0 1 72.236 50.391v.205ZM88.061 139.097l-21.845-12.585a.87.87 0 0 1-.41-.614V65.685a48.652 48.652 0 0 1 79.757-37.346l-1.535.87-51.67 29.825a8.595 8.595 0 0 0-4.246 7.367l-.051 72.697Zm11.868-25.58 28.138-16.217 28.188 16.218v32.434l-28.086 16.218-28.188-16.218-.052-32.434Z\" />\n    </svg>\n    <svg\n      aria-label=\"o3-mini icon\"\n      className=\"hidden dark:block\"\n      height=\"260\"\n      preserveAspectRatio=\"xMidYMid\"\n      viewBox=\"0 0 256 260\"\n      width=\"256\"\n      xmlns=\"http://www.w3.org/2000/svg\"\n    >\n      <title>OpenAI Icon Dark</title>\n      <path\n        d=\"M239.184 106.203a64.716 64.716 0 0 0-5.576-53.103C219.452 28.459 191 15.784 163.213 21.74A65.586 65.586 0 0 0 52.096 45.22a64.716 64.716 0 0 0-43.23 31.36c-14.31 24.602-11.061 55.634 8.033 76.74a64.665 64.665 0 0 0 5.525 53.102c14.174 24.65 42.644 37.324 70.446 31.36a64.72 64.72 0 0 0 48.754 21.744c28.481.025 53.714-18.361 62.414-45.481a64.767 64.767 0 0 0 43.229-31.36c14.137-24.558 10.875-55.423-8.083-76.483Zm-97.56 136.338a48.397 48.397 0 0 1-31.105-11.255l1.535-.87 51.67-29.825a8.595 8.595 0 0 0 4.247-7.367v-72.85l21.845 12.636c.218.111.37.32.409.563v60.367c-.056 26.818-21.783 48.545-48.601 48.601Zm-104.466-44.61a48.345 48.345 0 0 1-5.781-32.589l1.534.921 51.722 29.826a8.339 8.339 0 0 0 8.441 0l63.181-36.425v25.221a.87.87 0 0 1-.358.665l-52.335 30.184c-23.257 13.398-52.97 5.431-66.404-17.803ZM23.549 85.38a48.499 48.499 0 0 1 25.58-21.333v61.39a8.288 8.288 0 0 0 4.195 7.316l62.874 36.272-21.845 12.636a.819.819 0 0 1-.767 0L41.353 151.53c-23.211-13.454-31.171-43.144-17.804-66.405v.256Zm179.466 41.695-63.08-36.63L161.73 77.86a.819.819 0 0 1 .768 0l52.233 30.184a48.6 48.6 0 0 1-7.316 87.635v-61.391a8.544 8.544 0 0 0-4.4-7.213Zm21.742-32.69-1.535-.922-51.619-30.081a8.39 8.39 0 0 0-8.492 0L99.98 99.808V74.587a.716.716 0 0 1 .307-.665l52.233-30.133a48.652 48.652 0 0 1 72.236 50.391v.205ZM88.061 139.097l-21.845-12.585a.87.87 0 0 1-.41-.614V65.685a48.652 48.652 0 0 1 79.757-37.346l-1.535.87-51.67 29.825a8.595 8.595 0 0 0-4.246 7.367l-.051 72.697Zm11.868-25.58 28.138-16.217 28.188 16.218v32.434l-28.086 16.218-28.188-16.218-.052-32.434Z\"\n        fill=\"#fff\"\n      />\n    </svg>\n  </div>\n);\n\ninterface AIPromptProps {\n  models?: string[];\n  defaultModel?: string;\n  placeholder?: string;\n  headerText?: string;\n  headerAction?: string;\n  onSubmit?: (value: string, model: string) => void;\n  className?: string;\n}\n\nconst DEFAULT_MODELS = [\n  \"Gemini 3\",\n  \"GPT-5-mini\",\n  \"Claude 4.5 Sonnet\",\n  \"GPT-5-1 Mini\",\n  \"GPT-5-1\",\n];\n\nexport default function AI_Prompt({\n  models = DEFAULT_MODELS,\n  defaultModel = \"Claude 4.5 Sonnet\",\n  placeholder = \"What can I do for you?\",\n  headerText = \"is free this weekend!\",\n  headerAction = \"Ship Now!\",\n  onSubmit,\n  className,\n}: AIPromptProps) {\n  const [value, setValue] = useState(\"\");\n  const { textareaRef, adjustHeight } = useAutoResizeTextarea({\n    minHeight: 72,\n    maxHeight: 300,\n  });\n  const [selectedModel, setSelectedModel] = useState(defaultModel);\n\n  const MODEL_ICONS: Record<string, React.ReactNode> = {\n    \"GPT-5-mini\": OPENAI_SVG,\n    \"Gemini 3\": (\n      <svg\n        height=\"1em\"\n        style={{ flex: \"none\", lineHeight: \"1\" }}\n        viewBox=\"0 0 24 24\"\n        xmlns=\"http://www.w3.org/2000/svg\"\n      >\n        <title>Gemini</title>\n        <defs>\n          <linearGradient\n            id=\"lobe-icons-gemini-fill\"\n            x1=\"0%\"\n            x2=\"68.73%\"\n            y1=\"100%\"\n            y2=\"30.395%\"\n          >\n            <stop offset=\"0%\" stopColor=\"#1C7DFF\" />\n            <stop offset=\"52.021%\" stopColor=\"#1C69FF\" />\n            <stop offset=\"100%\" stopColor=\"#F0DCD6\" />\n          </linearGradient>\n        </defs>\n        <path\n          d=\"M12 24A14.304 14.304 0 000 12 14.304 14.304 0 0012 0a14.305 14.305 0 0012 12 14.305 14.305 0 00-12 12\"\n          fill=\"url(#lobe-icons-gemini-fill)\"\n          fillRule=\"nonzero\"\n        />\n      </svg>\n    ),\n    \"Claude 4.5 Sonnet\": (\n      <div>\n        <svg\n          className=\"block dark:hidden\"\n          fill=\"#000\"\n          fillRule=\"evenodd\"\n          style={{ flex: \"none\", lineHeight: \"1\" }}\n          viewBox=\"0 0 24 24\"\n          width=\"1em\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n        >\n          <title>Anthropic Icon Light</title>\n          <path d=\"M13.827 3.52h3.603L24 20h-3.603l-6.57-16.48zm-7.258 0h3.767L16.906 20h-3.674l-1.343-3.461H5.017l-1.344 3.46H0L6.57 3.522zm4.132 9.959L8.453 7.687 6.205 13.48H10.7z\" />\n        </svg>\n        <svg\n          className=\"hidden dark:block\"\n          fill=\"#ffff\"\n          fillRule=\"evenodd\"\n          style={{ flex: \"none\", lineHeight: \"1\" }}\n          viewBox=\"0 0 24 24\"\n          width=\"1em\"\n          xmlns=\"http://www.w3.org/2000/svg\"\n        >\n          <title>Anthropic Icon Dark</title>\n          <path d=\"M13.827 3.52h3.603L24 20h-3.603l-6.57-16.48zm-7.258 0h3.767L16.906 20h-3.674l-1.343-3.461H5.017l-1.344 3.46H0L6.57 3.522zm4.132 9.959L8.453 7.687 6.205 13.48H10.7z\" />\n        </svg>\n      </div>\n    ),\n    \"GPT-5-1 Mini\": OPENAI_SVG,\n    \"GPT-5-1\": OPENAI_SVG,\n  };\n\n  const handleKeyDown = (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n    if (e.key === \"Enter\" && !e.shiftKey) {\n      e.preventDefault();\n      onSubmit?.(value, selectedModel);\n      setValue(\"\");\n      adjustHeight(true);\n    }\n  };\n\n  return (\n    <div className={cn(\"w-4/6 py-4\", className)}>\n      <div className=\"rounded-2xl bg-black/5 p-1.5 pt-4 dark:bg-white/5\">\n        <div className=\"mx-2 mb-2.5 flex items-center gap-2\">\n          <div className=\"flex flex-1 items-center gap-2\">\n            <Anthropic className=\"h-3.5 w-3.5 text-black dark:hidden\" />\n            <AnthropicDark className=\"hidden h-3.5 w-3.5 dark:block\" />\n            <h3 className=\"text-black text-xs tracking-tighter dark:text-white/90\">\n              {headerText}\n            </h3>\n          </div>\n          <p className=\"text-black text-xs tracking-tighter dark:text-white/90\">\n            {headerAction}\n          </p>\n        </div>\n        <div className=\"relative\">\n          <div className=\"relative flex flex-col\">\n            <div className=\"overflow-y-auto\" style={{ maxHeight: \"400px\" }}>\n              <Textarea\n                className={cn(\n                  \"w-full resize-none rounded-xl rounded-b-none border-none bg-black/5 px-4 py-3 placeholder:text-black/70 focus-visible:ring-0 focus-visible:ring-offset-0 dark:bg-white/5 dark:text-white dark:placeholder:text-white/70\",\n                  \"min-h-[72px]\"\n                )}\n                id=\"ai-input-15\"\n                onChange={(e) => {\n                  setValue(e.target.value);\n                  adjustHeight();\n                }}\n                onKeyDown={handleKeyDown}\n                placeholder={placeholder}\n                ref={textareaRef}\n                value={value}\n              />\n            </div>\n\n            <div className=\"flex h-14 items-center rounded-b-xl bg-black/5 dark:bg-white/5\">\n              <div className=\"absolute right-3 bottom-3 left-3 flex w-[calc(100%-24px)] items-center justify-between\">\n                <div className=\"flex items-center gap-2\">\n                  <DropdownMenu>\n                    <DropdownMenuTrigger asChild>\n                      <Button\n                        className=\"flex h-8 items-center gap-1 rounded-md pr-2 pl-1 text-xs hover:bg-black/10 focus-visible:ring-1 focus-visible:ring-blue-500 focus-visible:ring-offset-0 dark:text-white dark:hover:bg-white/10\"\n                        variant=\"ghost\"\n                      >\n                        <AnimatePresence mode=\"wait\">\n                          <motion.div\n                            animate={{\n                              opacity: 1,\n                              y: 0,\n                            }}\n                            className=\"flex items-center gap-1\"\n                            exit={{\n                              opacity: 0,\n                              y: 5,\n                            }}\n                            initial={{\n                              opacity: 0,\n                              y: -5,\n                            }}\n                            key={selectedModel}\n                            transition={{\n                              duration: 0.15,\n                            }}\n                          >\n                            {MODEL_ICONS[selectedModel]}\n                            {selectedModel}\n                            <ChevronDown className=\"h-3 w-3 opacity-50\" />\n                          </motion.div>\n                        </AnimatePresence>\n                      </Button>\n                    </DropdownMenuTrigger>\n                    <DropdownMenuContent\n                      className={cn(\n                        \"min-w-[10rem]\",\n                        \"border-black/10 dark:border-white/10\",\n                        \"bg-gradient-to-b from-white via-white to-neutral-100 dark:from-neutral-950 dark:via-neutral-900 dark:to-neutral-800\"\n                      )}\n                    >\n                      {models.map((model) => (\n                        <DropdownMenuItem\n                          className=\"flex items-center justify-between gap-2\"\n                          key={model}\n                          onSelect={() => setSelectedModel(model)}\n                        >\n                          <div className=\"flex items-center gap-2\">\n                            {MODEL_ICONS[model] || (\n                              <Bot className=\"h-4 w-4 opacity-50\" />\n                            )}{\" \"}\n                            {/* Use mapped SVG or fallback */}\n                            <span>{model}</span>\n                          </div>\n                          {selectedModel === model && (\n                            <Check className=\"h-4 w-4 text-blue-500\" />\n                          )}\n                        </DropdownMenuItem>\n                      ))}\n                    </DropdownMenuContent>\n                  </DropdownMenu>\n                  <div className=\"mx-0.5 h-4 w-px bg-black/10 dark:bg-white/10\" />\n                  <label\n                    aria-label=\"Attach file\"\n                    className={cn(\n                      \"cursor-pointer rounded-lg bg-black/5 p-2 dark:bg-white/5\",\n                      \"hover:bg-black/10 focus-visible:ring-1 focus-visible:ring-blue-500 focus-visible:ring-offset-0 dark:hover:bg-white/10\",\n                      \"text-black/40 hover:text-black dark:text-white/40 dark:hover:text-white\"\n                    )}\n                  >\n                    <input className=\"hidden\" type=\"file\" />\n                    <Paperclip className=\"h-4 w-4 transition-colors\" />\n                  </label>\n                </div>\n                <button\n                  aria-label=\"Send message\"\n                  className={cn(\n                    \"rounded-lg bg-black/5 p-2 dark:bg-white/5\",\n                    \"hover:bg-black/10 focus-visible:ring-1 focus-visible:ring-blue-500 focus-visible:ring-offset-0 dark:hover:bg-white/10\"\n                  )}\n                  disabled={!value.trim()}\n                  type=\"button\"\n                >\n                  <ArrowRight\n                    className={cn(\n                      \"h-4 w-4 transition-opacity duration-200 dark:text-white\",\n                      value.trim() ? \"opacity-100\" : \"opacity-30\"\n                    )}\n                  />\n                </button>\n              </div>\n            </div>\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n",
      "path": "/components/kokonutui/ai-prompt.tsx",
      "target": "components/kokonutui/ai-prompt.tsx"
    },
    {
      "type": "registry:hook",
      "content": "import { useCallback, useEffect, useRef } from \"react\";\n\ninterface UseAutoResizeTextareaProps {\n  minHeight: number;\n  maxHeight?: number;\n}\n\nexport function useAutoResizeTextarea({\n  minHeight,\n  maxHeight,\n}: UseAutoResizeTextareaProps) {\n  const textareaRef = useRef<HTMLTextAreaElement>(null);\n\n  const adjustHeight = useCallback(\n    (reset?: boolean) => {\n      const textarea = textareaRef.current;\n      if (!textarea) return;\n\n      if (reset) {\n        textarea.style.height = `${minHeight}px`;\n        return;\n      }\n\n      // Temporarily shrink to get the right scrollHeight\n      textarea.style.height = `${minHeight}px`;\n\n      // Calculate new height\n      const newHeight = Math.max(\n        minHeight,\n        Math.min(textarea.scrollHeight, maxHeight ?? Number.POSITIVE_INFINITY)\n      );\n\n      textarea.style.height = `${newHeight}px`;\n    },\n    [minHeight, maxHeight]\n  );\n\n  useEffect(() => {\n    // Set initial height\n    const textarea = textareaRef.current;\n    if (textarea) {\n      textarea.style.height = `${minHeight}px`;\n    }\n  }, [minHeight]);\n\n  // Adjust height on window resize\n  useEffect(() => {\n    const handleResize = () => adjustHeight();\n    window.addEventListener(\"resize\", handleResize);\n    return () => window.removeEventListener(\"resize\", handleResize);\n  }, [adjustHeight]);\n\n  return { textareaRef, adjustHeight };\n}\n",
      "path": "/hooks/use-auto-resize-textarea.ts",
      "target": "hooks/use-auto-resize-textarea.ts"
    },
    {
      "type": "registry:component",
      "content": "const Anthropic = (props: React.SVGProps<SVGSVGElement>) => (\n  <svg\n    fill=\"#000\"\n    fillRule=\"evenodd\"\n    height=\"1em\"\n    style={{\n      flex: \"none\",\n      lineHeight: 1,\n    }}\n    viewBox=\"0 0 24 24\"\n    width=\"1em\"\n    xmlns=\"http://www.w3.org/2000/svg\"\n    {...props}\n  >\n    <title>{\"Anthropic\"}</title>\n    <path d=\"M13.827 3.52h3.603L24 20h-3.603l-6.57-16.48zm-7.258 0h3.767L16.906 20h-3.674l-1.343-3.461H5.017l-1.344 3.46H0L6.57 3.522zm4.132 9.959L8.453 7.687 6.205 13.48H10.7z\" />\n  </svg>\n);\nexport default Anthropic;\n",
      "path": "/components/icons/anthropic.tsx",
      "target": "components/kokonutui/anthropic.tsx"
    },
    {
      "type": "registry:component",
      "content": "import type { SVGProps } from \"react\";\n\nconst AnthropicDark = (props: SVGProps<SVGSVGElement>) => (\n  <svg\n    fill=\"#ffff\"\n    fillRule=\"evenodd\"\n    height=\"1em\"\n    style={{\n      flex: \"none\",\n      lineHeight: 1,\n    }}\n    viewBox=\"0 0 24 24\"\n    width=\"1em\"\n    xmlns=\"http://www.w3.org/2000/svg\"\n    {...props}\n  >\n    <title>{\"Anthropic\"}</title>\n    <path d=\"M13.827 3.52h3.603L24 20h-3.603l-6.57-16.48zm-7.258 0h3.767L16.906 20h-3.674l-1.343-3.461H5.017l-1.344 3.46H0L6.57 3.522zm4.132 9.959L8.453 7.687 6.205 13.48H10.7z\" />\n  </svg>\n);\nexport default AnthropicDark;\n",
      "path": "/components/icons/anthropic-dark.tsx",
      "target": "components/kokonutui/anthropic-dark.tsx"
    }
  ]
}