{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "chat-input",
  "title": "AI Chat Input",
  "description": "Expandable chat input with send button, file attachments, and keyboard shortcuts",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "button"
  ],
  "files": [
    {
      "path": "registry/default/blocks/ai/ai-chat-input/components/elements/ai-chat-input.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\n\nimport { Loader2, Paperclip } from \"lucide-react\";\n\nimport { cn } from \"@/lib/utils\";\n\ninterface AiChatInputProps {\n  onSubmit?: (message: string, attachments?: File[]) => void;\n  placeholder?: string;\n  disabled?: boolean;\n  loading?: boolean;\n  allowAttachments?: boolean;\n  maxRows?: number;\n  className?: string;\n}\n\nexport function AiChatInput({\n  onSubmit,\n  placeholder = \"Type a message...\",\n  disabled = false,\n  loading = false,\n  allowAttachments = false,\n  maxRows = 6,\n  className,\n}: AiChatInputProps) {\n  const [value, setValue] = React.useState(\"\");\n  const [attachments, setAttachments] = React.useState<File[]>([]);\n  const textareaRef = React.useRef<HTMLTextAreaElement>(null);\n  const fileInputRef = React.useRef<HTMLInputElement>(null);\n\n  const isDisabled = disabled || loading;\n  const canSubmit = value.trim().length > 0 && !isDisabled;\n\n  const adjustHeight = React.useCallback(() => {\n    const textarea = textareaRef.current;\n    if (!textarea) return;\n\n    textarea.style.height = \"auto\";\n    const lineHeight = 24;\n    const minHeight = 72;\n    const maxHeight = lineHeight * maxRows;\n    const newHeight = Math.max(minHeight, Math.min(textarea.scrollHeight, maxHeight));\n    textarea.style.height = `${newHeight}px`;\n  }, [maxRows]);\n\n  React.useEffect(() => {\n    adjustHeight();\n  }, [adjustHeight]);\n\n  const handleSubmit = React.useCallback(() => {\n    if (!canSubmit) return;\n\n    onSubmit?.(value.trim(), attachments.length > 0 ? attachments : undefined);\n    setValue(\"\");\n    setAttachments([]);\n\n    if (textareaRef.current) {\n      textareaRef.current.style.height = \"72px\";\n    }\n  }, [value, attachments, canSubmit, onSubmit]);\n\n  const handleKeyDown = React.useCallback(\n    (e: React.KeyboardEvent<HTMLTextAreaElement>) => {\n      if (e.key === \"Enter\" && (e.metaKey || e.ctrlKey)) {\n        e.preventDefault();\n        handleSubmit();\n      }\n    },\n    [handleSubmit]\n  );\n\n  const handleFileChange = React.useCallback(\n    (e: React.ChangeEvent<HTMLInputElement>) => {\n      const files = Array.from(e.target.files || []);\n      setAttachments((prev) => [...prev, ...files]);\n      if (fileInputRef.current) {\n        fileInputRef.current.value = \"\";\n      }\n    },\n    []\n  );\n\n  const removeAttachment = React.useCallback((index: number) => {\n    setAttachments((prev) => prev.filter((_, i) => i !== index));\n  }, []);\n\n  return (\n    <div\n      data-slot=\"ai-chat-input\"\n      className={cn(\"w-full font-mono\", className)}\n    >\n      {attachments.length > 0 && (\n        <div className=\"mb-2 flex flex-wrap gap-2\">\n          {attachments.map((file, index) => (\n            <div\n              key={`${file.name}-${index}`}\n              className=\"flex items-center gap-1.5 border bg-muted px-2 py-1 text-xs\"\n            >\n              <Paperclip className=\"size-3 text-muted-foreground\" />\n              <span className=\"max-w-[150px] truncate uppercase tracking-wider\">\n                {file.name}\n              </span>\n              <button\n                type=\"button\"\n                onClick={() => removeAttachment(index)}\n                className=\"ml-1 text-muted-foreground hover:text-foreground\"\n                disabled={isDisabled}\n                aria-label={`Remove ${file.name}`}\n              >\n                ×\n              </button>\n            </div>\n          ))}\n        </div>\n      )}\n\n      <div className=\"flex items-end gap-0 border bg-background focus-within:ring-1 focus-within:ring-ring\">\n        {allowAttachments && (\n          <>\n            <input\n              ref={fileInputRef}\n              type=\"file\"\n              multiple\n              onChange={handleFileChange}\n              className=\"hidden\"\n              disabled={isDisabled}\n            />\n            <button\n              type=\"button\"\n              className=\"flex size-10 shrink-0 items-center justify-center border-r text-muted-foreground transition-colors hover:bg-muted hover:text-foreground disabled:opacity-50\"\n              onClick={() => fileInputRef.current?.click()}\n              disabled={isDisabled}\n            >\n              <Paperclip className=\"size-4\" />\n              <span className=\"sr-only\">Attach files</span>\n            </button>\n          </>\n        )}\n\n        <textarea\n          ref={textareaRef}\n          value={value}\n          onChange={(e) => setValue(e.target.value)}\n          onKeyDown={handleKeyDown}\n          placeholder={placeholder}\n          disabled={isDisabled}\n          rows={3}\n          aria-label=\"Chat message input\"\n          className={cn(\n            \"flex-1 resize-none bg-transparent text-sm outline-none placeholder:text-muted-foreground/50 disabled:cursor-not-allowed disabled:opacity-50\",\n            \"min-h-[72px] px-3 py-2.5\"\n          )}\n        />\n\n        <button\n          type=\"button\"\n          className=\"shrink-0 border-l px-4 py-2.5 text-xs uppercase tracking-wider transition-colors hover:bg-muted disabled:opacity-30\"\n          onClick={handleSubmit}\n          disabled={!canSubmit}\n        >\n          {loading ? <Loader2 className=\"size-4 animate-spin\" /> : \"SEND\"}\n          <span className=\"sr-only\">Send message</span>\n        </button>\n      </div>\n    </div>\n  );\n}\n\nexport type { AiChatInputProps };\n",
      "type": "registry:component"
    }
  ],
  "docs": "Auto-expanding textarea with Cmd+Enter to send. Supports file attachments and loading states.",
  "categories": [
    "ai"
  ],
  "type": "registry:ui"
}