{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ai-input",
  "title": "AI Input",
  "description": "A polished AI input component with model selection, tools, file uploads, and smooth animations.",
  "dependencies": [
    "motion",
    "clsx",
    "tailwind-merge",
    "lucide-react"
  ],
  "files": [
    {
      "path": "registry/chamaac/ai-input/ai-input.tsx",
      "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport { m, LazyMotion, domMax, AnimatePresence } from \"motion/react\";\nimport Image from \"next/image\";\nimport React, {\n  useState,\n  useRef,\n  useEffect,\n  createContext,\n  useContext,\n} from \"react\";\nimport {\n  Mic,\n  ArrowUp,\n  Sparkles,\n  ChevronDown,\n  X,\n  Plus,\n  Check,\n  Globe,\n  Video,\n  Image as ImageIcon,\n  Layout,\n  BookOpen,\n  Paperclip,\n  File,\n  Square,\n  LucideIcon,\n} from \"lucide-react\";\n\n// =============================================================================\n// TYPE DEFINITIONS\n// =============================================================================\n\ntype IconComponent = React.ComponentType<{ className?: string }>;\n\ninterface AIInputContextType {\n  activeDropdown: \"plus\" | \"tools\" | \"model\" | null;\n  setActiveDropdown: (dropdown: \"plus\" | \"tools\" | \"model\" | null) => void;\n}\n\ninterface Model {\n  id: string;\n  name: string;\n  label: string;\n  icon: LucideIcon;\n}\n\ninterface MenuItem {\n  id: string;\n  icon: LucideIcon;\n  label: string;\n}\n\ninterface ToolItem {\n  icon: LucideIcon;\n  label: string;\n}\n\ninterface Attachment {\n  preview: string;\n  type: \"image\" | \"file\" | \"video\";\n}\n\ninterface Message {\n  id: string;\n  role: \"user\" | \"ai\";\n  content: string;\n  attachments?: Attachment[];\n}\n\ninterface UploadedFile {\n  id: string;\n  file: File;\n  preview: string;\n  type: \"image\" | \"file\" | \"video\";\n}\n\n// =============================================================================\n// CONSTANTS & DEFAULTS\n// =============================================================================\n\nconst DEFAULT_MODELS: Model[] = [\n  { id: \"gpt4o\", name: \"GPT-4o\", label: \"GPT-4o\", icon: Sparkles },\n  { id: \"gpt4\", name: \"GPT-4\", label: \"GPT-4\", icon: Sparkles },\n  { id: \"claude\", name: \"Claude 3.5\", label: \"Claude 3.5\", icon: Sparkles },\n  {\n    id: \"claude-opus\",\n    name: \"Claude 4.5 Opus\",\n    label: \"Claude 4.5 Opus\",\n    icon: Sparkles,\n  },\n];\n\nconst DEFAULT_PLUS_MENU: MenuItem[] = [\n  { id: \"files\", icon: Paperclip, label: \"Upload photos & files\" },\n  { id: \"videos\", icon: Video, label: \"Upload Videos\" },\n];\n\nconst DEFAULT_TOOLS: ToolItem[] = [\n  { icon: Globe, label: \"Deep Research\" },\n  { icon: Video, label: \"Create videos\" },\n  { icon: ImageIcon, label: \"Create images\" },\n  { icon: Layout, label: \"Canvas\" },\n  { icon: BookOpen, label: \"Guided Learning\" },\n];\n\n// =============================================================================\n// CONTEXT\n// =============================================================================\n\nconst AIInputContext = createContext<AIInputContextType | undefined>(undefined);\n\nexport const useAIInput = () => {\n  const context = useContext(AIInputContext);\n  if (!context) {\n    throw new Error(\"useAIInput must be used within an AIInput component\");\n  }\n  return context;\n};\n\n// =============================================================================\n// DROPDOWN COMPONENT\n// =============================================================================\n\ninterface DropdownItem {\n  icon?: IconComponent;\n  label: string;\n  onClick?: () => void;\n}\n\ninterface AIInputDropdownProps<T> {\n  isOpen: boolean;\n  onClose: () => void;\n  items: T[];\n  renderItem?: (item: T, index: number) => React.ReactNode;\n  className?: string;\n}\n\nexport function AIInputDropdown<T extends DropdownItem>({\n  isOpen,\n  onClose,\n  items,\n  renderItem,\n  className,\n}: AIInputDropdownProps<T>) {\n  return (\n    <AnimatePresence>\n      {isOpen && (\n        <>\n          <div\n            role=\"button\"\n            tabIndex={-1}\n            aria-label=\"Dismiss\"\n            className=\"fixed inset-0 z-40 bg-transparent\"\n            onClick={onClose}\n            onKeyDown={(e) => {\n              if (e.key === \"Escape\") onClose();\n            }}\n          />\n          <m.div\n            initial={{ opacity: 0, scale: 0.9, y: 10 }}\n            animate={{ opacity: 1, scale: 1, y: 0 }}\n            exit={{ opacity: 0, scale: 0.9, y: 10 }}\n            transition={{ type: \"spring\", duration: 0.3, bounce: 0 }}\n            className={cn(\n              \"absolute bottom-full left-0 mb-2 bg-white dark:bg-[#1a1a1a] border border-black/5 dark:border-white/10 rounded-2xl shadow-xl overflow-hidden z-50 p-1.5\",\n              className\n            )}\n          >\n            <div className=\"flex flex-col gap-0.5\">\n              {items.map((item, index) =>\n                renderItem ? (\n                  <div key={item.label} role=\"presentation\" onClick={onClose}>\n                    {renderItem(item, index)}\n                  </div>\n                ) : (\n                  <button\n                    key={item.label}\n                    onClick={() => {\n                      item.onClick?.();\n                      onClose();\n                    }}\n                    className=\"flex items-center gap-2 px-2 py-2.5 w-full text-left text-zinc-600 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-white/10 rounded-2xl transition-colors group\"\n                  >\n                    {item.icon && (\n                      <item.icon className=\"w-4 h-4 text-zinc-400 group-hover:text-zinc-600 dark:group-hover:text-zinc-200 transition-colors\" />\n                    )}\n                    <span className=\"text-sm font-medium\">{item.label}</span>\n                  </button>\n                )\n              )}\n            </div>\n          </m.div>\n        </>\n      )}\n    </AnimatePresence>\n  );\n}\nAIInputDropdown.displayName = \"AIInputDropdown\";\n\n// =============================================================================\n// PILL BUTTON COMPONENT\n// =============================================================================\n\ninterface AIInputPillButtonProps {\n  children: React.ReactNode;\n  isActive?: boolean;\n  showChevron?: boolean;\n  chevronRotated?: boolean;\n  showClose?: boolean;\n  onClose?: () => void;\n  onClick?: () => void;\n  layoutId?: string;\n  className?: string;\n  icon?: IconComponent;\n}\n\nexport function AIInputPillButton({\n  children,\n  isActive = false,\n  showChevron = false,\n  chevronRotated = false,\n  showClose = false,\n  onClose,\n  onClick,\n  layoutId,\n  className,\n  icon: Icon,\n}: AIInputPillButtonProps) {\n  const baseStyles =\n    \"flex items-center gap-2 px-3 py-2 rounded-full transition-colors border cursor-pointer\";\n  const activeStyles =\n    \"bg-zinc-100 dark:bg-zinc-800 text-zinc-900 dark:text-zinc-100 border-black/10 dark:border-white/10\";\n  const inactiveStyles =\n    \"bg-zinc-50 dark:bg-zinc-900 text-zinc-600 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-zinc-800 border-black/5 dark:border-white/5\";\n\n  const pillContent = (\n    <>\n      {Icon && <Icon className=\"w-4 h-4 text-zinc-500\" />}\n      {children}\n      {showChevron && (\n        <ChevronDown\n          className={cn(\n            \"w-4 h-4 text-zinc-400 transition-transform\",\n            chevronRotated && \"rotate-180\"\n          )}\n        />\n      )}\n    </>\n  );\n\n  if (showClose) {\n    return (\n      <m.div\n        layoutId={layoutId}\n        layout\n        transition={{ duration: 0.3 }}\n        className={cn(\n          baseStyles,\n          isActive ? activeStyles : inactiveStyles,\n          className\n        )}\n      >\n        <button\n          onClick={onClick}\n          className=\"flex items-center gap-2 cursor-pointer\"\n        >\n          {pillContent}\n        </button>\n        <button\n          onClick={(e) => {\n            e.stopPropagation();\n            onClose?.();\n          }}\n          className=\"ml-1 p-0.5 rounded-full bg-zinc-200 dark:bg-zinc-700 text-zinc-500 dark:text-zinc-400 flex items-center justify-center transition-colors hover:bg-zinc-300 dark:hover:bg-zinc-600 cursor-pointer\"\n        >\n          <X className=\"w-3 h-3\" />\n        </button>\n      </m.div>\n    );\n  }\n\n  return (\n    <m.button\n      layoutId={layoutId}\n      layout\n      onClick={onClick}\n      transition={{ duration: 0.3 }}\n      className={cn(\n        baseStyles,\n        isActive ? activeStyles : inactiveStyles,\n        className\n      )}\n    >\n      {pillContent}\n    </m.button>\n  );\n}\nAIInputPillButton.displayName = \"AIInputPillButton\";\n\n// =============================================================================\n// MESSAGES AREA COMPONENT\n// =============================================================================\n\ninterface AIInputMessagesProps {\n  messages: Message[];\n  hasSubmitted: boolean;\n  messagesEndRef: React.RefObject<HTMLDivElement | null>;\n}\n\nexport function AIInputMessages({\n  messages,\n  hasSubmitted,\n  messagesEndRef,\n}: AIInputMessagesProps) {\n  return (\n    <m.div\n      layout\n      className={cn(\n        \"w-full max-w-2xl mx-auto flex flex-col gap-6 overflow-y-auto px-4 hide-scrollbar\",\n        hasSubmitted ? \"flex-1 pt-10\" : \"hidden\"\n      )}\n    >\n      {hasSubmitted && (\n        <>\n          {messages.map((msg) => (\n            <m.div\n              initial={{ opacity: 0, y: 20, scale: 0.95 }}\n              animate={{ opacity: 1, y: 0, scale: 1 }}\n              key={msg.id}\n              className={cn(\n                \"flex flex-col gap-2 max-w-[85%]\",\n                msg.role === \"user\" ? \"ml-auto items-end\" : \"items-start\"\n              )}\n            >\n              {msg.attachments && msg.attachments.length > 0 && (\n                <div className=\"flex flex-wrap gap-2 justify-end\">\n                  {msg.attachments.map((attachment, attachIdx) => (\n                    <div key={attachIdx} className=\"relative\">\n                      {attachment.type === \"image\" ? (\n                        <div className=\"relative w-20 h-20 rounded-[12px] overflow-hidden border border-black/5 dark:border-white/10\">\n                          <Image\n                            src={attachment.preview}\n                            alt=\"Attachment\"\n                            fill\n                            sizes=\"80px\"\n                            className=\"object-cover\"\n                          />\n                        </div>\n                      ) : attachment.type === \"video\" ? (\n                        <div className=\"relative w-32 h-32 rounded-lg overflow-hidden bg-zinc-200 dark:bg-zinc-700 border border-black/5 dark:border-white/10\">\n                          <video\n                            src={attachment.preview}\n                            className=\"w-full h-full object-cover\"\n                          />\n                        </div>\n                      ) : (\n                        <div className=\"w-20 h-20 rounded-lg bg-zinc-100 dark:bg-zinc-800 border border-black/5 dark:border-white/10 flex items-center justify-center\">\n                          <File className=\"w-8 h-8 text-zinc-500\" />\n                        </div>\n                      )}\n                    </div>\n                  ))}\n                </div>\n              )}\n              {msg.content && (\n                <div\n                  className={cn(\n                    \"p-2 rounded-[12px]\",\n                    msg.role === \"user\"\n                      ? \"bg-zinc-100 dark:bg-zinc-800 text-zinc-900 dark:text-zinc-100\"\n                      : \"text-zinc-900 dark:text-zinc-100\"\n                  )}\n                >\n                  {msg.role === \"ai\" && (\n                    <div className=\"flex items-center gap-2 mb-2 text-xs font-medium text-neutral-500\">\n                      <Sparkles className=\"w-3 h-3\" />\n                      AI Response\n                    </div>\n                  )}\n                  {msg.content}\n                </div>\n              )}\n            </m.div>\n          ))}\n          <div className=\"h-24 flex-shrink-0\" />\n          <div ref={messagesEndRef} />\n        </>\n      )}\n    </m.div>\n  );\n}\nAIInputMessages.displayName = \"AIInputMessages\";\n\n// =============================================================================\n// FILE PREVIEW COMPONENT\n// =============================================================================\n\ninterface AIInputFilePreviewProps {\n  files: UploadedFile[];\n  onRemove: (id: string) => void;\n}\n\nexport function AIInputFilePreview({\n  files,\n  onRemove,\n}: AIInputFilePreviewProps) {\n  return (\n    <AnimatePresence>\n      {files.length > 0 && (\n        <m.div\n          layout\n          initial={{ opacity: 0, height: 0 }}\n          animate={{\n            opacity: 1,\n            height: \"auto\",\n            transition: { ease: \"easeInOut\" },\n          }}\n          exit={{\n            opacity: 0,\n            height: 0,\n            transition: { duration: 0.2, ease: \"easeInOut\" },\n          }}\n          className=\"overflow-hidden\"\n        >\n          <div className=\"px-4 pt-4 pb-2 flex flex-wrap gap-2\">\n            {files.map((file) => (\n              <m.div\n                key={file.id}\n                initial={{ opacity: 0, scale: 0.8 }}\n                animate={{ opacity: 1, scale: 1 }}\n                exit={{ opacity: 0, scale: 0.8 }}\n                layout\n                className=\"relative group/file\"\n              >\n                {file.type === \"image\" ? (\n                  <div className=\"relative w-16 h-16 rounded-[12px] overflow-hidden border border-black/5 dark:border-white/10\">\n                    <Image\n                      src={file.preview}\n                      alt={file.file.name}\n                      fill\n                      sizes=\"64px\"\n                      className=\"object-cover\"\n                    />\n                  </div>\n                ) : file.type === \"video\" ? (\n                  <div className=\"relative w-16 h-16 rounded-lg overflow-hidden border border-black/5 dark:border-white/10 bg-zinc-100 dark:bg-zinc-800 flex items-center justify-center\">\n                    <video\n                      src={file.preview}\n                      className=\"w-full h-full object-cover\"\n                    />\n                  </div>\n                ) : (\n                  <div className=\"w-16 h-16 rounded-lg border border-black/5 dark:border-white/10 bg-zinc-100 dark:bg-zinc-800 flex flex-col items-center justify-center gap-1 p-1\">\n                    <File className=\"w-5 h-5 text-zinc-500\" />\n                    <span className=\"text-[8px] text-zinc-500 truncate w-full text-center\">\n                      {file.file.name.split(\".\").pop()?.toUpperCase()}\n                    </span>\n                  </div>\n                )}\n                <button\n                  onClick={() => onRemove(file.id)}\n                  className=\"absolute -top-1.5 -right-1.5 w-5 h-5 rounded-full dark:bg-zinc-800 bg-zinc-100 text-zinc-500 dark:text-zinc-400 flex items-center justify-center border border-black/5 dark:border-white/10 cursor-pointer\"\n                >\n                  <X className=\"w-3 h-3\" />\n                </button>\n              </m.div>\n            ))}\n          </div>\n        </m.div>\n      )}\n    </AnimatePresence>\n  );\n}\nAIInputFilePreview.displayName = \"AIInputFilePreview\";\n\n// =============================================================================\n// MAIN AI INPUT COMPONENT\n// =============================================================================\n\ninterface AIInputProps {\n  models?: Model[];\n  tools?: ToolItem[];\n  plusMenuItems?: MenuItem[];\n  onSubmit?: (message: string, attachments: Attachment[]) => void;\n  placeholder?: string;\n  className?: string;\n}\n\nexport function AIInput({\n  models = DEFAULT_MODELS,\n  tools = DEFAULT_TOOLS,\n  plusMenuItems = DEFAULT_PLUS_MENU,\n  onSubmit,\n  placeholder = \"Ask anything...\",\n  className,\n}: AIInputProps) {\n  const [value, setValue] = useState<string>(\"\");\n  const [messages, setMessages] = useState<Message[]>([]);\n  const [hasSubmitted, setHasSubmitted] = useState<boolean>(false);\n  const [isListening, setIsListening] = useState<boolean>(false);\n  const [selectedTool, setSelectedTool] = useState<ToolItem | null>(null);\n  const [selectedModel, setSelectedModel] = useState<Model>(models[0]);\n  const [uploadedFiles, setUploadedFiles] = useState<UploadedFile[]>([]);\n  const [activeDropdown, setActiveDropdown] = useState<\n    \"plus\" | \"tools\" | \"model\" | null\n  >(null);\n\n  const fileInputRef = useRef<HTMLInputElement>(null);\n  const videoInputRef = useRef<HTMLInputElement>(null);\n  const messagesEndRef = useRef<HTMLDivElement>(null);\n\n  const hasText = value.length > 0;\n\n  useEffect(() => {\n    if (messagesEndRef.current) {\n      messagesEndRef.current.scrollIntoView({ behavior: \"smooth\" });\n    }\n  }, [messages]);\n\n  const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {\n    const files = e.target.files;\n    if (!files) return;\n\n    const newFiles: UploadedFile[] = Array.from(files).map((file) => {\n      const isImage = file.type.startsWith(\"image/\");\n      const isVideo = file.type.startsWith(\"video/\");\n      return {\n        id: `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,\n        file,\n        preview: isImage || isVideo ? URL.createObjectURL(file) : \"\",\n        type: isVideo ? \"video\" : isImage ? \"image\" : \"file\",\n      };\n    });\n\n    setUploadedFiles((prev) => [...prev, ...newFiles]);\n    e.target.value = \"\";\n  };\n\n  const removeFile = (id: string) => {\n    setUploadedFiles((prev) => {\n      const file = prev.find((f) => f.id === id);\n      if (file?.preview) URL.revokeObjectURL(file.preview);\n      return prev.filter((f) => f.id !== id);\n    });\n  };\n\n  const handlePlusMenuClick = (itemId: string) => {\n    setActiveDropdown(null);\n    if (itemId === \"files\") fileInputRef.current?.click();\n    else if (itemId === \"videos\") videoInputRef.current?.click();\n  };\n\n  const handleSubmit = () => {\n    if (!value.trim() && uploadedFiles.length === 0) return;\n\n    setHasSubmitted(true);\n    const attachments = uploadedFiles.map((file) => ({\n      preview: file.preview,\n      type: file.type,\n    }));\n\n    setMessages((prev) => [\n      ...prev,\n      {\n        id: `msg-${Date.now()}`,\n        role: \"user\",\n        content: value,\n        attachments: attachments.length > 0 ? attachments : undefined,\n      },\n    ]);\n\n    if (onSubmit) {\n      onSubmit(value, attachments);\n    }\n\n    setValue(\"\");\n    setUploadedFiles([]);\n\n    // Simulate AI reply (remove in production)\n    setTimeout(() => {\n      setMessages((prev) => [\n        ...prev,\n        {\n          id: `msg-${Date.now()}-ai`,\n          role: \"ai\",\n          content: `Your response content here...`,\n        },\n      ]);\n    }, 500);\n  };\n\n  const handleKeyDown = (e: React.KeyboardEvent) => {\n    if (e.key === \"Enter\" && !e.shiftKey) {\n      e.preventDefault();\n      handleSubmit();\n    }\n  };\n\n  return (\n    <LazyMotion features={domMax}>\n      <AIInputContext.Provider value={{ activeDropdown, setActiveDropdown }}>\n        <div\n          className={cn(\n            \"w-full h-[100dvh] flex flex-col relative overflow-hidden\",\n            className\n          )}\n        >\n          <AIInputMessages\n            messages={messages}\n            hasSubmitted={hasSubmitted}\n            messagesEndRef={messagesEndRef}\n          />\n\n          <m.div\n            layout\n            transition={{ type: \"spring\", damping: 25, stiffness: 200 }}\n            className={cn(\n              \"w-full px-4 flex flex-col z-20\",\n              hasSubmitted ? \"pb-8\" : \"flex-1 justify-center items-center\"\n            )}\n          >\n            <div className=\"w-full max-w-2xl mx-auto relative group\">\n              <m.div\n                layoutId=\"input-container\"\n                layout\n                transition={{ duration: 0.3, ease: \"easeInOut\" }}\n                className=\"relative bg-white dark:bg-[#09090b] rounded-[32px] border border-black/5 dark:border-white/5\"\n              >\n                <input\n                  ref={fileInputRef}\n                  type=\"file\"\n                  multiple\n                  accept=\"image/*,.pdf,.doc,.docx,.txt,.md\"\n                  className=\"hidden\"\n                  onChange={handleFileSelect}\n                />\n                <input\n                  ref={videoInputRef}\n                  type=\"file\"\n                  multiple\n                  accept=\"video/*\"\n                  className=\"hidden\"\n                  onChange={handleFileSelect}\n                />\n\n                <AIInputFilePreview\n                  files={uploadedFiles}\n                  onRemove={removeFile}\n                />\n\n                <div className=\"p-4 pb-14\">\n                  <m.textarea\n                    layout\n                    transition={{ duration: 0.2, ease: \"easeInOut\" }}\n                    value={value}\n                    onChange={(e) => setValue(e.target.value)}\n                    onKeyDown={handleKeyDown}\n                    disabled={isListening}\n                    placeholder={isListening ? \"Listening...\" : placeholder}\n                    className=\"w-full bg-transparent text-lg text-zinc-900 dark:text-zinc-100 placeholder:text-zinc-400 dark:placeholder:text-zinc-500 resize-none outline-none min-h-[40px] max-h-[200px]\"\n                    rows={1}\n                    style={{ minHeight: \"44px\", height: \"auto\" }}\n                    onInput={(e) => {\n                      const target = e.target as HTMLTextAreaElement;\n                      target.style.height = \"auto\";\n                      target.style.height = `${target.scrollHeight}px`;\n                    }}\n                  />\n                </div>\n\n                {/* Bottom Controls */}\n                <div className=\"absolute bottom-4 left-4 right-4 flex justify-between items-center z-10\">\n                  {/* Left Side */}\n                  <div className=\"flex items-center gap-2\">\n                    <div className=\"relative\">\n                      <button\n                        onClick={() =>\n                          setActiveDropdown(\n                            activeDropdown === \"plus\" ? null : \"plus\"\n                          )\n                        }\n                        className={cn(\n                          \"p-2.5 rounded-full transition-colors border\",\n                          activeDropdown === \"plus\"\n                            ? \"bg-zinc-100 dark:bg-zinc-800 text-zinc-900 dark:text-zinc-100 border-black/10 dark:border-white/10\"\n                            : \"bg-zinc-50 dark:bg-zinc-900 text-zinc-500 dark:text-zinc-400 hover:bg-zinc-100 dark:hover:bg-zinc-800 border-black/5 dark:border-white/5\"\n                        )}\n                      >\n                        <Plus\n                          className={cn(\n                            \"w-5 h-5 transition-transform\",\n                            activeDropdown === \"plus\" && \"rotate-45\"\n                          )}\n                        />\n                      </button>\n                      <AIInputDropdown\n                        isOpen={activeDropdown === \"plus\"}\n                        onClose={() => setActiveDropdown(null)}\n                        items={plusMenuItems}\n                        className=\"w-56 bottom-full left-0 mb-2\"\n                        renderItem={(item) => (\n                          <button\n                            onClick={() => handlePlusMenuClick(item.id)}\n                            className=\"flex items-center gap-2 px-4 py-3 w-full text-left text-zinc-600 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-white/10 rounded-2xl transition-colors group\"\n                          >\n                            <item.icon className=\"w-4 h-4 text-zinc-400 group-hover:text-zinc-600 dark:group-hover:text-zinc-200 transition-colors\" />\n                            <span className=\"text-sm font-medium\">\n                              {item.label}\n                            </span>\n                          </button>\n                        )}\n                      />\n                    </div>\n\n                    <div className=\"relative hidden sm:block\">\n                      {selectedTool ? (\n                        <AIInputPillButton\n                          layoutId=\"tools-pill\"\n                          icon={selectedTool.icon}\n                          isActive={activeDropdown === \"tools\"}\n                          showChevron\n                          chevronRotated={activeDropdown === \"tools\"}\n                          showClose\n                          onClick={() =>\n                            setActiveDropdown(\n                              activeDropdown === \"tools\" ? null : \"tools\"\n                            )\n                          }\n                          onClose={() => {\n                            setSelectedTool(null);\n                            setActiveDropdown(null);\n                          }}\n                        >\n                          <span className=\"text-sm font-medium\">\n                            {selectedTool.label}\n                          </span>\n                        </AIInputPillButton>\n                      ) : (\n                        <AIInputPillButton\n                          layoutId=\"tools-pill\"\n                          icon={Sparkles}\n                          isActive={activeDropdown === \"tools\"}\n                          showChevron\n                          chevronRotated={activeDropdown === \"tools\"}\n                          onClick={() =>\n                            setActiveDropdown(\n                              activeDropdown === \"tools\" ? null : \"tools\"\n                            )\n                          }\n                        >\n                          <span className=\"text-sm font-medium\">Tools</span>\n                        </AIInputPillButton>\n                      )}\n\n                      <AIInputDropdown\n                        isOpen={activeDropdown === \"tools\"}\n                        onClose={() => setActiveDropdown(null)}\n                        items={tools}\n                        className=\"w-64 bottom-full left-0 mb-2\"\n                        renderItem={(item) => (\n                          <button\n                            onClick={() => {\n                              setSelectedTool(item);\n                              setActiveDropdown(null);\n                            }}\n                            className={cn(\n                              \"flex items-center gap-3 px-4 py-3 w-full text-left text-zinc-600 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-white/10 rounded-2xl transition-colors group\",\n                              selectedTool?.label === item.label &&\n                                \"bg-zinc-100 dark:bg-zinc-800\"\n                            )}\n                          >\n                            <item.icon className=\"w-4 h-4 text-zinc-400 group-hover:text-zinc-600 dark:group-hover:text-zinc-200 transition-colors\" />\n                            <span className=\"text-sm font-medium\">\n                              {item.label}\n                            </span>\n                          </button>\n                        )}\n                      />\n                    </div>\n                  </div>\n\n                  {/* Right Side */}\n                  <div className=\"flex items-center gap-2\">\n                    <div className=\"relative\">\n                      <AIInputPillButton\n                        layoutId=\"model-pill\"\n                        icon={selectedModel.icon}\n                        isActive={activeDropdown === \"model\"}\n                        showChevron\n                        chevronRotated={activeDropdown === \"model\"}\n                        onClick={() =>\n                          setActiveDropdown(\n                            activeDropdown === \"model\" ? null : \"model\"\n                          )\n                        }\n                      >\n                        <span className=\"text-sm font-medium\">\n                          {selectedModel.name}\n                        </span>\n                      </AIInputPillButton>\n\n                      <AIInputDropdown\n                        isOpen={activeDropdown === \"model\"}\n                        onClose={() => setActiveDropdown(null)}\n                        items={models}\n                        className=\"w-48 bottom-full right-0 mb-2 p-1\"\n                        renderItem={(model) => (\n                          <button\n                            onClick={() => {\n                              setSelectedModel(model);\n                              setActiveDropdown(null);\n                            }}\n                            className={cn(\n                              \"flex items-center gap-3 px-4 py-3 w-full text-left text-zinc-600 dark:text-zinc-300 hover:bg-zinc-100 dark:hover:bg-white/10 rounded-2xl transition-colors group\",\n                              selectedModel.id === model.id &&\n                                \"bg-zinc-100 dark:bg-zinc-800\"\n                            )}\n                          >\n                            <model.icon className=\"w-4 h-4 text-zinc-400 group-hover:text-zinc-600 dark:group-hover:text-zinc-200 transition-colors\" />\n                            <span className=\"text-sm font-medium\">\n                              {model.name}\n                            </span>\n                            {selectedModel.id === model.id && (\n                              <Check className=\"w-4 h-4 ml-auto text-zinc-500\" />\n                            )}\n                          </button>\n                        )}\n                      />\n                    </div>\n\n                    <div className=\"flex justify-end\">\n                      <AnimatePresence mode=\"wait\" initial={false}>\n                        {hasText ? (\n                          <m.div\n                            key=\"active-controls\"\n                            initial={{ opacity: 0, scale: 0.9 }}\n                            animate={{ opacity: 1, scale: 1 }}\n                            exit={{ opacity: 0, scale: 0.9 }}\n                            transition={{ duration: 0.15 }}\n                            className=\"flex items-center gap-2\"\n                          >\n                            <button\n                              onClick={() => setValue(\"\")}\n                              className=\"p-2 text-zinc-400 hover:text-zinc-600 dark:text-zinc-500 dark:hover:text-zinc-300 transition-colors\"\n                            >\n                              <X className=\"w-4 h-4\" />\n                            </button>\n                            <button\n                              onClick={handleSubmit}\n                              className=\"p-2.5 rounded-full bg-zinc-900 dark:bg-zinc-100 text-white dark:text-zinc-900 hover:opacity-90 transition-opacity\"\n                            >\n                              <ArrowUp className=\"w-5 h-5\" />\n                            </button>\n                          </m.div>\n                        ) : (\n                          <m.div\n                            key=\"inactive-controls\"\n                            initial={{ opacity: 0, scale: 0.9 }}\n                            animate={{ opacity: 1, scale: 1 }}\n                            exit={{ opacity: 0, scale: 0.9 }}\n                            transition={{ duration: 0.15 }}\n                            className=\"flex items-center gap-2\"\n                          >\n                            <button\n                              onClick={() => setIsListening(!isListening)}\n                              className={cn(\n                                \"p-2 transition-all duration-300 relative cursor-pointer\",\n                                isListening\n                                  ? \"text-red-500 dark:text-red-400 bg-red-50 dark:bg-red-900/20 rounded-full\"\n                                  : \"text-zinc-400 hover:text-zinc-600 dark:text-zinc-500 dark:hover:text-zinc-300\"\n                              )}\n                            >\n                              {isListening ? (\n                                <Square\n                                  className=\"w-4 h-4\"\n                                  fill=\"currentColor\"\n                                />\n                              ) : (\n                                <Mic className=\"w-4 h-4\" />\n                              )}\n                              {isListening && (\n                                <span className=\"absolute inset-0 rounded-full animate-ping bg-red-500/20\" />\n                              )}\n                            </button>\n                            <button\n                              disabled\n                              className=\"p-2.5 rounded-full bg-zinc-100 dark:bg-zinc-800 text-zinc-300 dark:text-zinc-600\"\n                            >\n                              <ArrowUp className=\"w-4 h-4\" />\n                            </button>\n                          </m.div>\n                        )}\n                      </AnimatePresence>\n                    </div>\n                  </div>\n                </div>\n              </m.div>\n            </div>\n          </m.div>\n        </div>\n      </AIInputContext.Provider>\n    </LazyMotion>\n  );\n}\nAIInput.displayName = \"AIInput\";\n\nexport default AIInput;\n",
      "type": "registry:component"
    }
  ],
  "type": "registry:component"
}