{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "ai-reasoning",
  "type": "registry:ui",
  "description": "AI reasoning visualization component",
  "files": [
    {
      "path": "packages/ai/reasoning.tsx",
      "content": "'use client';\n\nimport { useControllableState } from '@radix-ui/react-use-controllable-state';\nimport {\n  Collapsible,\n  CollapsibleContent,\n  CollapsibleTrigger,\n} from '@repo/shadcn-ui/components/ui/collapsible';\nimport { ChevronDownIcon } from 'lucide-react';\nimport type { ComponentProps } from 'react';\nimport { createContext, memo, useContext, useEffect, useState } from 'react';\nimport { cn } from '@repo/shadcn-ui/lib/utils';\nimport { AIResponse } from './response';\n\ntype AIReasoningContextValue = {\n  isStreaming: boolean;\n  isOpen: boolean;\n  setIsOpen: (open: boolean) => void;\n  duration: number;\n};\n\nconst AIReasoningContext = createContext<AIReasoningContextValue | null>(null);\n\nconst useAIReasoning = () => {\n  const context = useContext(AIReasoningContext);\n  if (!context) {\n    throw new Error('AIReasoning components must be used within AIReasoning');\n  }\n  return context;\n};\n\nexport type AIReasoningProps = ComponentProps<typeof Collapsible> & {\n  isStreaming?: boolean;\n  open?: boolean;\n  defaultOpen?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  duration?: number;\n};\n\nexport const AIReasoning = memo(\n  ({\n    className,\n    isStreaming = false,\n    open,\n    defaultOpen = false,\n    onOpenChange,\n    duration: durationProp,\n    children,\n    ...props\n  }: AIReasoningProps) => {\n    const [isOpen, setIsOpen] = useControllableState({\n      prop: open,\n      defaultProp: defaultOpen,\n      onChange: onOpenChange,\n    });\n    const [duration, setDuration] = useControllableState({\n      prop: durationProp,\n      defaultProp: 0,\n    });\n\n    const [hasAutoClosedRef, setHasAutoClosedRef] = useState(false);\n    const [startTime, setStartTime] = useState<number | null>(null);\n\n    // Track duration when streaming starts and ends\n    useEffect(() => {\n      if (isStreaming) {\n        if (startTime === null) {\n          setStartTime(Date.now());\n        }\n      } else if (startTime !== null) {\n        setDuration(Math.round((Date.now() - startTime) / 1000));\n        setStartTime(null);\n      }\n    }, [isStreaming, startTime, setDuration]);\n\n    // Auto-open when streaming starts, auto-close when streaming ends (once only)\n    useEffect(() => {\n      if (isStreaming && !isOpen) {\n        setIsOpen(true);\n      } else if (!isStreaming && isOpen && !defaultOpen && !hasAutoClosedRef) {\n        // Add a small delay before closing to allow user to see the content\n        const timer = setTimeout(() => {\n          setIsOpen(false);\n          setHasAutoClosedRef(true);\n        }, 1000);\n        return () => clearTimeout(timer);\n      }\n    }, [isStreaming, isOpen, defaultOpen, setIsOpen, hasAutoClosedRef]);\n\n    const handleOpenChange = (open: boolean) => {\n      setIsOpen(open);\n    };\n\n    return (\n      <AIReasoningContext.Provider\n        value={{ isStreaming, isOpen, setIsOpen, duration }}\n      >\n        <Collapsible\n          className={cn('not-prose mb-4', className)}\n          onOpenChange={handleOpenChange}\n          open={isOpen}\n          {...props}\n        >\n          {children}\n        </Collapsible>\n      </AIReasoningContext.Provider>\n    );\n  }\n);\n\nexport type AIReasoningTriggerProps = ComponentProps<\n  typeof CollapsibleTrigger\n> & {\n  title?: string;\n};\n\nexport const AIReasoningTrigger = memo(\n  ({\n    className,\n    title = 'Reasoning',\n    children,\n    ...props\n  }: AIReasoningTriggerProps) => {\n    const { isStreaming, isOpen, duration } = useAIReasoning();\n\n    return (\n      <CollapsibleTrigger\n        className={cn(\n          'flex items-center gap-2 text-muted-foreground text-sm',\n          className\n        )}\n        {...props}\n      >\n        {children ?? (\n          <>\n            {isStreaming && duration === 0 ? (\n              <p>Thinking...</p>\n            ) : (\n              <p>Thought for {duration} seconds</p>\n            )}\n            <ChevronDownIcon\n              className={cn(\n                'size-4 text-muted-foreground transition-transform',\n                isOpen ? 'rotate-180' : 'rotate-0'\n              )}\n            />\n          </>\n        )}\n      </CollapsibleTrigger>\n    );\n  }\n);\n\nexport type AIReasoningContentProps = ComponentProps<\n  typeof CollapsibleContent\n> & {\n  children: string;\n};\n\nexport const AIReasoningContent = memo(\n  ({ className, children, ...props }: AIReasoningContentProps) => (\n    <CollapsibleContent\n      className={cn('mt-4 text-muted-foreground text-sm', className)}\n      {...props}\n    >\n      <AIResponse className=\"grid gap-2\">{children}</AIResponse>\n    </CollapsibleContent>\n  )\n);\n\nAIReasoning.displayName = 'AIReasoning';\nAIReasoningTrigger.displayName = 'AIReasoningTrigger';\nAIReasoningContent.displayName = 'AIReasoningContent';\n",
      "type": "registry:component"
    }
  ]
}