fix: voice input in Electron - local Whisper STT + network error handling

- Add local Whisper STT via Transformers.js with Web Worker (no UI freeze)
- Default sttProvider to 'local' in Electron (browser STT unavailable)
- Fix infinite toast loop: stop auto-restart on network errors
- Add retry limit with exponential backoff for transient STT errors
- Append voice transcript to input field (append-inline), not replace
- Add model catalog with download/load button in Voice Settings
This commit is contained in:
Bohdan Triapitsyn
2026-05-14 01:19:52 +03:00
parent ceb5bbd5dd
commit ef85c63336
18 changed files with 1114 additions and 89 deletions
@@ -1301,6 +1301,14 @@ export const settingsDict = {
'settings.voice.page.provider.custom': 'Custom',
'settings.voice.page.provider.say': 'Say',
'settings.voice.page.provider.server': 'Server',
'settings.voice.page.provider.wasm': 'Local',
'settings.voice.page.stt.wasmModel': 'Whisper Model',
'settings.voice.page.stt.wasmLoaded': 'Model loaded, ready',
'settings.voice.page.stt.wasmDownloading': 'Downloading model...',
'settings.voice.page.stt.wasmLoading': 'Loading model...',
'settings.voice.page.stt.wasmNotLoaded': 'Model will load on first voice use',
'settings.voice.page.stt.wasmDownload': 'Load',
'settings.voice.page.stt.wasmRetry': 'Retry',
'settings.voice.page.tooltip.browser': 'Free, offline, limited mobile support.',
'settings.voice.page.tooltip.openai': 'High quality, mobile ready, needs API key.',
'settings.voice.page.tooltip.custom': 'OpenAI-compatible server (for example Kokoro).',
@@ -1301,6 +1301,14 @@ export const settingsDict = {
"settings.voice.page.provider.custom": "Personalizado",
"settings.voice.page.provider.say": "Decir",
"settings.voice.page.provider.server": "Servidor",
"settings.voice.page.provider.wasm": "Local",
"settings.voice.page.stt.wasmModel": "Modelo Whisper",
"settings.voice.page.stt.wasmLoaded": "Modelo cargado",
"settings.voice.page.stt.wasmDownloading": "Descargando modelo...",
"settings.voice.page.stt.wasmLoading": "Cargando modelo...",
"settings.voice.page.stt.wasmNotLoaded": "El modelo se descargará en el primer uso de voz",
"settings.voice.page.stt.wasmDownload": "Descargar",
"settings.voice.page.stt.wasmRetry": "Reintentar",
"settings.voice.page.tooltip.browser": "Gratuito, offline, soporte limitado en móvil.",
"settings.voice.page.tooltip.openai": "Calidad alta, compatible con móviles, requiere clave de API.",
"settings.voice.page.tooltip.custom": "Servidor compatible con OpenAI (por ejemplo Kokoro).",
@@ -1301,6 +1301,14 @@ export const settingsDict = {
'settings.voice.page.provider.custom': '사용자 정의',
'settings.voice.page.provider.say': 'Say',
'settings.voice.page.provider.server': '서버',
'settings.voice.page.provider.wasm': '로컬',
'settings.voice.page.stt.wasmModel': 'Whisper 모델',
'settings.voice.page.stt.wasmLoaded': '모델 로드됨',
'settings.voice.page.stt.wasmDownloading': '모델 다운로드 중...',
'settings.voice.page.stt.wasmLoading': '모델 로드 중...',
'settings.voice.page.stt.wasmNotLoaded': '첫 음성 사용 시 모델이 다운로드됩니다',
'settings.voice.page.stt.wasmDownload': '다운로드',
'settings.voice.page.stt.wasmRetry': '재시도',
'settings.voice.page.tooltip.browser': '무료, 오프라인, 모바일 지원 제한.',
'settings.voice.page.tooltip.openai': '고품질, 모바일 지원, API key 필요.',
'settings.voice.page.tooltip.custom': 'OpenAI 호환 서버(예: Kokoro).',
@@ -1527,6 +1527,14 @@ export const settingsDict = {
'settings.voice.page.provider.custom': 'Własny',
'settings.voice.page.provider.say': 'Say',
'settings.voice.page.provider.server': 'Serwer',
'settings.voice.page.provider.wasm': 'Lokalny',
'settings.voice.page.stt.wasmModel': 'Model Whisper',
'settings.voice.page.stt.wasmLoaded': 'Model załadowany',
'settings.voice.page.stt.wasmDownloading': 'Pobieranie modelu...',
'settings.voice.page.stt.wasmLoading': 'Ładowanie modelu...',
'settings.voice.page.stt.wasmNotLoaded': 'Model zostanie pobrany przy pierwszym użyciu',
'settings.voice.page.stt.wasmDownload': 'Pobierz',
'settings.voice.page.stt.wasmRetry': 'Spróbuj ponownie',
'settings.voice.page.section.playbackAndSummary': 'Odtwarzanie i podsumowanie',
'settings.voice.page.section.speechRecognition': 'Rozpoznawanie mowy',
'settings.voice.page.section.voiceSetup': 'Konfiguracja głosu',
@@ -1301,6 +1301,14 @@ export const settingsDict = {
"settings.voice.page.provider.custom": "Personalizado",
"settings.voice.page.provider.say": "Falar",
"settings.voice.page.provider.server": "Servidor",
"settings.voice.page.provider.wasm": "Local",
"settings.voice.page.stt.wasmModel": "Modelo Whisper",
"settings.voice.page.stt.wasmLoaded": "Modelo carregado",
"settings.voice.page.stt.wasmDownloading": "Baixando modelo...",
"settings.voice.page.stt.wasmLoading": "Carregando modelo...",
"settings.voice.page.stt.wasmNotLoaded": "O modelo será baixado no primeiro uso",
"settings.voice.page.stt.wasmDownload": "Baixar",
"settings.voice.page.stt.wasmRetry": "Tentar novamente",
"settings.voice.page.tooltip.browser": "Gratuito, offline, suporte limitado em dispositivos móveis.",
"settings.voice.page.tooltip.openai": "Alta qualidade, compatível com dispositivos móveis, exige chave de API.",
"settings.voice.page.tooltip.custom": "Servidor compatível com OpenAI (por exemplo Kokoro).",
@@ -1301,6 +1301,14 @@ export const settingsDict = {
"settings.voice.page.provider.custom": "Власний",
"settings.voice.page.provider.say": "Say",
"settings.voice.page.provider.server": "Сервер",
"settings.voice.page.provider.wasm": "Локально",
"settings.voice.page.stt.wasmModel": "Модель Whisper",
"settings.voice.page.stt.wasmLoaded": "Модель завантажено",
"settings.voice.page.stt.wasmDownloading": "Завантаження моделі...",
"settings.voice.page.stt.wasmLoading": "Завантаження моделі...",
"settings.voice.page.stt.wasmNotLoaded": "Модель завантажиться при першому використанні",
"settings.voice.page.stt.wasmDownload": "Завантажити",
"settings.voice.page.stt.wasmRetry": "Спробувати знову",
"settings.voice.page.tooltip.browser": "Безкоштовна, офлайн, обмежена мобільна підтримка.",
"settings.voice.page.tooltip.openai": "Висока якість, готово для мобільних пристроїв, потрібен API ключ.",
"settings.voice.page.tooltip.custom": "OpenAI-сумісний сервер, наприклад Kokoro.",
@@ -1301,6 +1301,14 @@ export const settingsDict = {
'settings.voice.page.provider.custom': '自定义',
'settings.voice.page.provider.say': 'Say',
'settings.voice.page.provider.server': '服务器',
'settings.voice.page.provider.wasm': '本地',
'settings.voice.page.stt.wasmModel': 'Whisper 模型',
'settings.voice.page.stt.wasmLoaded': '模型已加载',
'settings.voice.page.stt.wasmDownloading': '下载模型中...',
'settings.voice.page.stt.wasmLoading': '加载模型中...',
'settings.voice.page.stt.wasmNotLoaded': '模型将在首次使用时下载',
'settings.voice.page.stt.wasmDownload': '下载',
'settings.voice.page.stt.wasmRetry': '重试',
'settings.voice.page.tooltip.browser': '免费、离线,移动端支持有限。',
'settings.voice.page.tooltip.openai': '质量高,移动端可用,需要 API Key。',
'settings.voice.page.tooltip.custom': 'OpenAI 兼容服务器(例如 Kokoro)。',