{"$schema":"https://ui.shadcn.com/schema/registry-item.json","name":"thinking-indicator","type":"registry:ui","title":"Thinking Indicator","description":"The loading state before the first token — bouncing dots, a blinking caret, or a labeled status line while the model thinks.","author":"Scrim UI (https://scrimui.dev)","categories":["reasoning"],"docs":"https://scrimui.dev/components/thinking-indicator","dependencies":[],"registryDependencies":[],"files":[{"path":"src/showcase/thinking-indicator/thinking-indicator.tsx","content":"\"use client\";\n\nimport * as React from \"react\";\n\n/* ------------------------------------------------------------------ */\n/* Types                                                               */\n/* ------------------------------------------------------------------ */\n\nexport type ThinkingIndicatorProps = {\n  /** Visual style — bouncing dots, a blinking caret, or a labeled pulse. */\n  variant?: \"dots\" | \"caret\" | \"label\";\n  /** Short status line shown alongside the animation (default \"Thinking\"). */\n  label?: string;\n  className?: string;\n};\n\n/* ------------------------------------------------------------------ */\n/* Animations — one shared keyframes block                             */\n/* ------------------------------------------------------------------ */\n\nconst KEYFRAMES = `\n@keyframes aiui-think-bounce{0%,80%,100%{opacity:.25;transform:scale(.85)}40%{opacity:1;transform:scale(1)}}\n@keyframes aiui-think-caret{50%{opacity:0}}\n/* .85, not .4: this one pulses real text, and at .4 the label rendered\n   1.68:1 against the bubble — unreadable for part of every cycle. The dots\n   above may fade further because they are decorative and aria-hidden. */\n@keyframes aiui-think-pulse{0%,100%{opacity:.85}50%{opacity:1}}\n`;\n\nfunction Dots() {\n  return (\n    <span className=\"flex items-center gap-1\" aria-hidden>\n      {[0, 1, 2].map((i) => (\n        <span\n          key={i}\n          className=\"h-1.5 w-1.5 rounded-full bg-current\"\n          style={{ animation: `aiui-think-bounce 1.2s ${i * 0.15}s infinite ease-in-out` }}\n        />\n      ))}\n    </span>\n  );\n}\n\nfunction Caret() {\n  return (\n    <span\n      aria-hidden\n      className=\"inline-block h-[1em] w-[2px] translate-y-[2px] rounded-full bg-current\"\n      style={{ animation: \"aiui-think-caret 1s steps(1) infinite\" }}\n    />\n  );\n}\n\n/* ------------------------------------------------------------------ */\n/* ThinkingIndicator                                                   */\n/* ------------------------------------------------------------------ */\n\nexport function ThinkingIndicator({\n  variant = \"dots\",\n  label = \"Thinking\",\n  className = \"\",\n}: ThinkingIndicatorProps) {\n  return (\n    <div\n      className={`inline-flex items-center gap-2 text-sm text-zinc-500 dark:text-zinc-400 ${className}`}\n    >\n      <style>{KEYFRAMES}</style>\n      <span className=\"flex h-8 w-8 shrink-0 select-none items-center justify-center rounded-lg bg-zinc-900 text-xs font-semibold text-white dark:bg-zinc-100 dark:text-zinc-900\">\n        AI\n      </span>\n      <span className=\"flex min-h-8 items-center gap-2 rounded-2xl rounded-tl-md border border-zinc-200 bg-zinc-50 px-3.5 py-2 dark:border-zinc-800 dark:bg-zinc-800/60\">\n        {variant === \"dots\" && <Dots />}\n        {variant === \"caret\" && <Caret />}\n        {variant === \"label\" && (\n          <span\n            /* zinc-600 so the trough of the pulse still clears AA (5.05:1). */\n            className=\"inline-block text-zinc-600 dark:text-zinc-300\"\n            style={{ animation: \"aiui-think-pulse 1.4s infinite ease-in-out\" }}\n          >\n            {label}…\n          </span>\n        )}\n        {variant !== \"label\" && <span>{label}…</span>}\n      </span>\n    </div>\n  );\n}\n","type":"registry:ui","target":"components/ui/thinking-indicator.tsx"}]}