{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "components-ai-thought-chain",
  "title": "Thought Chain",
  "description": "An animated AI reasoning step-by-step thought chain component with collapsible steps and status indicators.",
  "dependencies": [
    "motion",
    "lucide-react"
  ],
  "registryDependencies": [
    "collapsible",
    "badge",
    "@odysseyui/components-texts-text-shimmer"
  ],
  "files": [
    {
      "path": "registry/components/ai/thought-chain/index.tsx",
      "content": "'use client';\n\nimport React, { createContext, useContext, useState } from 'react';\nimport { motion, AnimatePresence } from 'motion/react';\nimport { Check, Loader2, Circle, ChevronDown } from 'lucide-react';\nimport {\n  Collapsible,\n  CollapsibleContent,\n  CollapsibleTrigger,\n} from '@/components/ui/collapsible';\nimport { Badge } from '@/components/ui/badge';\nimport { cn } from '@/lib/utils';\nimport { ShimmerText } from '@/components/odyssey/components/texts/text-shimmer';\n\ntype Status = 'done' | 'active' | 'pending';\n\ntype StepContextType = {\n  open: boolean;\n  setOpen: React.Dispatch<React.SetStateAction<boolean>>;\n  status: Status;\n};\n\nconst StepContext = createContext<StepContextType | null>(null);\n\nconst statusStyles: Record<\n  Status,\n  { label: string; line: string; badge: string }\n> = {\n  done: {\n    label: 'text-gray-500',\n    line: 'bg-green-600/30',\n    badge: '',\n  },\n  active: {\n    label: 'text-gray-900 dark:text-gray-100',\n    line: 'bg-blue-500/25',\n    badge: 'bg-blue-50 text-blue-500 dark:bg-blue-900/30',\n  },\n  pending: {\n    label: 'text-gray-400',\n    line: 'bg-gray-200 dark:bg-gray-700',\n    badge: '',\n  },\n};\n\nfunction StatusIcon({ status }: { status: Status }) {\n  if (status === 'done') {\n    return (\n      <span className=\"flex size-5 items-center justify-center rounded-full bg-green-600\">\n        <Check className=\"size-3 text-white\" strokeWidth={3} />\n      </span>\n    );\n  }\n\n  if (status === 'active') {\n    return (\n      <motion.span\n        animate={{ rotate: 360 }}\n        transition={{ repeat: Infinity, duration: 0.75, ease: 'linear' }}\n        className=\"flex size-5 items-center justify-center\"\n      >\n        <Loader2 className=\"size-5 text-blue-500\" />\n      </motion.span>\n    );\n  }\n\n  return <Circle className=\"size-5 text-gray-300 dark:text-gray-600\" />;\n}\n\nexport function ThoughtChain({ children }: { children: React.ReactNode }) {\n  const steps = React.Children.toArray(children);\n  const total = steps.length;\n\n  return (\n    <div>\n      {steps.map((step, index) =>\n        React.isValidElement(step)\n          ? React.cloneElement(\n              step as React.ReactElement<{ _isLast?: boolean }>,\n              {\n                _isLast: index === total - 1,\n              },\n            )\n          : step,\n      )}\n    </div>\n  );\n}\n\nexport function ThoughtChainStep({\n  children,\n  status = 'pending',\n  defaultOpen = true,\n  _isLast = false,\n}: {\n  children: React.ReactNode;\n  status?: Status;\n  defaultOpen?: boolean;\n  _isLast?: boolean;\n}) {\n  const [open, setOpen] = useState(defaultOpen);\n  const styles = statusStyles[status];\n\n  return (\n    <StepContext.Provider value={{ open, setOpen, status }}>\n      <Collapsible open={open} onOpenChange={setOpen}>\n        <div className=\"flex gap-3.5\">\n          <div className=\"flex shrink-0 flex-col items-center\">\n            <span className=\"mt-0.5\">\n              <StatusIcon status={status} />\n            </span>\n\n            <span\n              className={cn(\n                'mt-1.5 min-h-5 w-0.5 flex-1 rounded-sm',\n                styles.line,\n              )}\n            />\n          </div>\n\n          <div className=\"flex-1 pb-2\">{children}</div>\n        </div>\n      </Collapsible>\n    </StepContext.Provider>\n  );\n}\n\nexport function ThoughtChainTrigger({\n  children,\n}: {\n  children: React.ReactNode;\n}) {\n  const { open, status } = useContext(StepContext)!;\n  const styles = statusStyles[status];\n\n  return (\n    <CollapsibleTrigger className=\"flex cursor-pointer select-none items-center gap-1.5 outline-none\">\n      <span\n        className={cn(\n          'text-[13.5px] font-semibold tracking-tight',\n          styles.label,\n        )}\n      >\n        {status === 'active' ? (\n          typeof children === 'string' ? (\n            <ShimmerText text={children} />\n          ) : (\n            children\n          )\n        ) : (\n          children\n        )}\n      </span>\n\n      <motion.span\n        animate={{ rotate: open ? 180 : 0 }}\n        transition={{ duration: 0.2, ease: 'easeInOut' }}\n        className=\"flex items-center text-gray-400\"\n      >\n        <ChevronDown className=\"size-3.5\" />\n      </motion.span>\n\n      {status === 'active' && (\n        <Badge\n          variant=\"outline\"\n          className={cn(styles.badge, 'border-blue-200 dark:border-blue-500')}\n        >\n          In progress\n        </Badge>\n      )}\n    </CollapsibleTrigger>\n  );\n}\n\nexport function ThoughtChainContent({\n  children,\n}: {\n  children: React.ReactNode;\n}) {\n  const { open } = useContext(StepContext)!;\n\n  return (\n    <CollapsibleContent forceMount>\n      <AnimatePresence initial={false}>\n        {open && (\n          <motion.div\n            key=\"content\"\n            initial={{ opacity: 0, height: 0 }}\n            animate={{ opacity: 1, height: 'auto' }}\n            exit={{ opacity: 0, height: 0 }}\n            transition={{ duration: 0.22, ease: 'easeInOut' }}\n            className=\"overflow-hidden\"\n          >\n            <div className=\"mt-1 pl-0.5\">{children}</div>\n          </motion.div>\n        )}\n      </AnimatePresence>\n    </CollapsibleContent>\n  );\n}\n\nexport function ThoughtChainItem({ children }: { children: React.ReactNode }) {\n  return (\n    <motion.div\n      initial={{ opacity: 0, y: -4 }}\n      animate={{ opacity: 1, y: 0 }}\n      transition={{ duration: 0.3, ease: 'easeOut' }}\n      className=\"flex items-start gap-2 py-1.25\"\n    >\n      <span className=\"mt-1.75 size-1 shrink-0 rounded-full bg-gray-300 dark:bg-gray-600\" />\n      <span className=\"text-[13px] leading-[1.55] text-gray-500 dark:text-gray-400\">\n        {children}\n      </span>\n    </motion.div>\n  );\n}\n",
      "type": "registry:ui",
      "target": "components/odysseyui/thought-chain.tsx"
    }
  ],
  "type": "registry:ui"
}