From fc8ab04bf8224f5fa6206baba8f8f3d46c09bed4 Mon Sep 17 00:00:00 2001 From: Hank Zhang Date: Wed, 17 Jun 2026 12:14:46 +0800 Subject: [PATCH] feat: play notification sound when agent finishes responding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Play a short ascending two-tone beep (Web Audio API) when the agent completes a response, so users can switch away from the app and still know when an answer is ready. Changes: - Chat: add useEffect watching isLoading transitions, generates a brief sine-wave beep (880→1100 Hz, 0.2s) using AudioContext. No external audio files needed. Fails silently if audio is blocked. --- src/renderer/src/screens/Chat/Chat.tsx | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/renderer/src/screens/Chat/Chat.tsx b/src/renderer/src/screens/Chat/Chat.tsx index eed0a00b4..dcc56c893 100644 --- a/src/renderer/src/screens/Chat/Chat.tsx +++ b/src/renderer/src/screens/Chat/Chat.tsx @@ -81,6 +81,32 @@ function Chat({ useEffect(() => { onLoadingChange?.(runId, isLoading); }, [runId, isLoading, onLoadingChange]); + + // Play a notification sound when the agent finishes responding + const prevLoadingRef = useRef(isLoading); + useEffect(() => { + const wasLoading = prevLoadingRef.current; + prevLoadingRef.current = isLoading; + if (!wasLoading || isLoading) return; + // Agent just finished — play a short notification beep + try { + const ctx = new AudioContext(); + const osc = ctx.createOscillator(); + const gain = ctx.createGain(); + osc.connect(gain); + gain.connect(ctx.destination); + osc.type = "sine"; + // Play two quick ascending tones + osc.frequency.setValueAtTime(880, ctx.currentTime); + osc.frequency.setValueAtTime(1100, ctx.currentTime + 0.08); + gain.gain.setValueAtTime(0.12, ctx.currentTime); + gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.2); + osc.start(ctx.currentTime); + osc.stop(ctx.currentTime + 0.2); + } catch { + // AudioContext may fail in some environments — silently ignore + } + }, [isLoading]); const [hermesSessionId, setHermesSessionId] = useState( initialSessionId ?? null, );