{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ai-chat-tailwind",
  "type": "registry:item",
  "title": "AI Chat (Tailwind)",
  "description": "An AI chat input component with mode selector and action buttons.",
  "registryDependencies": [
    "https://roiui.com/r/button-tailwind.json",
    "https://roiui.com/r/card-tailwind.json",
    "https://roiui.com/r/field-tailwind.json",
    "https://roiui.com/r/form-tailwind.json",
    "https://roiui.com/r/select-tailwind.json"
  ],
  "files": [
    {
      "path": "registry/brook/tailwind/blocks/ai-chat/components/ai-chat.tsx",
      "content": "\"use client\";\n\nimport { ArrowUp, AudioLines, Paperclip } from \"lucide-react\";\nimport { useActionState, useState } from \"react\";\nimport { cn } from \"@/lib/utils\";\nimport { Button } from \"@/registry/brook/tailwind/ui/button\";\nimport { Card, CardContent, CardFooter } from \"@/registry/brook/tailwind/ui/card\";\nimport { Field, FieldControl } from \"@/registry/brook/tailwind/ui/field\";\nimport { Form } from \"@/registry/brook/tailwind/ui/form\";\nimport {\n  Select,\n  SelectIcon,\n  SelectItem,\n  SelectItemIndicator,\n  SelectItemText,\n  SelectList,\n  SelectPopup,\n  SelectPortal,\n  SelectPositioner,\n  SelectSpacer,\n  SelectTrigger,\n  SelectValue,\n} from \"@/registry/brook/tailwind/ui/select\";\n\nconst aiModes = [\n  { value: \"creative\", label: \"Creative\" },\n  { value: \"fast\", label: \"Fast\" },\n  { value: \"reasoning\", label: \"Reason\" },\n  { value: \"teach\", label: \"Teach\" },\n];\n\ntype FormState =\n  | { status: \"idle\" }\n  | { status: \"success\"; data: { message: string; mode: string } }\n  | { status: \"error\"; error: string; submittedData: { message: string; mode: string } };\n\nconst initialFormState: FormState = { status: \"idle\" };\n\nasync function chatAction(_prevState: FormState, formData: FormData): Promise<FormState> {\n  await new Promise((resolve) => setTimeout(resolve, 500));\n\n  const message = formData.get(\"message\") as string;\n  const mode = formData.get(\"mode\") as string;\n\n  return { status: \"success\", data: { message, mode } };\n}\n\nexport function AiChat() {\n  const [formState, submitAction, isPending] = useActionState(chatAction, initialFormState);\n  const [hasContent, setHasContent] = useState(false);\n\n  const defaultMessage = formState.status === \"error\" ? formState.submittedData.message : \"\";\n  const defaultMode = formState.status === \"error\" ? formState.submittedData.mode : aiModes[0].value;\n\n  return (\n    <Form action={submitAction} className=\"w-full max-sm:flex max-sm:items-center max-sm:justify-center\">\n      <Card\n        className={cn(\n          \"!gap-3 !p-3 mx-auto h-auto w-full rounded-[var(--radius-lg)] border-[oklch(from_var(--border)_l_c_h_/_0.25)] bg-[var(--mix-card-50-bg)] transition-[border-color] duration-200 ease-in-out focus-within:border-[oklch(from_var(--border)_l_c_h_/_0.5)] hover:border-[oklch(from_var(--border)_l_c_h_/_0.5)]\",\n          \"min-w-0 max-w-[600px]\",\n          \"max-sm:min-w-[230px]\"\n        )}\n      >\n        <CardContent>\n          <Field\n            className={cn(\n              \"[&_textarea]:!border-0 [&_textarea]:!bg-transparent [&_textarea]:!p-1 [&_textarea]:resize-none\",\n              \"[&_textarea]:h-auto [&_textarea]:min-h-10 [&_textarea]:leading-[1.5] [&_textarea]:focus:outline-none\",\n              \"max-xl:[&_textarea]:!p-2 max-xl:[&_textarea]:min-h-16\"\n            )}\n          >\n            <FieldControl\n              placeholder=\"How can I help…\"\n              render={\n                <textarea\n                  className=\"focus:outline-none\"\n                  defaultValue={defaultMessage}\n                  disabled={isPending}\n                  name=\"message\"\n                  onChange={(e) => setHasContent(e.target.value.trim().length > 0)}\n                />\n              }\n            />\n          </Field>\n        </CardContent>\n        <CardFooter className=\"flex items-center justify-between gap-2\">\n          <Button\n            aria-label=\"Attach file\"\n            className=\"!size-8 !rounded-full !p-2 shrink-0 [&>svg]:shrink-0 [&>svg]:rotate-[-45deg] [&>svg]:text-muted-foreground\"\n            size=\"icon\"\n            type=\"button\"\n            variant=\"ghost\"\n          >\n            <Paperclip size={14} />\n          </Button>\n\n          <div className=\"flex items-center gap-2\">\n            <Select defaultValue={defaultMode} items={aiModes} name=\"mode\">\n              <SelectTrigger\n                className=\"!transition-none hover:!bg-accent data-[popup-open]:!bg-accent\"\n                render={<Button className=\"!rounded-[var(--radius)]\" size=\"sm\" variant=\"ghost\" />}\n              >\n                <SelectValue>\n                  {(value) => {\n                    const selectedMode = aiModes.find((mode) => mode.value === value);\n                    return <span className=\"text-muted-foreground max-md:text-sm\">{selectedMode?.label}</span>;\n                  }}\n                </SelectValue>\n                <SelectIcon className=\"ml-1 max-md:hidden\" />\n              </SelectTrigger>\n              <SelectPortal>\n                <SelectPositioner align=\"start\" alignItemWithTrigger={false} side=\"top\" sideOffset={8}>\n                  <SelectPopup\n                    className=\"max-md:!w-[120px] max-md:!min-w-[120px] max-md:!max-w-[120px] box-border min-w-[140px]\"\n                    data-slot=\"select-popup\"\n                  >\n                    <SelectSpacer />\n                    <SelectList>\n                      {aiModes.map(({ label, value }) => (\n                        <SelectItem data-slot=\"select-item\" key={value} value={value}>\n                          <SelectItemText>{label}</SelectItemText>\n                          <SelectItemIndicator className=\"text-muted-foreground\" />\n                        </SelectItem>\n                      ))}\n                    </SelectList>\n                    <SelectSpacer />\n                  </SelectPopup>\n                </SelectPositioner>\n              </SelectPortal>\n            </Select>\n\n            <Button\n              aria-label={hasContent ? \"Send message\" : \"Start voice input\"}\n              className=\"!size-9 !rounded-full !bg-primary hover:not-disabled:!bg-[oklch(from_var(--primary)_calc(l*0.8)_c_h)] shrink-0\"\n              disabled={isPending}\n              size=\"icon\"\n              type=\"submit\"\n              variant=\"ghost\"\n            >\n              {hasContent ? (\n                <ArrowUp className=\"text-primary-foreground\" size={16} />\n              ) : (\n                <AudioLines className=\"text-primary-foreground\" size={16} />\n              )}\n            </Button>\n          </div>\n        </CardFooter>\n      </Card>\n    </Form>\n  );\n}\n",
      "type": "registry:file",
      "target": "~/components/blocks/ai-chat/ai-chat.tsx"
    }
  ]
}