import { useEffect } from 'react'; import { Download, Trash2, X, CheckCircle2, Cpu, Mic, Volume2, AlertTriangle, RefreshCw, Ear, Activity } from 'lucide-react'; import type { VoiceModelStatus } from '../../../shared/voice'; import { bridge } from '../../lib/bridge'; import { useShallow } from 'zustand/react/shallow'; import { pickSpeaker } from '../../lib/voicePreference'; import { useVoiceModels } from '../../state/voiceModels'; import { useSettings } from '../../state/settings'; function formatMb(bytes: number): string { return bytes >= 1024 * 1024 * 1024 ? `${(bytes / 1024 ** 3).toFixed(2)} Go` : `${Math.round(bytes / 1024 / 1024)} Mo`; } function ModelRow({ model }: { model: VoiceModelStatus }) { const progress = useVoiceModels((s) => s.progress[model.id]); const { download, cancel, remove } = useVoiceModels.getState(); const activeStt = useSettings((s) => s.settings.voice.localModel); const activeTts = useSettings((s) => s.settings.speech.localModel); const update = useSettings((s) => s.update); const isActive = model.kind === 'stt' ? activeStt === model.id : activeTts === model.id; const busy = !!progress && (progress.phase === 'download' || progress.phase === 'extract'); const wakeMode = useSettings((s) => s.settings.voice.wakeMode); const activate = () => { if (model.kind === 'stt') update({ voice: { localModel: model.id, provider: 'local' } }); else if (model.kind === 'kws') update({ voice: { wakeMode: 'kws' } }); else if (model.kind === 'vad') update({ voice: { neuralVad: true } }); else { const { speech } = useSettings.getState().settings; update({ speech: { localModel: model.id, provider: 'local', localSpeaker: pickSpeaker(model, speech.language || 'fr-FR', speech.voiceGender ?? 'male') } }); } }; const neuralVad = useSettings((s) => s.settings.voice.neuralVad); const activeNow = model.kind === 'kws' ? wakeMode === 'kws' : model.kind === 'vad' ? neuralVad : isActive; return (
{engine?.modelsDir}
{error &&