{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "stop-generating",
  "title": "Stop Generating",
  "description": "The control that interrupts a running answer.",
  "dependencies": [
    "lucide-react@^1.31.0"
  ],
  "registryDependencies": [
    "utils"
  ],
  "files": [
    {
      "path": "registry/default/stop-generating/stop-generating.tsx",
      "content": "\"use client\"\n\nimport * as React from \"react\"\nimport { Square } from \"lucide-react\"\n\nimport { cn } from \"@/lib/utils\"\n\nexport type StopGeneratingProps = Omit<\n  React.ButtonHTMLAttributes<HTMLButtonElement>,\n  \"children\" | \"onClick\"\n> & {\n  onStop: () => void\n  /** Renders nothing while false. Defaults to true. */\n  running?: boolean\n  label?: string\n  /** Epoch milliseconds. Drives the live elapsed reading. */\n  startedAt?: number\n  showElapsed?: boolean\n  /** Stop on Escape as well as on click. Defaults to true. */\n  shortcut?: boolean\n}\n\nfunction useElapsed(startedAt: number | undefined, running: boolean) {\n  const [clock, setClock] = React.useState({ startedAt, now: startedAt ?? 0 })\n\n  // A new run resets the reading in the same render that introduced it, so the\n  // old run's elapsed time is never shown against the new one's start.\n  if (clock.startedAt !== startedAt) {\n    setClock({ startedAt, now: startedAt ?? 0 })\n  }\n\n  React.useEffect(() => {\n    if (startedAt === undefined || !running) return\n\n    const timer = setInterval(\n      () => setClock({ startedAt, now: Date.now() }),\n      100\n    )\n\n    return () => clearInterval(timer)\n  }, [startedAt, running])\n\n  if (startedAt === undefined) return undefined\n\n  return Math.max(0, clock.now - startedAt)\n}\n\nexport function StopGenerating({\n  onStop,\n  running = true,\n  label = \"Stop generating\",\n  startedAt,\n  showElapsed = true,\n  shortcut = true,\n  className,\n  ...buttonProps\n}: StopGeneratingProps) {\n  const elapsed = useElapsed(startedAt, running)\n\n  const stop = React.useRef(onStop)\n\n  React.useEffect(() => {\n    stop.current = onStop\n  }, [onStop])\n\n  React.useEffect(() => {\n    if (!shortcut || !running) return\n\n    const onKeyDown = (event: KeyboardEvent) => {\n      if (event.key === \"Escape\") stop.current()\n    }\n\n    window.addEventListener(\"keydown\", onKeyDown)\n\n    return () => window.removeEventListener(\"keydown\", onKeyDown)\n  }, [shortcut, running])\n\n  if (!running) return null\n\n  return (\n    <button\n      type=\"button\"\n      data-slot=\"stop-generating\"\n      className={cn(\n        \"border-border text-muted-foreground hover:text-foreground hover:bg-muted inline-flex min-h-9 items-center gap-2 rounded-full border px-3 text-xs transition-colors duration-150 motion-reduce:transition-none\",\n        className\n      )}\n      onClick={() => onStop()}\n      {...buttonProps}\n    >\n      <Square aria-hidden=\"true\" size={11} className=\"fill-current\" />\n      {label}\n      {showElapsed && elapsed !== undefined ? (\n        <span className=\"text-muted-foreground/70 tabular-nums\">\n          {(elapsed / 1000).toFixed(1)}s\n        </span>\n      ) : null}\n    </button>\n  )\n}\n",
      "type": "registry:ui"
    }
  ],
  "categories": [
    "ai",
    "agent",
    "streaming"
  ],
  "type": "registry:ui"
}