{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "reasoning-trace",
  "title": "Reasoning Trace",
  "description": "An expandable chain-of-thought trace with a live thinking timer.",
  "dependencies": [
    "lucide-react"
  ],
  "files": [
    {
      "path": "components/spectrumui/blocks/ai-assistants/reasoning-trace.tsx",
      "content": "\"use client\";\n\nimport { useEffect, useRef, useState } from \"react\";\nimport { ChevronDown, Sparkles } from \"lucide-react\";\nimport { cn } from \"@/lib/utils\";\nimport type { ReasoningStep } from \"./types\";\n\nconst KEYFRAMES = `\n@keyframes su-shimmer-text { 0% { background-position: 200% 0 } 100% { background-position: -200% 0 } }\n@keyframes su-step-in { from { opacity: 0; transform: translateY(4px) } to { opacity: 1; transform: none } }\n`;\n\nexport type ReasoningTraceVariant = \"Steps\" | \"Reasoning\";\n\nexport interface ReasoningTraceProps {\n  steps: ReasoningStep[];\n  status: \"thinking\" | \"complete\";\n  durationMs?: number;\n  variant?: ReasoningTraceVariant;\n  defaultOpen?: boolean;\n  className?: string;\n}\n\nfunction useThinkingClock(active: boolean) {\n  const [ds, setDs] = useState(0);\n  const wasActive = useRef(active);\n  useEffect(() => {\n    if (active && !wasActive.current) setDs(0);\n    wasActive.current = active;\n    if (!active) return;\n    const t = setInterval(() => setDs((d) => d + 1), 100);\n    return () => clearInterval(t);\n  }, [active]);\n  return ds / 10;\n}\n\nexport function ReasoningTrace({\n  steps,\n  status,\n  durationMs,\n  variant = \"Steps\",\n  defaultOpen = false,\n  className,\n}: ReasoningTraceProps) {\n  const [open, setOpen] = useState(defaultOpen);\n  const thinking = status === \"thinking\";\n  const elapsed = useThinkingClock(thinking);\n\n  const seconds = thinking\n    ? elapsed.toFixed(1)\n    : ((durationMs ?? elapsed * 1000) / 1000).toFixed(1);\n\n  return (\n    <div className={cn(\"w-full max-w-[440px] text-[13px]\", className)}>\n      <style dangerouslySetInnerHTML={{ __html: KEYFRAMES }} />\n\n      <button\n        type=\"button\"\n        onClick={() => setOpen((o) => !o)}\n        aria-expanded={open}\n        className=\"group flex items-center gap-2 rounded-lg py-1 text-neutral-600 transition-[color,transform] duration-150 hover:text-neutral-900 active:scale-[0.98] focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-neutral-400 dark:text-neutral-400 dark:hover:text-neutral-100\"\n      >\n        <Sparkles\n          aria-hidden\n          className={cn(\"size-3.5\", thinking && \"motion-safe:animate-pulse\")}\n        />\n        {thinking ? (\n          <span\n            className=\"bg-clip-text font-medium text-transparent motion-reduce:!animate-none motion-reduce:!text-current\"\n            style={{\n              backgroundImage:\n                \"linear-gradient(90deg, #a3a3a3 35%, currentColor 50%, #a3a3a3 65%)\",\n              backgroundSize: \"200% 100%\",\n              animation: \"su-shimmer-text 1.4s linear infinite\",\n            }}\n          >\n            Thinking\n          </span>\n        ) : (\n          <span className=\"font-medium\">Thought for {seconds}s</span>\n        )}\n        {thinking && (\n          <span className=\"font-mono text-[12px] tabular-nums text-neutral-400 dark:text-neutral-500\">\n            {seconds}s\n          </span>\n        )}\n        <ChevronDown\n          aria-hidden\n          className={cn(\n            \"size-3.5 text-neutral-400 transition-transform duration-200 ease-[cubic-bezier(0.23,1,0.32,1)]\",\n            open && \"rotate-180\",\n          )}\n        />\n      </button>\n\n      <div\n        className=\"grid transition-[grid-template-rows] duration-[240ms] ease-[cubic-bezier(0.32,0.72,0,1)]\"\n        style={{ gridTemplateRows: open ? \"1fr\" : \"0fr\" }}\n      >\n        <div className=\"overflow-hidden\">\n          <div className=\"ml-[7px] mt-1 border-l border-neutral-200 pb-1 pl-4 pt-1 dark:border-neutral-800\">\n            {variant === \"Steps\" ? (\n              <ol className=\"space-y-2.5\">\n                {steps.map((step, i) => (\n                  <li\n                    key={step.id}\n                    className=\"flex gap-2.5 leading-[1.6] text-neutral-500 motion-safe:animate-[su-step-in_240ms_cubic-bezier(0.23,1,0.32,1)_both] dark:text-neutral-400\"\n                  >\n                    <span className=\"mt-px font-mono text-[11px] tabular-nums text-neutral-400 dark:text-neutral-600\">\n                      {String(i + 1).padStart(2, \"0\")}\n                    </span>\n                    {step.content}\n                  </li>\n                ))}\n              </ol>\n            ) : (\n              <p className=\"leading-[1.7] text-neutral-500 dark:text-neutral-400\">\n                {steps.map((step) => step.content).join(\" \")}\n              </p>\n            )}\n          </div>\n        </div>\n      </div>\n    </div>\n  );\n}\n\nexport default ReasoningTrace;\n",
      "type": "registry:block",
      "target": "components/spectrumui/blocks/ai-assistants/reasoning-trace.tsx"
    },
    {
      "path": "components/spectrumui/blocks/ai-assistants/types.ts",
      "content": "export type MessageRole = 'user' | 'assistant' | 'system';\n\nexport type MessageState = 'streaming' | 'complete' | 'error';\n\nexport interface Attachment {\n  id: string;\n  name: string;\n  size: number;\n  type: string;\n  url?: string;\n  previewUrl?: string;\n}\n\nexport interface Citation {\n  id: string;\n  index: number;\n  url: string;\n  title: string;\n  snippet?: string;\n  favicon?: string;\n}\n\nexport type ToolCallStatus = 'pending' | 'running' | 'success' | 'error' | 'cancelled';\n\nexport interface ToolCall {\n  id: string;\n  name: string;\n  args?: Record<string, unknown>;\n  result?: string;\n  status: ToolCallStatus;\n  startedAt?: number;\n  completedAt?: number;\n  children?: ToolCall[];\n  parallel?: boolean;\n}\n\nexport interface ReasoningStep {\n  id: string;\n  content: string;\n}\n\nexport interface Reasoning {\n  steps: ReasoningStep[];\n  status: 'thinking' | 'complete';\n  durationMs?: number;\n}\n\nexport interface Message {\n  id: string;\n  role: MessageRole;\n  content: string;\n  createdAt?: number;\n  state?: MessageState;\n  attachments?: Attachment[];\n  citations?: Citation[];\n  toolCalls?: ToolCall[];\n  reasoning?: Reasoning;\n}\n\nexport interface Conversation {\n  id: string;\n  label: string;\n}\n\nexport interface ModelOption {\n  id: string;\n  name: string;\n  description?: string;\n  badge?: string;\n  disabled?: boolean;\n}\n\nexport interface SuggestedPrompt {\n  id: string;\n  label: string;\n  prompt?: string;\n}\n\nexport type MessageFeedback = 'positive' | 'negative';\n\nexport interface UsageState {\n  promptTokens: number;\n  completionTokens: number;\n  contextWindow: number;\n  estimatedCostUsd?: number;\n}\n",
      "type": "registry:file",
      "target": "components/spectrumui/blocks/ai-assistants/types.ts"
    }
  ],
  "type": "registry:block"
}
