{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "chain-of-thought",
  "title": "Chain of Thought",
  "description": "The steps an agent took, on a rail with live status.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "utils",
    "https://ui.cuelark.com/r/theme.json",
    "https://ui.cuelark.com/r/collapsible.json",
    "https://ui.cuelark.com/r/motion.json",
    "https://ui.cuelark.com/r/text-shimmer.json"
  ],
  "files": [
    {
      "path": "src/registry/cuelark/ui/chain-of-thought.tsx",
      "content": "\"use client\";\n\nimport * as React from \"react\";\nimport { ChevronDownIcon } from \"lucide-react\";\n\nimport { enterChildren, gsap, motionScale } from \"@/lib/motion\";\nimport { Collapsible, CollapsibleContent, CollapsibleTrigger } from \"@/registry/cuelark/ui/collapsible\";\nimport { TextShimmer } from \"@/registry/cuelark/ui/text-shimmer\";\nimport { cn } from \"@/lib/utils\";\n\ntype Status = \"complete\" | \"active\" | \"pending\";\n\n/**\n * The steps an agent takes, on a rail that fills as it goes. Each finished step pops its marker and draws its\n * check, the active one breathes a signal ring, and the rail segment below a finished step fills top to bottom.\n * The header keeps a live count and a thin progress line.\n */\nfunction ChainOfThought({ className, title = \"Chain of thought\", defaultOpen = true, children, ...props }: React.ComponentProps<typeof Collapsible> & { title?: React.ReactNode }) {\n  const steps = React.Children.toArray(children).filter(React.isValidElement) as React.ReactElement<StepProps>[];\n  const done = steps.filter((s) => (s.props.status ?? \"complete\") === \"complete\").length;\n  const running = steps.some((s) => s.props.status === \"active\");\n  const bar = React.useRef<HTMLSpanElement>(null);\n\n  React.useEffect(() => {\n    const el = bar.current;\n    if (!el) return;\n    gsap.to(el, { scaleX: steps.length ? done / steps.length : 0, duration: motionScale(el) ? 0.6 : 0, ease: \"cue\" });\n  }, [done, steps.length]);\n\n  return (\n    <Collapsible data-slot=\"chain-of-thought\" defaultOpen={defaultOpen} className={cn(\"grid gap-3\", className)} {...props}>\n      <CollapsibleTrigger className=\"group/cot grid w-full gap-1.5 rounded-md text-left outline-none focus-visible:ring-3 focus-visible:ring-ring/50\">\n        <span className=\"flex items-center gap-2 text-sm font-medium\">\n          {title}\n          {running && <span aria-hidden=\"true\" className=\"size-1.5 animate-pulse rounded-full bg-signal\" />}\n          <span className=\"font-mono text-xs font-normal text-muted-foreground tabular-nums\">\n            {done}/{steps.length}\n          </span>\n          <ChevronDownIcon className=\"size-4 text-muted-foreground transition-transform duration-200 ease-cue group-data-[state=open]/cot:rotate-180\" />\n        </span>\n        <span aria-hidden=\"true\" className=\"relative block h-px w-full overflow-hidden bg-border\">\n          <span ref={bar} className=\"absolute inset-0 origin-left scale-x-0 bg-signal\" />\n        </span>\n      </CollapsibleTrigger>\n      {/* Room for the active marker's ring, which the collapsible's overflow clip would otherwise cut. */}\n      <CollapsibleContent className=\"-m-2 p-2\">\n        <ol className=\"grid\">{children}</ol>\n      </CollapsibleContent>\n    </Collapsible>\n  );\n}\n\ntype StepProps = Omit<React.ComponentProps<\"li\">, \"title\"> & {\n  status?: Status;\n  label: React.ReactNode;\n  icon?: React.ReactNode;\n};\n\nfunction ChainOfThoughtStep({ status = \"complete\", label, icon, className, children, ...props }: StepProps) {\n  const ref = React.useRef<HTMLLIElement>(null);\n  const prev = React.useRef<Status | null>(null);\n  const breathe = React.useRef<gsap.core.Tween | null>(null);\n\n  React.useLayoutEffect(() => {\n    const li = ref.current;\n    if (!li) return;\n    const s = motionScale(li);\n    const marker = li.querySelector<HTMLElement>('[data-part=\"marker\"]');\n    const check = li.querySelector<SVGPathElement>('[data-part=\"check\"]');\n    const fill = li.querySelector<HTMLElement>('[data-part=\"fill\"]');\n    const first = prev.current === null;\n    const was = prev.current;\n    prev.current = status;\n\n    // Rail below this step: filled once the step is complete.\n    if (fill) gsap.to(fill, { scaleY: status === \"complete\" ? 1 : 0, duration: s && !first ? 0.5 : 0, ease: \"cue\", delay: s && !first ? 0.15 : 0 });\n\n    // The active marker breathes a signal ring drawn as its own box-shadow, so it can never drift off-center.\n    breathe.current?.kill();\n    if (marker) {\n      if (status === \"active\" && s)\n        breathe.current = gsap.fromTo(\n          marker,\n          { boxShadow: \"0 0 0 0px color-mix(in oklch, var(--signal) 45%, transparent)\" },\n          { boxShadow: \"0 0 0 7px color-mix(in oklch, var(--signal) 0%, transparent)\", duration: 1.4, ease: \"power1.out\", repeat: -1 },\n        );\n      else gsap.set(marker, { clearProps: \"boxShadow\" });\n    }\n\n    if (!s) return;\n    if (first) {\n      gsap.fromTo(li, { opacity: 0 }, { opacity: 1, duration: 0.35, ease: \"cue\", clearProps: \"opacity\" });\n    }\n    if (status === \"complete\" && (was === \"active\" || was === \"pending\")) {\n      if (marker) gsap.fromTo(marker, { scale: 0.6 }, { scale: 1, duration: 0.5, ease: \"elastic.out(1.2, 0.5)\", clearProps: \"transform\" });\n      if (check) gsap.fromTo(check, { strokeDashoffset: 14 }, { strokeDashoffset: 0, duration: 0.35, ease: \"cue\", delay: 0.08 });\n    }\n    if (status === \"complete\" && first && check) gsap.set(check, { strokeDashoffset: 0 });\n  }, [status]);\n\n  // Detail (search results, files) can arrive after the step: stagger it in the first time it appears.\n  React.useLayoutEffect(() => {\n    const detail = ref.current?.querySelector<HTMLElement>('[data-part=\"detail\"]');\n    const inner = detail?.firstElementChild;\n    if (!detail || !inner || detail.dataset.shown !== undefined || !motionScale(detail)) return;\n    detail.dataset.shown = \"\";\n    // Grow the space smoothly so the steps below glide down instead of jumping, then stagger the content in.\n    gsap.fromTo(detail, { height: 0, opacity: 0 }, { height: \"auto\", opacity: 1, duration: 0.35, ease: \"cue\", clearProps: \"height,opacity\" });\n    enterChildren(inner.children.length ? inner.children : [inner], { opacity: 0, y: 4, scale: 0.96 }, { stagger: 0.05, duration: 0.3, delay: 0.1 });\n  });\n\n  React.useEffect(() => () => void breathe.current?.kill(), []);\n\n  return (\n    <li ref={ref} data-slot=\"chain-of-thought-step\" data-status={status} className={cn(\"group/step relative grid grid-cols-[1.25rem_minmax(0,1fr)] gap-x-3 pb-5 last:pb-0\", className)} {...props}>\n      {/* Rail: a quiet track, with a signal fill that grows when the step completes. */}\n      <span aria-hidden=\"true\" className=\"absolute top-3 -bottom-3 left-[0.5625rem] w-px bg-border group-last/step:hidden\">\n        <span data-part=\"fill\" className=\"absolute inset-0 origin-top scale-y-0 bg-signal\" />\n      </span>\n      <span className=\"relative z-10 mt-0.5 grid size-5 place-items-center\">\n        <span\n          data-part=\"marker\"\n          className={cn(\n            \"relative grid size-5 place-items-center rounded-full border text-[0.625rem] transition-colors duration-200 [&_svg]:size-3\",\n            status === \"complete\" && \"border-foreground bg-foreground text-background\",\n            status === \"active\" && \"border-signal bg-background text-foreground\",\n            status === \"pending\" && \"border-dashed border-input bg-background text-muted-foreground\",\n          )}\n        >\n          {status === \"complete\" ? (\n            <svg viewBox=\"0 0 16 16\" fill=\"none\" stroke=\"currentColor\" strokeWidth={2.4} strokeLinecap=\"round\" strokeLinejoin=\"round\" aria-hidden=\"true\">\n              <path data-part=\"check\" d=\"M3.5 8.5l3 3 6-7\" strokeDasharray={14} strokeDashoffset={14} />\n            </svg>\n          ) : status === \"active\" ? (\n            <span className=\"size-1.5 rounded-full bg-signal\" />\n          ) : (\n            icon\n          )}\n        </span>\n      </span>\n      <div className=\"grid min-w-0 gap-2\">\n        <span className={cn(\"text-sm transition-colors duration-300\", status === \"pending\" && \"text-muted-foreground\")}>\n          {status === \"active\" ? <TextShimmer>{label}</TextShimmer> : label}\n          <span className=\"sr-only\">{status === \"complete\" ? \" (done)\" : status === \"active\" ? \" (in progress)\" : \" (pending)\"}</span>\n        </span>\n        {children && (\n          <div data-part=\"detail\" className=\"text-sm text-muted-foreground\">\n            {children}\n          </div>\n        )}\n      </div>\n    </li>\n  );\n}\n\nexport { ChainOfThought, ChainOfThoughtStep };\n",
      "type": "registry:ui"
    }
  ],
  "docs": "Docs and examples: https://ui.cuelark.com/components/chain-of-thought",
  "categories": [
    "agents"
  ],
  "type": "registry:ui"
}