{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "thinking-state",
  "title": "Thinking State",
  "description": "A status row for work in progress, with a live elapsed timer and optional reasoning.",
  "dependencies": [
    "lucide-react@^1.31.0"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/default/thinking-state/thinking-state.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { ChevronRight, Loader, Sparkles, TriangleAlert } from \"lucide-react\"\nimport { cn } from \"@/lib/utils\"\n\nexport type ThinkingStatus = \"idle\" | \"thinking\" | \"done\" | \"error\"\n\nexport type ThinkingStateProps = Omit<\n  React.HTMLAttributes<HTMLDivElement>,\n  \"children\"\n> & {\n  status?: ThinkingStatus\n  label?: React.ReactNode\n  doneLabel?: React.ReactNode\n  errorLabel?: React.ReactNode\n  startedAt?: number\n  elapsedMs?: number\n  showElapsed?: boolean\n  reasoning?: React.ReactNode\n  reasoningLabel?: string\n  open?: boolean\n  defaultOpen?: boolean\n  onOpenChange?: (open: boolean) => void\n}\n\nfunction formatElapsed(ms: number) {\n  const seconds = ms / 1000\n\n  if (seconds < 60) {\n    return `${seconds < 10 ? seconds.toFixed(1) : Math.round(seconds)}s`\n  }\n\n  const minutes = Math.floor(seconds / 60)\n  return `${minutes}m ${Math.round(seconds - minutes * 60)}s`\n}\n\nexport function ThinkingState({\n  status = \"thinking\",\n  label = \"Thinking\",\n  doneLabel = \"Thought\",\n  errorLabel = \"Could not finish\",\n  startedAt,\n  elapsedMs,\n  showElapsed = true,\n  reasoning,\n  reasoningLabel = \"Show reasoning\",\n  open,\n  defaultOpen = false,\n  onOpenChange,\n  className,\n  ...rootProps\n}: ThinkingStateProps) {\n  const reactId = React.useId()\n  const panelId = `${reactId}-reasoning`\n  const isThinking = status === \"thinking\"\n\n  const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen)\n  const isOpen = open ?? uncontrolledOpen\n\n  const setOpen = (next: boolean) => {\n    if (open === undefined) setUncontrolledOpen(next)\n    onOpenChange?.(next)\n  }\n\n  const [tick, setTick] = React.useState(() => Date.now())\n\n  React.useEffect(() => {\n    if (!isThinking || startedAt == null) return\n\n    const timer = window.setInterval(() => setTick(Date.now()), 100)\n\n    return () => window.clearInterval(timer)\n  }, [isThinking, startedAt])\n\n  const elapsed =\n    elapsedMs ?? (startedAt == null ? undefined : Math.max(0, tick - startedAt))\n\n  const statusLabel =\n    status === \"error\" ? errorLabel : isThinking ? label : doneLabel\n\n  const Icon =\n    status === \"error\" ? TriangleAlert : isThinking ? Loader : Sparkles\n\n  return (\n    <div\n      data-slot=\"thinking-state\"\n      data-status={status}\n      aria-busy={isThinking || undefined}\n      className={cn(\n        \"border-border bg-muted/40 text-foreground rounded-[var(--radius)] border\",\n        className\n      )}\n      {...rootProps}\n    >\n      <div\n        data-slot=\"thinking-state-header\"\n        className=\"flex items-center gap-2.5 px-3.5 py-2.5\"\n      >\n        <Icon\n          aria-hidden=\"true\"\n          size={16}\n          className={cn(\n            \"shrink-0\",\n            status === \"error\" ? \"text-destructive\" : \"text-muted-foreground\",\n            isThinking && \"animate-spin motion-reduce:animate-none\"\n          )}\n        />\n\n        <span\n          data-slot=\"thinking-state-label\"\n          className={cn(\n            \"text-sm font-semibold\",\n            isThinking && \"animate-pulse motion-reduce:animate-none\"\n          )}\n        >\n          {statusLabel}\n        </span>\n\n        {showElapsed && elapsed != null ? (\n          <span\n            data-slot=\"thinking-state-elapsed\"\n            className=\"text-muted-foreground font-[family-name:var(--font-mono),monospace] text-xs tabular-nums\"\n          >\n            {formatElapsed(elapsed)}\n          </span>\n        ) : null}\n\n        {reasoning ? (\n          <button\n            type=\"button\"\n            data-slot=\"thinking-state-trigger\"\n            aria-expanded={isOpen}\n            aria-controls={panelId}\n            className=\"text-muted-foreground hover:text-foreground focus-visible:ring-ring ml-auto inline-flex min-h-11 items-center gap-1 rounded-full px-2 text-xs font-semibold focus-visible:ring-2 focus-visible:outline-none\"\n            onClick={() => setOpen(!isOpen)}\n          >\n            {reasoningLabel}\n            <ChevronRight\n              aria-hidden=\"true\"\n              size={14}\n              className={cn(\n                \"transition-transform duration-200 motion-reduce:transition-none\",\n                isOpen && \"rotate-90\"\n              )}\n            />\n          </button>\n        ) : null}\n      </div>\n\n      {reasoning ? (\n        <div\n          id={panelId}\n          data-slot=\"thinking-state-reasoning\"\n          hidden={!isOpen}\n          className=\"text-muted-foreground border-border border-t px-3.5 py-3 text-sm leading-relaxed\"\n        >\n          {reasoning}\n        </div>\n      ) : null}\n    </div>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "ai",
    "status",
    "loading",
    "agent"
  ],
  "type": "registry:ui"
}