{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "chat-kit",
  "title": "Chat Kit",
  "description": "Orchestero AI chat component group for threads, messages, composers, and tool calls.",
  "registryDependencies": [
    "@orchestero/avatar",
    "@orchestero/badge",
    "@orchestero/button",
    "@orchestero/collapsible",
    "@orchestero/empty",
    "@orchestero/spinner",
    "@orchestero/textarea"
  ],
  "files": [
    {
      "path": "components/chat/composer.tsx",
      "content": "\"use client\"\n\nimport { LoaderCircleIcon, SendIcon, SquareIcon } from \"lucide-react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport { Spinner } from \"@/components/ui/spinner\"\nimport { Textarea } from \"@/components/ui/textarea\"\nimport { cn } from \"@/lib/utils\"\n\ntype ChatComposerStatus = \"ready\" | \"submitted\" | \"streaming\" | \"error\"\n\nfunction ChatComposer({ className, ...props }: React.ComponentProps<\"form\">) {\n  return (\n    <form\n      data-slot=\"chat-composer\"\n      className={cn(\n        \"flex min-w-0 flex-col gap-2 rounded-xl border bg-background p-2 shadow-sm focus-within:border-ring focus-within:ring-3 focus-within:ring-ring/50\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction ChatComposerTextarea({\n  className,\n  ...props\n}: React.ComponentProps<typeof Textarea>) {\n  return (\n    <Textarea\n      data-slot=\"chat-composer-textarea\"\n      className={cn(\n        \"max-h-48 min-h-20 resize-none border-0 bg-transparent p-2 shadow-none focus-visible:ring-0\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction ChatComposerToolbar({\n  className,\n  ...props\n}: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"chat-composer-toolbar\"\n      className={cn(\"flex items-center justify-between gap-2\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction ChatComposerTools({\n  className,\n  ...props\n}: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"chat-composer-tools\"\n      className={cn(\"flex min-w-0 items-center gap-1\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction ChatComposerSubmit({\n  status = \"ready\",\n  disabled,\n  children,\n  ...props\n}: React.ComponentProps<typeof Button> & {\n  status?: ChatComposerStatus\n}) {\n  const isBusy = status === \"submitted\" || status === \"streaming\"\n\n  return (\n    <Button\n      data-slot=\"chat-composer-submit\"\n      type=\"submit\"\n      size=\"icon\"\n      disabled={disabled || status === \"submitted\"}\n      aria-label={isBusy ? \"Stop generation\" : \"Send message\"}\n      {...props}\n    >\n      {children ??\n        (status === \"streaming\" ? (\n          <SquareIcon aria-hidden=\"true\" />\n        ) : status === \"submitted\" ? (\n          <Spinner aria-hidden=\"true\" />\n        ) : status === \"error\" ? (\n          <LoaderCircleIcon aria-hidden=\"true\" />\n        ) : (\n          <SendIcon aria-hidden=\"true\" />\n        ))}\n    </Button>\n  )\n}\n\nexport {\n  ChatComposer,\n  ChatComposerSubmit,\n  ChatComposerTextarea,\n  ChatComposerToolbar,\n  ChatComposerTools,\n  type ChatComposerStatus,\n}\n",
      "type": "registry:component",
      "target": "@components/chat/composer.tsx"
    },
    {
      "path": "components/chat/index.ts",
      "content": "export * from \"./composer\"\nexport * from \"./message\"\nexport * from \"./thread\"\nexport * from \"./tool-call\"\n",
      "type": "registry:component",
      "target": "@components/chat/index.ts"
    },
    {
      "path": "components/chat/message.tsx",
      "content": "import { Avatar, AvatarFallback, AvatarImage } from \"@/components/ui/avatar\"\nimport { Badge } from \"@/components/ui/badge\"\nimport { cn } from \"@/lib/utils\"\n\ntype ChatMessageRole = \"user\" | \"assistant\" | \"system\" | \"tool\"\n\nfunction ChatMessage({\n  from = \"assistant\",\n  className,\n  ...props\n}: React.ComponentProps<\"article\"> & {\n  from?: ChatMessageRole\n}) {\n  return (\n    <article\n      data-slot=\"chat-message\"\n      data-role={from}\n      className={cn(\n        \"group/chat-message flex w-full min-w-0 items-start gap-3 data-[role=user]:flex-row-reverse\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction ChatMessageAvatar({\n  src,\n  fallback,\n  className,\n  ...props\n}: React.ComponentProps<typeof Avatar> & {\n  src?: string\n  fallback?: React.ReactNode\n}) {\n  return (\n    <Avatar\n      data-slot=\"chat-message-avatar\"\n      size=\"sm\"\n      className={cn(\"mt-1\", className)}\n      {...props}\n    >\n      {src ? <AvatarImage src={src} alt=\"\" /> : null}\n      <AvatarFallback>{fallback ?? \"AI\"}</AvatarFallback>\n    </Avatar>\n  )\n}\n\nfunction ChatMessageBody({ className, ...props }: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"chat-message-body\"\n      className={cn(\n        \"flex max-w-[min(42rem,85%)] min-w-0 flex-col gap-2 group-data-[role=user]/chat-message:items-end\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction ChatMessageContent({\n  className,\n  ...props\n}: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"chat-message-content\"\n      className={cn(\n        \"rounded-xl bg-muted px-3 py-2 text-sm leading-6 text-foreground group-data-[role=assistant]/chat-message:bg-transparent group-data-[role=assistant]/chat-message:px-0 group-data-[role=assistant]/chat-message:py-0 group-data-[role=user]/chat-message:bg-primary group-data-[role=user]/chat-message:text-primary-foreground\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction ChatMessageMeta({\n  className,\n  ...props\n}: React.ComponentProps<typeof Badge>) {\n  return (\n    <Badge\n      data-slot=\"chat-message-meta\"\n      variant=\"outline\"\n      className={cn(\"font-normal\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction ChatMessageActions({\n  className,\n  ...props\n}: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"chat-message-actions\"\n      className={cn(\"flex items-center gap-1\", className)}\n      {...props}\n    />\n  )\n}\n\nexport {\n  ChatMessage,\n  ChatMessageActions,\n  ChatMessageAvatar,\n  ChatMessageBody,\n  ChatMessageContent,\n  ChatMessageMeta,\n  type ChatMessageRole,\n}\n",
      "type": "registry:component",
      "target": "@components/chat/message.tsx"
    },
    {
      "path": "components/chat/thread.tsx",
      "content": "import { MessageSquareIcon } from \"lucide-react\"\n\nimport {\n  Empty,\n  EmptyDescription,\n  EmptyHeader,\n  EmptyMedia,\n  EmptyTitle,\n} from \"@/components/ui/empty\"\nimport { cn } from \"@/lib/utils\"\n\nfunction ChatThread({ className, ...props }: React.ComponentProps<\"section\">) {\n  return (\n    <section\n      data-slot=\"chat-thread\"\n      className={cn(\"flex min-h-0 flex-1 flex-col bg-background\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction ChatThreadContent({\n  className,\n  ...props\n}: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"chat-thread-content\"\n      role=\"log\"\n      aria-live=\"polite\"\n      aria-relevant=\"additions text\"\n      className={cn(\n        \"flex min-h-0 flex-1 flex-col gap-4 overflow-y-auto px-4 py-6\",\n        className\n      )}\n      {...props}\n    />\n  )\n}\n\nfunction ChatThreadFooter({\n  className,\n  ...props\n}: React.ComponentProps<\"div\">) {\n  return (\n    <div\n      data-slot=\"chat-thread-footer\"\n      className={cn(\"shrink-0 border-t bg-background p-4\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction ChatThreadEmpty({\n  title = \"Start a conversation\",\n  description = \"Send a message to begin chatting with the assistant.\",\n  icon,\n  className,\n  children,\n  ...props\n}: React.ComponentProps<typeof Empty> & {\n  title?: React.ReactNode\n  description?: React.ReactNode\n  icon?: React.ReactNode\n}) {\n  return (\n    <Empty\n      data-slot=\"chat-thread-empty\"\n      className={cn(\"border-0\", className)}\n      {...props}\n    >\n      <EmptyHeader>\n        <EmptyMedia variant=\"icon\">\n          {icon ?? <MessageSquareIcon aria-hidden=\"true\" />}\n        </EmptyMedia>\n        <EmptyTitle>{title}</EmptyTitle>\n        <EmptyDescription>{description}</EmptyDescription>\n      </EmptyHeader>\n      {children}\n    </Empty>\n  )\n}\n\nexport { ChatThread, ChatThreadContent, ChatThreadEmpty, ChatThreadFooter }\n",
      "type": "registry:component",
      "target": "@components/chat/thread.tsx"
    },
    {
      "path": "components/chat/tool-call.tsx",
      "content": "\"use client\"\n\nimport { ChevronDownIcon } from \"lucide-react\"\n\nimport { Badge } from \"@/components/ui/badge\"\nimport {\n  Collapsible,\n  CollapsibleContent,\n  CollapsibleTrigger,\n} from \"@/components/ui/collapsible\"\nimport { cn } from \"@/lib/utils\"\n\ntype ChatToolCallState = \"pending\" | \"running\" | \"completed\" | \"error\"\n\nfunction formatValue(value: unknown) {\n  if (typeof value === \"string\") {\n    return value\n  }\n\n  return JSON.stringify(value, null, 2)\n}\n\nfunction ChatToolCall({\n  className,\n  ...props\n}: React.ComponentProps<typeof Collapsible>) {\n  return (\n    <Collapsible\n      data-slot=\"chat-tool-call\"\n      className={cn(\"rounded-xl border bg-muted/40\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction ChatToolCallHeader({\n  name,\n  state = \"pending\",\n  className,\n  children,\n  ...props\n}: React.ComponentProps<typeof CollapsibleTrigger> & {\n  name: string\n  state?: ChatToolCallState\n}) {\n  return (\n    <CollapsibleTrigger\n      data-slot=\"chat-tool-call-header\"\n      className={cn(\n        \"group flex w-full items-center justify-between gap-3 px-3 py-2 text-left text-sm outline-none focus-visible:ring-3 focus-visible:ring-ring/50\",\n        className\n      )}\n      {...props}\n    >\n      <span className=\"flex min-w-0 items-center gap-2\">\n        <span className=\"truncate font-medium\">{name}</span>\n        <Badge\n          variant={state === \"error\" ? \"destructive\" : \"secondary\"}\n          className=\"capitalize\"\n        >\n          {state}\n        </Badge>\n      </span>\n      {children ?? (\n        <ChevronDownIcon\n          aria-hidden=\"true\"\n          className=\"shrink-0 transition-transform group-data-[panel-open]:rotate-180\"\n        />\n      )}\n    </CollapsibleTrigger>\n  )\n}\n\nfunction ChatToolCallContent({\n  className,\n  ...props\n}: React.ComponentProps<typeof CollapsibleContent>) {\n  return (\n    <CollapsibleContent\n      data-slot=\"chat-tool-call-content\"\n      className={cn(\"border-t px-3 py-3\", className)}\n      {...props}\n    />\n  )\n}\n\nfunction ChatToolCallInput({\n  input,\n  className,\n  ...props\n}: React.ComponentProps<\"pre\"> & {\n  input: unknown\n}) {\n  return (\n    <pre\n      data-slot=\"chat-tool-call-input\"\n      className={cn(\n        \"max-h-72 overflow-auto rounded-lg bg-background p-3 text-xs leading-5 text-muted-foreground\",\n        className\n      )}\n      {...props}\n    >\n      {formatValue(input)}\n    </pre>\n  )\n}\n\nfunction ChatToolCallOutput({\n  output,\n  error,\n  className,\n  ...props\n}: React.ComponentProps<\"div\"> & {\n  output?: React.ReactNode\n  error?: React.ReactNode\n}) {\n  return (\n    <div\n      data-slot=\"chat-tool-call-output\"\n      data-error={Boolean(error)}\n      className={cn(\n        \"rounded-lg bg-background p-3 text-sm data-[error=true]:text-destructive\",\n        className\n      )}\n      {...props}\n    >\n      {error ?? output}\n    </div>\n  )\n}\n\nexport {\n  ChatToolCall,\n  ChatToolCallContent,\n  ChatToolCallHeader,\n  ChatToolCallInput,\n  ChatToolCallOutput,\n  type ChatToolCallState,\n}\n",
      "type": "registry:component",
      "target": "@components/chat/tool-call.tsx"
    }
  ],
  "meta": {
    "group": "chat"
  },
  "categories": [
    "ai",
    "chat"
  ],
  "type": "registry:component"
}