import { useMemo } from "react"; import { useMicMeter } from "../hooks/useMicMeter"; import { CameraIcon, CheckIcon, ChevronDown, MicIcon } from "./Icons"; import { Switch } from "./Switch"; import { useRowMenu } from "./useRowMenu"; function Toggle({ on, onChange, label, }: { on: boolean; onChange: (v: boolean) => void; label: string; }) { return ( ); } // Live mic level meter — a single wave line driven by real audio. The hook // owns the analyser and writes the path's `d`; the line oscillates around the // center and flattens when silent. function MicWave({ deviceId, active }: { deviceId: string; active: boolean }) { const pathRef = useMicMeter({ deviceId, active }); return ( ); } export function MediaDeviceRow({ kind, devices, selectedId, selectedLabel, onSelect, onRefresh, on, onToggle, systemAudio, onSystemAudioToggle, meterActive = true, }: { kind: "camera" | "mic"; devices: MediaDeviceInfo[]; selectedId: string; selectedLabel?: string; onSelect: (id: string, label: string) => void; onRefresh: () => void; on: boolean; onToggle: (v: boolean) => void; systemAudio?: boolean; onSystemAudioToggle?: (v: boolean) => void; meterActive?: boolean; }) { const current = useMemo( () => selectedId ? (devices.find((d) => d.deviceId === selectedId) ?? null) : null, [devices, selectedId], ); const label = // Prefer the live label from the enumerated device. current?.label || (selectedId ? devices.length > 0 ? // List is loaded but the saved device isn't in it — genuinely gone. kind === "camera" ? "Selected camera unavailable" : "Selected mic unavailable" : // List is still locked (no getUserMedia grant yet this session, // e.g. a cold launch). Fall back to the label we persisted with // the id last time so the user still sees their device by name. selectedLabel || (kind === "camera" ? "Camera" : "Microphone") : kind === "camera" ? "Default camera" : "Default mic"); const Icon = kind === "camera" ? CameraIcon : MicIcon; const { open, setOpen, rowRef } = useRowMenu(); const disabled = !on; const canOpenMenu = !disabled || (kind === "mic" && !!onSystemAudioToggle); const defaultLabel = kind === "camera" ? "Default camera" : "Default mic"; const accessLabel = kind === "camera" ? "Allow camera access" : "Allow microphone access"; const refreshLabel = kind === "camera" ? "Refresh cameras" : "Refresh microphones"; return (