From 245d007e2904424aab1a7dd24da533e86339f49d Mon Sep 17 00:00:00 2001 From: dadaloop82 Date: Tue, 26 May 2026 12:04:31 +0000 Subject: [PATCH] fix: Chrome speechSynthesis paused-state bug + better TTS diagnostics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _speakBrowser: Chrome sets speechSynthesis.paused=true after tab is backgrounded/minimized. Calling cancel() does NOT clear this state. Adding resume() before speak() inside the setTimeout fixes silent failure when user returns to the tab (e.g. switch app → back to browser). testTTS: add diagnostic messages for: - Android kiosk: check isTtsReady() and show actionable error if the native TTS engine hasn't initialized yet - Web browser: check getVoices().length before speaking; if empty, show error asking user to install a voice pack in OS settings (instead of always showing '✅ Riproduzione in corso' even when silent) --- assets/js/app.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/assets/js/app.js b/assets/js/app.js index 911f6df..38625c7 100644 --- a/assets/js/app.js +++ b/assets/js/app.js @@ -14127,8 +14127,14 @@ function _speakBrowser(text) { utt.lang = _currentLang === 'de' ? 'de-DE' : _currentLang === 'en' ? 'en-US' : 'it-IT'; } } - // Chrome quirk: cancel() + immediate speak() is silently dropped — 50 ms gap fixes it - setTimeout(() => window.speechSynthesis.speak(utt), 50); + // Chrome quirks: + // 1. cancel() + immediate speak() is silently dropped → 50 ms gap fixes it + // 2. speechSynthesis gets paused after tab backgrounding; cancel() does NOT + // clear the paused state — need an explicit resume() before speak() + setTimeout(() => { + if (window.speechSynthesis.paused) window.speechSynthesis.resume(); + window.speechSynthesis.speak(utt); + }, 50); }; // If voices haven't loaded yet (async in Chrome/Android), wait once then speak @@ -14161,6 +14167,12 @@ async function testTTS() { if (engine === 'browser') { // Kiosk native TTS bridge takes priority over Web Speech API if (typeof _kioskBridge !== 'undefined' && typeof _kioskBridge.speak === 'function') { + // Diagnostic: check if Android TTS engine is ready + const ready = typeof _kioskBridge.isTtsReady === 'function' ? _kioskBridge.isTtsReady() : 'unknown'; + if (ready === 'false') { + if (statusEl) { statusEl.style.display = 'block'; statusEl.className = 'settings-status error'; statusEl.textContent = '❌ Android TTS non inizializzato — riavvia l\'app kiosk o installa un motore TTS dal Play Store.'; } + return; + } const s = getSettings(); s.tts_rate = parseFloat(document.getElementById('setting-tts-rate')?.value) || 1; s.tts_pitch = parseFloat(document.getElementById('setting-tts-pitch')?.value) || 1; @@ -14180,6 +14192,12 @@ async function testTTS() { s.tts_rate = parseFloat(document.getElementById('setting-tts-rate')?.value) || 1; s.tts_pitch = parseFloat(document.getElementById('setting-tts-pitch')?.value) || 1; saveSettingsToStorage(s); + // Diagnostic: surface paused/no-voices state to user + const voices = window.speechSynthesis.getVoices(); + if (!voices.length) { + if (statusEl) { statusEl.style.display = 'block'; statusEl.className = 'settings-status error'; statusEl.textContent = '❌ Nessuna voce disponibile nel browser. Installa un pacchetto vocale nelle impostazioni del sistema operativo.'; } + return; + } _speakBrowser('Test vocale EverShelf. La sintesi vocale funziona correttamente.'); if (statusEl) { statusEl.style.display = 'block'; statusEl.className = 'settings-status success'; statusEl.textContent = '✅ Riproduzione in corso — controlla l\'audio del dispositivo.'; } return;