import { Check, ChevronDown, Pause, Play, Search } from "lucide-react"; import { useMemo, useState } from "react"; import type { RecordingResponseSchema } from "@/client/types.gen"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Popover, PopoverContentInline, PopoverTrigger } from "@/components/ui/popover"; import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group"; import { useAudioPlayback } from "@/hooks/useAudioPlayback"; import { cn } from "@/lib/utils"; interface TextOrAudioInputProps { type: 'text' | 'audio'; onTypeChange: (type: 'text' | 'audio') => void; recordingId: string; onRecordingIdChange: (id: string) => void; recordings?: RecordingResponseSchema[]; /** Rendered when type === 'text' */ children: React.ReactNode; } export function TextOrAudioInput({ type, onTypeChange, recordingId, onRecordingIdChange, recordings = [], children, }: TextOrAudioInputProps) { return ( <> onTypeChange(value as 'text' | 'audio')} className="flex items-center gap-4" >
{type === 'text' ? ( children ) : ( )} ); } interface RecordingSelectProps { value: string; onChange: (id: string) => void; recordings: RecordingResponseSchema[]; } /** * Dropdown to select a pre-recorded audio file. * Re-exported so callers that only need the dropdown (e.g. tool configs with * their own none/custom/audio radio) can use it directly. */ export function RecordingSelect({ value, onChange, recordings }: RecordingSelectProps) { const [open, setOpen] = useState(false); const [search, setSearch] = useState(""); const { playingId, toggle, stop } = useAudioPlayback(); const selected = recordings.find((r) => String(r.id) === value); const filtered = useMemo(() => { if (!search) return recordings; const q = search.toLowerCase(); return recordings.filter((r) => r.recording_id.toLowerCase().includes(q) || r.transcript.toLowerCase().includes(q) || ((r.metadata?.original_filename as string) || "").toLowerCase().includes(q) ); }, [recordings, search]); const handleSelect = (rec: RecordingResponseSchema) => { stop(); onChange(String(rec.id)); setOpen(false); }; const handlePlay = async (e: React.MouseEvent, rec: RecordingResponseSchema) => { e.stopPropagation(); try { await toggle(rec.recording_id, rec.storage_key, rec.storage_backend); } catch { // Ignore playback errors } }; return (
{ if (!v) { stop(); setSearch(""); } setOpen(v); }}> {recordings.length === 0 ? (
No recordings available
) : (
setSearch(e.target.value)} className="h-8 pl-8 text-sm" autoFocus />
{filtered.length === 0 ? (
No recordings match “{search}”
) : filtered.map((r) => { const filename = (r.metadata?.original_filename as string) || ""; const isSelected = String(r.id) === value; const isPlaying = playingId === r.recording_id; return (
handleSelect(r)} > {r.recording_id} {filename && ( {filename} )} {r.transcript}
); })}
)}
); }