{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "mic-selector",
  "type": "registry:ui",
  "description": "Microphone device selector with permission handling",
  "files": [
    {
      "path": "components/ui/MicSelector.tsx",
      "content": "import { useState, useEffect, useCallback } from \"react\";\nimport {\n  Select,\n  SelectContent,\n  SelectItem,\n  SelectTrigger,\n  SelectValue,\n} from \"@/components/ui/select\";\nimport { cn } from \"@/lib/utils\";\n\nexport interface AudioDevice {\n  deviceId: string;\n  label: string;\n  groupId: string;\n}\n\nexport interface MicSelectorProps {\n  /** Selected device ID (controlled). */\n  value?: string;\n  /** Called when the user selects a different device. */\n  onValueChange?: (deviceId: string) => void;\n  className?: string;\n  disabled?: boolean;\n}\n\n/**\n * Microphone device selector dropdown.\n * Enumerates available audio input devices and lets the user pick one.\n *\n * Requests microphone permission on first open if not already granted.\n * Listens for device changes (plug/unplug) and updates automatically.\n *\n * @example\n * ```tsx\n * const [deviceId, setDeviceId] = useState(\"\");\n * <MicSelector value={deviceId} onValueChange={setDeviceId} />\n * ```\n */\nexport function MicSelector({\n  value,\n  onValueChange,\n  className,\n  disabled = false,\n}: MicSelectorProps) {\n  const [devices, setDevices] = useState<AudioDevice[]>([]);\n  const [loading, setLoading] = useState(false);\n  const [hasPermission, setHasPermission] = useState(false);\n\n  const loadDevices = useCallback(async () => {\n    setLoading(true);\n    try {\n      if (!hasPermission) {\n        const stream = await navigator.mediaDevices.getUserMedia({ audio: true });\n        stream.getTracks().forEach((t) => t.stop());\n        setHasPermission(true);\n      }\n\n      const allDevices = await navigator.mediaDevices.enumerateDevices();\n      const audioInputs = allDevices\n        .filter((d) => d.kind === \"audioinput\")\n        .map((d) => ({\n          deviceId: d.deviceId,\n          label: d.label || `Microphone ${d.deviceId.slice(0, 5)}`,\n          groupId: d.groupId,\n        }));\n\n      setDevices(audioInputs);\n    } catch {\n      setDevices([]);\n    } finally {\n      setLoading(false);\n    }\n  }, [hasPermission]);\n\n  useEffect(() => {\n    loadDevices();\n    const handler = () => loadDevices();\n    navigator.mediaDevices?.addEventListener(\"devicechange\", handler);\n    return () => navigator.mediaDevices?.removeEventListener(\"devicechange\", handler);\n  }, [loadDevices]);\n\n  return (\n    <Select\n      value={value || \"\"}\n      onValueChange={onValueChange}\n      disabled={disabled || loading}\n    >\n      <SelectTrigger\n        className={cn(\"dg:text-sm\", className)}\n        data-agent-mic-selector\n        aria-label=\"Select microphone\"\n      >\n        <SelectValue placeholder={loading ? \"Loading…\" : \"Select microphone\"} />\n      </SelectTrigger>\n      <SelectContent>\n        {devices.length === 0 && !loading && (\n          <SelectItem value=\"__none__\" disabled>\n            No microphones found\n          </SelectItem>\n        )}\n        {devices.map((d) => (\n          <SelectItem key={d.deviceId} value={d.deviceId}>\n            {d.label}\n          </SelectItem>\n        ))}\n      </SelectContent>\n    </Select>\n  );\n}\n",
      "type": "registry:ui",
      "target": ""
    }
  ],
  "registryDependencies": [
    "utils",
    "select"
  ]
}
