From fa10425e2cf73097e42b8132fe6232ca66208c99 Mon Sep 17 00:00:00 2001 From: coxyj Date: Mon, 30 Mar 2026 16:27:09 +0800 Subject: [PATCH] feat(playback): add browser TTS mute detection and notification - detect abnormally fast TTS completion indicating muted state - show toast notification when browser TTS appears muted - add bilingual prompts for Chinese and English users Co-authored-by: Qwen-Coder --- components/stage.tsx | 7 +++++++ lib/i18n/common.ts | 6 ++++++ lib/playback/engine.ts | 12 ++++++++++++ lib/playback/types.ts | 3 +++ 4 files changed, 28 insertions(+) diff --git a/components/stage.tsx b/components/stage.tsx index 856d20005..641ca6938 100644 --- a/components/stage.tsx +++ b/components/stage.tsx @@ -32,6 +32,7 @@ import { } from '@/components/ui/alert-dialog'; import { AlertTriangle } from 'lucide-react'; import { VisuallyHidden } from 'radix-ui'; +import { toast } from 'sonner'; /** * Stage Component @@ -528,6 +529,12 @@ export function Stage({ }, 1500); } }, + onBrowserTTSMuted: () => { + toast.warning(t('playback.browserTtsMuted'), { + duration: 5000, + id: 'browser-tts-muted', + }); + }, }); engineRef.current = engine; diff --git a/lib/i18n/common.ts b/lib/i18n/common.ts index 1bceb5d61..513c7ae29 100644 --- a/lib/i18n/common.ts +++ b/lib/i18n/common.ts @@ -37,6 +37,9 @@ export const commonZhCN = { exportSuccess: '导出成功', exportFailed: '导出失败', }, + playback: { + browserTtsMuted: '当前浏览器处于静音状态,无法听到语音。请打开系统或浏览器音量。', + }, } as const; export const commonEnUS = { @@ -78,4 +81,7 @@ export const commonEnUS = { exportSuccess: 'Export successful', exportFailed: 'Export failed', }, + playback: { + browserTtsMuted: 'Browser is muted. Please unmute your system or browser to hear the audio.', + }, } as const; diff --git a/lib/playback/engine.ts b/lib/playback/engine.ts index e13e60b18..a20bcc2c0 100644 --- a/lib/playback/engine.ts +++ b/lib/playback/engine.ts @@ -663,7 +663,19 @@ export class PlaybackEngine { utterance.lang = cjkRatio > CJK_LANG_THRESHOLD ? 'zh-CN' : 'en-US'; } + // Track start time to detect abnormally fast completion (likely due to mute) + const startTime = performance.now(); + const MIN_NORMAL_DURATION = 200; // ms - anything faster is suspicious + utterance.onend = () => { + const duration = performance.now() - startTime; + + // Detect abnormally fast completion - likely browser/system is muted + if (duration < MIN_NORMAL_DURATION && chunkText.trim().length > 0) { + log.warn(`Browser TTS chunk ended too quickly (${duration}ms) - possible mute state`); + this.callbacks.onBrowserTTSMuted?.(); + } + this.browserTTSChunkIndex++; if (this.mode === 'playing') { this.playBrowserTTSChunk(); // next chunk diff --git a/lib/playback/types.ts b/lib/playback/types.ts index 6347d2e1f..b94a89d05 100644 --- a/lib/playback/types.ts +++ b/lib/playback/types.ts @@ -59,4 +59,7 @@ export interface PlaybackEngineCallbacks { getPlaybackSpeed?: () => number; onComplete?: () => void; + + /** Browser TTS is muted - prompt user to unmute */ + onBrowserTTSMuted?: () => void; }