{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "chain-of-thought",
  "title": "Chain of Thought",
  "description": "The steps an assistant took before answering, open while it works and folded away after.",
  "dependencies": [
    "lucide-react@^1.31.0"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/default/chain-of-thought/chain-of-thought.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Check, ChevronRight, X } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport type ThoughtStatus = \"pending\" | \"active\" | \"done\" | \"failed\"\n\nexport type Thought = {\n  id: string\n  label: string\n  detail?: React.ReactNode\n  status?: ThoughtStatus\n  /** Seconds this step took, shown beside it. */\n  duration?: number\n}\n\nexport type ChainOfThoughtProps = Omit<\n  React.HTMLAttributes<HTMLDivElement>,\n  \"children\"\n> & {\n  thoughts: readonly Thought[]\n  /** Still working. The trace opens while this is true. */\n  thinking?: boolean\n  open?: boolean\n  defaultOpen?: boolean\n  onOpenChange?: (open: boolean) => void\n}\n\nfunction formatSeconds(seconds: number) {\n  return seconds < 1\n    ? `${Math.round(seconds * 1000)}ms`\n    : `${seconds < 10 ? seconds.toFixed(1) : Math.round(seconds)}s`\n}\n\n/**\n * The steps an assistant went through before answering, opened while it is\n * working and folded away once it is done.\n *\n * The trace is never put in a live region. Reasoning arrives a token at a time\n * and announcing it would talk over the answer, so the summary carries the\n * state and the steps stay ordinary text to be read on purpose.\n */\nexport function ChainOfThought({\n  thoughts,\n  thinking = false,\n  open,\n  defaultOpen,\n  onOpenChange,\n  className,\n  ...rootProps\n}: ChainOfThoughtProps) {\n  const [held, setHeld] = React.useState(defaultOpen ?? thinking)\n  const touched = React.useRef(defaultOpen !== undefined)\n\n  // Folding away on its own is right up until someone opens it to read, and\n  // then closing it under them is the rudest thing this component could do.\n  React.useEffect(() => {\n    if (open !== undefined || touched.current) return\n    setHeld(thinking)\n  }, [open, thinking])\n\n  const shown = open ?? held\n  const total = thoughts.reduce((sum, step) => sum + (step.duration ?? 0), 0)\n\n  const summary = thinking\n    ? `Working on it`\n    : total > 0\n      ? `Thought for ${formatSeconds(total)}`\n      : `Thought about it`\n\n  const toggle = () => {\n    touched.current = true\n    const next = !shown\n    if (open === undefined) setHeld(next)\n    onOpenChange?.(next)\n  }\n\n  return (\n    <div\n      data-slot=\"chain-of-thought\"\n      data-thinking={thinking ? \"\" : undefined}\n      className={cn(\n        \"border-border bg-muted/30 w-full rounded-xl border\",\n        className\n      )}\n      {...rootProps}\n    >\n      <button\n        type=\"button\"\n        aria-expanded={shown}\n        onClick={toggle}\n        className=\"text-muted-foreground hover:text-foreground focus-visible:ring-ring flex min-h-11 w-full items-center gap-2 rounded-xl px-3 text-sm transition-colors focus-visible:ring-2 focus-visible:outline-none motion-reduce:transition-none\"\n      >\n        <ChevronRight\n          aria-hidden=\"true\"\n          size={15}\n          className={cn(\n            \"shrink-0 transition-transform duration-150 motion-reduce:transition-none\",\n            shown && \"rotate-90\"\n          )}\n        />\n        <span\n          className={cn(thinking && \"animate-pulse motion-reduce:animate-none\")}\n        >\n          {summary}\n        </span>\n        <span className=\"ms-auto text-xs opacity-70\">\n          {thoughts.length} {thoughts.length === 1 ? \"step\" : \"steps\"}\n        </span>\n      </button>\n\n      {shown ? (\n        <ol data-slot=\"chain-of-thought-steps\" className=\"space-y-1 px-3 pb-3\">\n          {thoughts.map((step) => {\n            const status = step.status ?? \"done\"\n\n            return (\n              <li\n                key={step.id}\n                data-status={status}\n                className=\"border-border/70 relative ps-5 text-sm before:absolute before:start-[5px] before:top-6 before:bottom-0 before:w-px before:bg-current before:opacity-20 last:before:hidden\"\n              >\n                <span\n                  aria-hidden=\"true\"\n                  className={cn(\n                    \"absolute start-0 top-1.5 flex size-[11px] items-center justify-center rounded-full\",\n                    status === \"active\" && \"bg-primary\",\n                    status === \"done\" && \"bg-muted-foreground/40\",\n                    status === \"failed\" && \"bg-destructive\",\n                    status === \"pending\" && \"border-muted-foreground/40 border\"\n                  )}\n                >\n                  {status === \"done\" ? (\n                    <Check size={7} className=\"text-background\" />\n                  ) : status === \"failed\" ? (\n                    <X size={7} className=\"text-background\" />\n                  ) : null}\n                </span>\n\n                <p className=\"flex items-baseline gap-2\">\n                  <span\n                    className={cn(\n                      status === \"pending\"\n                        ? \"text-muted-foreground\"\n                        : \"text-foreground\"\n                    )}\n                  >\n                    {step.label}\n                  </span>\n                  {step.duration !== undefined ? (\n                    <span className=\"text-muted-foreground shrink-0 font-mono text-[0.6875rem] tabular-nums\">\n                      {formatSeconds(step.duration)}\n                    </span>\n                  ) : null}\n                  <span className=\"sr-only\">\n                    {status === \"failed\"\n                      ? \", failed\"\n                      : status === \"pending\"\n                        ? \", not started\"\n                        : status === \"active\"\n                          ? \", running\"\n                          : \", done\"}\n                  </span>\n                </p>\n\n                {step.detail ? (\n                  <div className=\"text-muted-foreground mt-1 text-[0.8125rem] leading-relaxed\">\n                    {step.detail}\n                  </div>\n                ) : null}\n              </li>\n            )\n          })}\n        </ol>\n      ) : null}\n\n      <span aria-live=\"polite\" className=\"sr-only\">\n        {summary}\n      </span>\n    </div>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "ai",
    "agent",
    "chat",
    "reasoning",
    "feedback"
  ],
  "type": "registry:ui"
}