{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"reasoning","type":"registry:ui","title":"Thinking / Reasoning","description":"A collapsible chain-of-thought panel — show the model's reasoning trace with elapsed time, folded away by default.","author":"Scrim UI (https://scrimui.dev)","categories":["reasoning"],"docs":"https://scrimui.dev/components/reasoning","dependencies":[],"registryDependencies":[],"files":[{"path":"src/showcase/reasoning/reasoning.tsx","content":"\"use client\";\n\nimport * as React from \"react\";\n\n/* ------------------------------------------------------------------ */\n/* Types                                                               */\n/* ------------------------------------------------------------------ */\n\nexport type ReasoningStep = {\n  title: string;\n  detail?: string;\n};\n\nexport type ReasoningProps = {\n  steps?: ReasoningStep[];\n  isThinking?: boolean;\n  elapsed?: string;\n  onStop?: () => void;\n  defaultOpen?: boolean;\n  open?: boolean;\n  onOpenChange?: (open: boolean) => void;\n  className?: string;\n};\n\n/* ------------------------------------------------------------------ */\n/* Icons                                                               */\n/* ------------------------------------------------------------------ */\n\nfunction BrainIcon(props: React.SVGProps<SVGSVGElement>) {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" width=\"15\" height=\"15\" {...props}>\n      <path d=\"M9.5 2A2.5 2.5 0 0 1 12 4.5v15a2.5 2.5 0 0 1-4.96.44A2.5 2.5 0 0 1 4 17.5v-11A2.5 2.5 0 0 1 6.5 4h3Z\" />\n      <path d=\"M14.5 2A2.5 2.5 0 0 0 12 4.5v15a2.5 2.5 0 0 0 4.96.44A2.5 2.5 0 0 0 20 17.5v-11A2.5 2.5 0 0 0 17.5 4h-3Z\" />\n      <path d=\"M12 5v1M12 18v1\" />\n    </svg>\n  );\n}\n\nfunction ChevronIcon(props: React.SVGProps<SVGSVGElement>) {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\" width=\"14\" height=\"14\" {...props}>\n      <path d=\"m6 9 6 6 6-6\" />\n    </svg>\n  );\n}\n\nfunction StopIcon(props: React.SVGProps<SVGSVGElement>) {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"currentColor\" width=\"10\" height=\"10\" {...props}>\n      <rect x=\"6\" y=\"6\" width=\"12\" height=\"12\" rx=\"2\" />\n    </svg>\n  );\n}\n\nfunction CheckIcon(props: React.SVGProps<SVGSVGElement>) {\n  return (\n    <svg viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2.5\" strokeLinecap=\"round\" strokeLinejoin=\"round\" width=\"11\" height=\"11\" {...props}>\n      <path d=\"M20 6 9 17l-5-5\" />\n    </svg>\n  );\n}\n\n/* ------------------------------------------------------------------ */\n/* Reasoning                                                           */\n/* ------------------------------------------------------------------ */\n\nexport function Reasoning({\n  steps = [],\n  isThinking = false,\n  elapsed,\n  onStop,\n  defaultOpen = true,\n  open: controlledOpen,\n  onOpenChange,\n  className = \"\",\n}: ReasoningProps) {\n  const [internalOpen, setInternalOpen] = React.useState(defaultOpen);\n  const open = controlledOpen ?? internalOpen;\n  const setOpen = (v: boolean) => {\n    setInternalOpen(v);\n    onOpenChange?.(v);\n  };\n\n  return (\n    <div\n      className={`overflow-hidden rounded-xl border border-zinc-200 bg-white transition-colors dark:border-zinc-800 dark:bg-zinc-900 ${className}`}\n    >\n      {/* Header */}\n      <button\n        type=\"button\"\n        onClick={() => setOpen(!open)}\n        aria-expanded={open}\n        className=\"flex w-full items-center gap-2.5 px-3.5 py-2.5 text-left\"\n      >\n        <span className=\"flex h-7 w-7 shrink-0 items-center justify-center rounded-lg bg-violet-100 text-violet-600 dark:bg-violet-900/40 dark:text-violet-400\">\n          <BrainIcon />\n        </span>\n        <span className=\"min-w-0 flex-1 truncate text-sm font-medium text-zinc-900 dark:text-zinc-100\">\n          {isThinking ? \"Reasoning\" : \"Reasoning trace\"}\n        </span>\n        {elapsed && <span className=\"shrink-0 text-xs tabular-nums text-zinc-500 dark:text-zinc-400\">{elapsed}</span>}\n        {isThinking && (\n          <span className=\"inline-flex shrink-0 items-center gap-1 rounded-full bg-violet-100 px-2 py-0.5 text-[11px] text-violet-700 dark:bg-violet-900/40 dark:text-violet-400\">\n            <span className=\"h-2.5 w-2.5 animate-spin rounded-full border-[1.5px] border-current border-t-transparent\" />\n            Thinking\n          </span>\n        )}\n        <span className={`shrink-0 text-zinc-400 transition-transform ${open ? \"rotate-180\" : \"\"}`}>\n          <ChevronIcon />\n        </span>\n      </button>\n\n      {/* Steps */}\n      {open && (\n        <div className=\"border-t border-zinc-100 px-4 py-3 dark:border-zinc-800\">\n          {steps.length === 0 ? (\n            <div className=\"flex items-center gap-2 text-sm text-zinc-500 dark:text-zinc-400\">\n              <span className=\"h-2 w-2 animate-pulse rounded-full bg-zinc-300 dark:bg-zinc-600\" />\n              <span>Formulating an approach…</span>\n            </div>\n          ) : (\n            <ol className=\"relative space-y-3 before:absolute before:left-[7px] before:top-2 before:bottom-2 before:w-px before:bg-zinc-200 dark:before:bg-zinc-800\">\n              {steps.map((step, i) => (\n                <li key={i} className=\"relative pl-6\">\n                  <span className=\"absolute left-0 top-1 flex h-[15px] w-[15px] items-center justify-center rounded-full border border-zinc-200 bg-white text-[9px] text-zinc-500 dark:text-zinc-400 dark:border-zinc-700 dark:bg-zinc-800\">\n                    {i + 1}\n                  </span>\n                  <p className=\"text-sm font-medium text-zinc-800 dark:text-zinc-200\">{step.title}</p>\n                  {step.detail && (\n                    <p className=\"mt-0.5 text-[13px] leading-5 text-zinc-500 dark:text-zinc-400\">\n                      {step.detail}\n                    </p>\n                  )}\n                </li>\n              ))}\n            </ol>\n          )}\n\n          {isThinking && onStop && (\n            <button\n              type=\"button\"\n              onClick={onStop}\n              className=\"mt-3 inline-flex h-7 items-center gap-1.5 rounded-lg border border-zinc-200 px-2.5 text-xs text-zinc-600 transition-colors hover:bg-zinc-100 dark:border-zinc-800 dark:text-zinc-300 dark:hover:bg-zinc-800\"\n            >\n              <StopIcon />\n              Stop reasoning\n            </button>\n          )}\n          {!isThinking && steps.length > 0 && (\n            <p className=\"mt-3 flex items-center gap-1.5 text-xs text-emerald-700 dark:text-emerald-400\">\n              <CheckIcon />\n              Reasoning complete — {steps.length} step{steps.length > 1 ? \"s\" : \"\"}\n            </p>\n          )}\n        </div>\n      )}\n    </div>\n  );\n}\n","type":"registry:ui","target":"components/ui/reasoning.tsx"}]}