{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "voice-button",
  "type": "registry:ui",
  "description": "All-in-one voice button with 5 lifecycle states",
  "files": [
    {
      "path": "components/ui/VoiceButton.tsx",
      "content": "import { useAgentState, useAgentMode } from \"@deepgram/react\";\nimport { Button } from \"@/components/ui/button\";\nimport { cn } from \"@/lib/utils\";\n\nexport type VoiceButtonState = \"idle\" | \"connecting\" | \"listening\" | \"speaking\" | \"error\";\n\nexport interface VoiceButtonProps {\n  className?: string;\n  /** Label for each state. Keys: idle, connecting, listening, speaking, error */\n  labels?: Partial<Record<VoiceButtonState, unknown>>;\n  onClick?: () => void;\n}\n\nconst DEFAULT_LABELS: Record<VoiceButtonState, string> = {\n  idle:       \"Start conversation\",\n  connecting: \"Connecting…\",\n  listening:  \"Listening…\",\n  speaking:   \"Agent speaking\",\n  error:      \"Error\",\n};\n\n/**\n * All-in-one voice interaction button that reflects the full agent lifecycle.\n * Click to start, click again to stop. Shows speaking/listening state.\n *\n * Five visual states exposed via `data-voice-state`:\n * - `idle`       — not connected, ready to start\n * - `connecting` — establishing connection (pulsing)\n * - `listening`  — mic open, accent border glow\n * - `speaking`   — agent producing audio, primary fill, pulsing\n * - `error`      — connection failed, destructive color\n *\n * @example\n * ```tsx\n * <AgentProvider config={config}>\n *   <VoiceButton />\n * </AgentProvider>\n * ```\n */\nexport function VoiceButton({ className, labels, onClick }: VoiceButtonProps) {\n  const { state, isActive, isConnecting, start, stop } = useAgentState();\n  const { isSpeaking, isListening } = useAgentMode();\n\n  const voiceState: VoiceButtonState =\n    isConnecting ? \"connecting\"\n    : isSpeaking  ? \"speaking\"\n    : isListening ? \"listening\"\n    : \"idle\";\n\n  const allLabels = { ...DEFAULT_LABELS, ...labels };\n  const label = allLabels[voiceState];\n\n  async function handleClick() {\n    if (isActive) { stop(); } else { await start(); }\n    onClick?.();\n  }\n\n  return (\n    <Button\n      data-agent-voice-button\n      data-voice-state={voiceState}\n      disabled={isConnecting}\n      aria-label={typeof label === \"string\" ? label : \"Voice agent\"}\n      onClick={handleClick}\n      className={cn(\n        // Base layout — override Button default sizing\n        \"dg:inline-flex dg:items-center dg:gap-2.5 dg:px-7 dg:py-3.5 dg:text-[15px] dg:font-semibold dg:h-auto\",\n        // Idle / base\n        \"dg:border dg:border-border dg:bg-card dg:text-foreground\",\n        \"dg:hover:bg-accent dg:hover:border-primary\",\n        \"dg:active:bg-[var(--accent-active)]\",\n        \"dg:focus-visible:outline-2 dg:focus-visible:outline-primary dg:focus-visible:outline-offset-2\",\n        // Connecting — subtle pulse\n        voiceState === \"connecting\" && \"dg:animate-dg-pulse-fast\",\n        // Listening — accent border glow (box-shadow rule lives in @layer components)\n        voiceState === \"listening\" && \"dg:border-primary\",\n        // Speaking — primary fill, pulse\n        voiceState === \"speaking\" && [\n          \"dg:bg-primary dg:text-primary-foreground dg:border-primary\",\n          \"dg:hover:bg-[var(--primary-hover)]\",\n          \"dg:animate-dg-pulse-slow\",\n          \"dg:[&_svg]:stroke-primary-foreground\",\n        ],\n        // Error (reserved for future use — exposed via data-voice-state for consumer styling)\n        (voiceState as string) === \"error\" && \"dg:border-destructive dg:text-destructive dg:hover:border-destructive\",\n        // Connecting disabled cursor\n        isConnecting && \"dg:cursor-wait\",\n        className\n      )}\n    >\n      <svg\n        width=\"20\" height=\"20\" viewBox=\"0 0 24 24\"\n        fill=\"none\" stroke=\"currentColor\" strokeWidth={2}\n        strokeLinecap=\"round\" strokeLinejoin=\"round\"\n        aria-hidden=\"true\"\n      >\n        {isActive ? (\n          <rect x=\"6\" y=\"6\" width=\"12\" height=\"12\" rx=\"2\" fill=\"currentColor\" />\n        ) : (\n          <>\n            <path d=\"M12 2a3 3 0 0 1 3 3v7a3 3 0 0 1-6 0V5a3 3 0 0 1 3-3Z\" />\n            <path d=\"M19 10v2a7 7 0 0 1-14 0v-2\" />\n            <line x1=\"12\" x2=\"12\" y1=\"19\" y2=\"22\" />\n          </>\n        )}\n      </svg>\n      <span>{label as React.ReactNode}</span>\n    </Button>\n  );\n}\n",
      "type": "registry:ui",
      "target": ""
    }
  ],
  "dependencies": [
    "@deepgram/react"
  ],
  "registryDependencies": [
    "utils",
    "button"
  ]
}
