{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "reasoning",
  "type": "registry:ui",
  "title": "Reasoning",
  "description": "Collapsible thinking trace: a shimmering “Thinking… 12s” label while active that settles into “Thought for 14s”, revealing muted reasoning text.",
  "dependencies": [
    "lucide-react"
  ],
  "registryDependencies": [
    "https://kuro.abstergo.fyi/r/kura-theme.json",
    "https://kuro.abstergo.fyi/r/shimmer-text.json",
    "https://kuro.abstergo.fyi/r/use-controllable-state.json",
    "utils"
  ],
  "files": [
    {
      "path": "registry/kura/reasoning/index.ts",
      "type": "registry:ui",
      "target": "components/kura/reasoning/index.ts",
      "content": "export { formatSeconds, Reasoning, type ReasoningProps } from \"./reasoning\";\n"
    },
    {
      "path": "registry/kura/reasoning/reasoning.tsx",
      "type": "registry:ui",
      "target": "components/kura/reasoning/reasoning.tsx",
      "content": "\"use client\";\n\nimport { ChevronRight } from \"lucide-react\";\nimport * as React from \"react\";\nimport { useControllableState } from \"@/hooks/kura/use-controllable-state\";\nimport { cn } from \"@/lib/utils\";\nimport { ShimmerText } from \"@/components/kura/shimmer-text\";\n\nexport interface ReasoningProps extends React.ComponentProps<\"div\"> {\n  /** The model is still thinking: the label shimmers and an elapsed timer ticks. Default false. */\n  active?: boolean;\n  /** Seconds spent thinking, if you know it (e.g. from the API). Otherwise measured while `active`. */\n  duration?: number;\n  /** Expanded (controlled). */\n  open?: boolean;\n  /** Initial expanded state when uncontrolled. Default: `active` (open while thinking). */\n  defaultOpen?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  /** Collapse automatically when `active` turns false — unless the reader toggled it meanwhile. Default true. */\n  autoCollapse?: boolean;\n  /** Custom label, e.g. for i18n. Default “Thinking… 12s” / “Thought for 14s”. */\n  formatLabel?: (state: { active: boolean; seconds: number }) => React.ReactNode;\n}\n\nexport function formatSeconds(seconds: number): string {\n  const s = Math.max(0, Math.round(seconds));\n  if (s < 60) return `${s}s`;\n  const m = Math.floor(s / 60);\n  return s % 60 ? `${m}m ${s % 60}s` : `${m}m`;\n}\n\n/**\n * Collapsible “thinking” trace. While `active` it shows a shimmering “Thinking… 12s” label and\n * the reasoning streaming in; when done it collapses to “Thought for 14s”. Expanding reveals the\n * muted trace with an interruptible height transition.\n */\nexport function Reasoning({\n  active = false,\n  duration,\n  open: openProp,\n  defaultOpen,\n  onOpenChange,\n  autoCollapse = true,\n  formatLabel,\n  className,\n  children,\n  ...props\n}: ReasoningProps) {\n  const [open, setOpen] = useControllableState({\n    value: openProp,\n    defaultValue: defaultOpen ?? active,\n    onChange: onOpenChange,\n  });\n\n  // Elapsed time: measured from when `active` became true.\n  const startedAt = React.useRef<number | null>(null);\n  const [elapsed, setElapsed] = React.useState(0);\n  const elapsedRef = React.useRef(elapsed);\n  elapsedRef.current = elapsed;\n  React.useEffect(() => {\n    if (!active) return;\n    // Resume from the time already spent (e.g. a paused-then-resumed agent).\n    startedAt.current = Date.now() - elapsedRef.current * 1000;\n    const id = setInterval(() => {\n      if (startedAt.current != null) setElapsed((Date.now() - startedAt.current) / 1000);\n    }, 250);\n    return () => {\n      clearInterval(id);\n      if (startedAt.current != null) setElapsed((Date.now() - startedAt.current) / 1000);\n    };\n  }, [active]);\n  const seconds = duration ?? elapsed;\n\n  // Auto-collapse when thinking ends, unless the reader took control during it.\n  const touched = React.useRef(false);\n  const wasActive = React.useRef(active);\n  React.useEffect(() => {\n    if (wasActive.current && !active && autoCollapse && !touched.current) setOpen(false);\n    if (!wasActive.current && active) touched.current = false;\n    wasActive.current = active;\n  }, [active, autoCollapse, setOpen]);\n\n  const id = React.useId();\n  const contentId = `${id}-content`;\n  const label = formatLabel ? (\n    formatLabel({ active, seconds })\n  ) : active ? (\n    <>\n      Thinking… <span className=\"tabular-nums\">{formatSeconds(seconds)}</span>\n    </>\n  ) : seconds >= 1 ? (\n    <>\n      Thought for <span className=\"tabular-nums\">{formatSeconds(seconds)}</span>\n    </>\n  ) : (\n    \"Thought for a moment\"\n  );\n\n  return (\n    <div\n      data-slot=\"reasoning\"\n      data-status={active ? \"active\" : \"done\"}\n      data-state={open ? \"open\" : \"closed\"}\n      className={cn(\"flex flex-col font-kura-sans\", className)}\n      {...props}\n    >\n      <button\n        type=\"button\"\n        data-slot=\"reasoning-trigger\"\n        aria-expanded={open}\n        aria-controls={contentId}\n        onClick={() => {\n          touched.current = true;\n          setOpen(!open);\n        }}\n        className={cn(\n          \"group/trigger -mx-1.5 inline-flex h-7 w-fit cursor-pointer items-center gap-1 rounded-kura-sm px-1.5 text-kura-fg-muted text-sm outline-none\",\n          \"transition-[color,background-color,box-shadow] duration-(--kura-duration-fast) ease-kura-out hover:text-kura-fg focus-visible:shadow-kura-focus\",\n        )}\n      >\n        <ShimmerText active={active} data-slot=\"reasoning-label\">\n          {label}\n        </ShimmerText>\n        <ChevronRight\n          aria-hidden\n          className={cn(\n            \"size-3.5 text-kura-fg-subtle transition-[rotate,color] duration-(--kura-duration-base) ease-kura-out group-hover/trigger:text-kura-fg-muted motion-reduce:transition-none\",\n            open && \"rotate-90\",\n          )}\n        />\n      </button>\n      <span className=\"sr-only\" role=\"status\">\n        {active ? \"Thinking\" : `Thought for ${formatSeconds(seconds)}`}\n      </span>\n      <div\n        id={contentId}\n        data-slot=\"reasoning-content\"\n        inert={!open || undefined}\n        className={cn(\n          \"grid transition-[grid-template-rows,opacity,filter] duration-(--kura-duration-slow) ease-kura-out motion-reduce:transition-opacity\",\n          open\n            ? \"grid-rows-[1fr] opacity-100\"\n            : \"grid-rows-[0fr] opacity-0 blur-[2px] motion-reduce:blur-none\",\n        )}\n      >\n        <div className=\"min-h-0 overflow-hidden\">\n          <div className=\"mt-1.5 ml-[3px] whitespace-pre-wrap border-kura-border border-l-2 py-0.5 pl-3.5 text-kura-fg-muted text-sm leading-relaxed [&_[data-slot=streaming-text]]:text-kura-fg-muted [&_[data-slot=streaming-text]]:text-sm\">\n            {children}\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n"
    }
  ],
  "categories": [
    "agent",
    "reasoning",
    "thinking",
    "chain-of-thought",
    "collapsible"
  ],
  "docs": "Docs: https://kuro.abstergo.fyi/components/reasoning · Markdown for agents: https://kuro.abstergo.fyi/components/reasoning.md",
  "meta": {
    "kura": {
      "status": "beta",
      "whenToUse": [
        "Show a reasoning model's chain of thought / thinking blocks (Claude extended thinking, o-series reasoning summaries) above the answer.",
        "Indicate that the model is thinking, with elapsed time, before the first answer token arrives.",
        "Keep a finished trace available but out of the way (collapsed to one line)."
      ],
      "whenNotToUse": [
        "A bare “Thinking…” label with no trace to reveal — use shimmer-text (or thinking-indicator).",
        "A sequence of discrete agent steps with states — use agent-timeline.",
        "A single tool invocation with arguments and results — use tool-call."
      ]
    }
  }
}
