{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "retro-thinking",
  "title": "RetroThinking",
  "description": "Mac OS 9 AI thinking states: the watch cursor and a barber pole while a reply is pending, and a collapsible reasoning panel.",
  "registryDependencies": [
    "https://nostalgia-ui.com/r/nostalgia-theme.json",
    "https://nostalgia-ui.com/r/retro-ai.json",
    "https://nostalgia-ui.com/r/retro-collapsible.json",
    "https://nostalgia-ui.com/r/retro-progress.json",
    "https://nostalgia-ui.com/r/retro-streaming-text.json"
  ],
  "files": [
    {
      "path": "registry/new-york/ui/retro-thinking.tsx",
      "content": "\"use client\"\n\n/*\n * RetroThinking / RetroReasoning: what an assistant shows before and while\n * it thinks.\n *\n * Crafted moments:\n *  - The watch. OS 9's busy cursor was a wristwatch whose hands went round;\n *    here it is a 16px pixel watch whose minute hand sweeps (classic flair\n *    ticks it in eight hard steps, as the cursor did). Beside it a short\n *    barber pole, and after three seconds a quiet count of the seconds, so a\n *    long wait reads as work rather than a hang.\n *  - The reasoning disclosure opens on its own while the model thinks and\n *    closes itself when the answer starts, settling into \"Thought for 4\n *    seconds\". Open or close it yourself and it leaves your choice alone.\n *    Two-frame triangle and height morph from retro-collapsible.\n */\n\nimport * as React from \"react\"\n\nimport { cn } from \"@/lib/utils\"\nimport type { RetroReasoningPart } from \"@/registry/new-york/lib/retro-ai\"\nimport {\n  RetroCollapsible,\n  RetroCollapsibleContent,\n  RetroCollapsibleTrigger,\n} from \"@/registry/new-york/ui/retro-collapsible\"\nimport { RetroProgress } from \"@/registry/new-york/ui/retro-progress\"\nimport { RetroStreamingText } from \"@/registry/new-york/ui/retro-streaming-text\"\n\n/* ── the watch ─────────────────────────────────────────── */\n\nconst WATCH_CSS = `\n@keyframes os9-watch-turn { to { transform: rotate(360deg); } }\n.os9-watch-hand { transform-box: view-box; }\n.os9-watch[data-running] .os9-watch-minute { animation: os9-watch-turn 1.6s linear infinite; }\n.os9-watch[data-running] .os9-watch-hour { animation: os9-watch-turn 19.2s linear infinite; }\n[data-flair=\"classic\"] .os9-watch[data-running] .os9-watch-minute,\n[data-flair=\"classic\"] .os9-watch[data-running] .os9-watch-hour { animation-timing-function: steps(8, end); }\n@media (prefers-reduced-motion: reduce) {\n  .os9-watch[data-running] .os9-watch-hand { animation: none; }\n}\n`\n\nconst INK = \"var(--os9-black)\"\nconst STRAP = \"var(--os9-gray-500)\"\nconst TICK = \"var(--os9-gray-600)\"\n\n/* drawn on each pixel grid rather than scaled, so no pixel goes soft */\nfunction Watch16() {\n  const origin = { transformOrigin: \"7.5px 7.5px\" }\n  return (\n    <>\n      <rect x=\"4\" y=\"0\" width=\"7\" height=\"3\" fill={INK} />\n      <rect x=\"5\" y=\"0\" width=\"5\" height=\"2\" fill={STRAP} />\n      <rect x=\"4\" y=\"13\" width=\"7\" height=\"3\" fill={INK} />\n      <rect x=\"5\" y=\"14\" width=\"5\" height=\"2\" fill={STRAP} />\n      <path d=\"M4 2h7v1h1v1h1v7h-1v1h-1v1h-7v-1h-1v-1h-1v-7h1v-1h1z\" fill={INK} />\n      <rect x=\"13\" y=\"7\" width=\"1\" height=\"1\" fill={INK} />\n      <path d=\"M4 3h7v1h1v7h-1v1h-7v-1h-1v-7h1z\" fill=\"var(--os9-white)\" />\n      <rect x=\"7\" y=\"3\" width=\"1\" height=\"1\" fill={TICK} />\n      <rect x=\"7\" y=\"11\" width=\"1\" height=\"1\" fill={TICK} />\n      <rect x=\"3\" y=\"7\" width=\"1\" height=\"1\" fill={TICK} />\n      <rect x=\"11\" y=\"7\" width=\"1\" height=\"1\" fill={TICK} />\n      <rect className=\"os9-watch-hand os9-watch-hour\" style={origin} x=\"7\" y=\"7\" width=\"3\" height=\"1\" fill={INK} />\n      <rect className=\"os9-watch-hand os9-watch-minute\" style={origin} x=\"7\" y=\"4\" width=\"1\" height=\"4\" fill={INK} />\n    </>\n  )\n}\n\nfunction Watch12() {\n  const origin = { transformOrigin: \"5.5px 5.5px\" }\n  return (\n    <>\n      <rect x=\"3\" y=\"0\" width=\"5\" height=\"2\" fill={INK} />\n      <rect x=\"4\" y=\"0\" width=\"3\" height=\"1\" fill={STRAP} />\n      <rect x=\"3\" y=\"10\" width=\"5\" height=\"2\" fill={INK} />\n      <rect x=\"4\" y=\"11\" width=\"3\" height=\"1\" fill={STRAP} />\n      <path d=\"M3 1h5v1h1v1h1v5h-1v1h-1v1h-5v-1h-1v-1h-1v-5h1v-1h1z\" fill={INK} />\n      <rect x=\"10\" y=\"5\" width=\"1\" height=\"1\" fill={INK} />\n      <path d=\"M3 2h5v1h1v5h-1v1h-5v-1h-1v-5h1z\" fill=\"var(--os9-white)\" />\n      <rect className=\"os9-watch-hand os9-watch-hour\" style={origin} x=\"5\" y=\"5\" width=\"2\" height=\"1\" fill={INK} />\n      <rect className=\"os9-watch-hand os9-watch-minute\" style={origin} x=\"5\" y=\"3\" width=\"1\" height=\"3\" fill={INK} />\n    </>\n  )\n}\n\ninterface RetroWatchIconProps extends React.SVGAttributes<SVGSVGElement> {\n  /** Pixel size: 16 (default) or 12. */\n  size?: 12 | 16\n  /** Turn the hands. Default true. */\n  running?: boolean\n}\n\n/**\n * The OS 9 wristwatch busy cursor as a 16px pixel icon. Use it anywhere\n * something is working: a pending reply, a running tool call.\n */\nfunction RetroWatchIcon({ size = 16, running = true, className, ...props }: RetroWatchIconProps) {\n  return (\n    <>\n      <style href=\"os9-watch\" precedence=\"default\">\n        {WATCH_CSS}\n      </style>\n      <svg\n        width={size}\n        height={size}\n        viewBox={`0 0 ${size} ${size}`}\n        aria-hidden=\"true\"\n        shapeRendering=\"crispEdges\"\n        data-slot=\"retro-watch-icon\"\n        data-running={running || undefined}\n        className={cn(\"os9-watch inline-block shrink-0\", className)}\n        {...props}\n      >\n        {size === 16 ? <Watch16 /> : <Watch12 />}\n      </svg>\n    </>\n  )\n}\n\n/* ── elapsed seconds ───────────────────────────────────── */\n\n/** Whole seconds since `running` turned on; holds its value once it stops. */\nfunction useElapsedSeconds(running: boolean, since?: number) {\n  const [seconds, setSeconds] = React.useState(0)\n  React.useEffect(() => {\n    if (!running) return\n    const start = since ?? Date.now()\n    const update = () => setSeconds(Math.max(0, Math.round((Date.now() - start) / 1000)))\n    const timer = window.setInterval(update, 250)\n    return () => {\n      window.clearInterval(timer)\n      update()\n    }\n  }, [running, since])\n  return seconds\n}\n\nfunction secondsLabel(n: number) {\n  return n === 1 ? \"1 second\" : `${n} seconds`\n}\n\n/* ── thinking (pending) ────────────────────────────────── */\n\ninterface RetroThinkingProps extends Omit<React.ComponentProps<\"div\">, \"children\"> {\n  /** Default \"Thinking…\". */\n  label?: React.ReactNode\n  /** When the request started (ms since epoch). Defaults to when this mounted. */\n  since?: number\n  /** Count the seconds after three have passed. Default true. */\n  showElapsed?: boolean\n}\n\n/**\n * The pending state between Send and the first token. Its watch sits in the\n * same 16px column as RetroMessage's assistant icon, so it lines up in a\n * thread on its own.\n */\nfunction RetroThinking({\n  label = \"Thinking…\",\n  since,\n  showElapsed = true,\n  className,\n  ...props\n}: RetroThinkingProps) {\n  const seconds = useElapsedSeconds(true, since)\n  return (\n    <div\n      role=\"status\"\n      data-slot=\"retro-thinking\"\n      className={cn(\n        \"flex items-center gap-2 font-[family-name:var(--os9-font-sans)] text-[12px] leading-[1.5] text-os9-black\",\n        \"transition-[opacity,translate] duration-160 ease-[cubic-bezier(0.23,1,0.32,1)]\",\n        \"starting:translate-y-1 starting:opacity-0\",\n        \"in-data-[flair=classic]:transition-none motion-reduce:transition-none\",\n        className\n      )}\n      {...props}\n    >\n      <RetroWatchIcon />\n      <span>{label}</span>\n      <span aria-hidden=\"true\" className=\"contents\">\n        <RetroProgress indeterminate aria-label=\"Working\" className=\"h-[10px] w-[56px]\" />\n      </span>\n      {showElapsed && seconds >= 3 ? (\n        <span className=\"text-[10px] tabular-nums text-os9-gray-700\">{seconds} sec</span>\n      ) : null}\n    </div>\n  )\n}\n\n/* ── reasoning (disclosure) ────────────────────────────── */\n\ninterface RetroReasoningProps\n  extends Omit<\n    React.ComponentProps<typeof RetroCollapsible>,\n    \"children\" | \"open\" | \"defaultOpen\" | \"onOpenChange\" | \"part\"\n  > {\n  /** A reasoning part from a message; supplies `text` and `streaming`. */\n  part?: RetroReasoningPart\n  /** The reasoning text. Overrides `part.text`. */\n  text?: string\n  /** The model is still thinking. Defaults to `part.state === \"streaming\"`. */\n  streaming?: boolean\n  /** How long it thought, in seconds. Measured while streaming if left out. */\n  duration?: number\n  /** Controlled open state. Left out, it opens while streaming and closes after. */\n  open?: boolean\n  defaultOpen?: boolean\n  onOpenChange?: (open: boolean) => void\n  /** Custom content instead of the text. */\n  children?: React.ReactNode\n}\n\nfunction RetroReasoning({\n  part,\n  text: textProp,\n  streaming: streamingProp,\n  duration,\n  open: openProp,\n  defaultOpen,\n  onOpenChange,\n  children,\n  className,\n  ...props\n}: RetroReasoningProps) {\n  const text = textProp ?? part?.text ?? \"\"\n  const streaming = streamingProp ?? part?.state === \"streaming\"\n  const measured = useElapsedSeconds(streaming)\n  const [everStreamed, setEverStreamed] = React.useState(streaming)\n  if (streaming && !everStreamed) setEverStreamed(true)\n\n  /* null: follow the stream; a boolean: the reader chose */\n  const [chosen, setChosen] = React.useState<boolean | null>(defaultOpen ?? null)\n  const open = openProp ?? chosen ?? streaming\n\n  const seconds = duration ?? (everStreamed ? Math.max(1, measured) : undefined)\n  const label = streaming\n    ? \"Thinking…\"\n    : seconds !== undefined\n      ? `Thought for ${secondsLabel(Math.round(seconds))}`\n      : \"Thought for a moment\"\n\n  return (\n    <RetroCollapsible\n      data-streaming={streaming || undefined}\n      open={open}\n      onOpenChange={(next) => {\n        setChosen(next)\n        onOpenChange?.(next)\n      }}\n      className={cn(\"flex flex-col\", className)}\n      {...props}\n    >\n      <RetroCollapsibleTrigger className=\"self-start py-[2px] text-os9-gray-800 hover:text-os9-black focus-visible:os9-focus-ring\">\n        {streaming ? <RetroWatchIcon size={12} className=\"ml-[2px]\" /> : null}\n        <span aria-live=\"polite\">{label}</span>\n      </RetroCollapsibleTrigger>\n      <RetroCollapsibleContent className=\"pl-3 pt-1 pb-1 text-[11px] leading-[1.5] text-os9-gray-700\">\n        <div className=\"border-l border-os9-gray-500 pl-2\">\n          {children ?? <RetroStreamingText text={text} streaming={streaming} />}\n        </div>\n      </RetroCollapsibleContent>\n    </RetroCollapsible>\n  )\n}\n\nexport { RetroThinking, RetroReasoning, RetroWatchIcon }\nexport type { RetroThinkingProps, RetroReasoningProps, RetroWatchIconProps }\n",
      "type": "registry:ui"
    }
  ],
  "type": "registry:ui"
}