rohitsar567 Claude Opus 4.7 (1M context) commited on
Commit
94aeb46
·
1 Parent(s): f84acc7

fix(voice): KI-174 — revive on visibilitychange + window focus

Browse files

User report: "sometimes when I go away from clicking the text box, it
seems to not input my voice anymore. I have to restart the whole voice
thing."

Root cause: Chrome's SpeechRecognition auto-stops when the document
loses visibility (tab switch, app switch, screenshot tool, OS modal,
notification overlay). The KI-173 heartbeat is throttled to ~1Hz when
the tab is hidden, so revival takes 4-8s after returning.

Fix: added two immediate-revival event listeners:
- document.visibilitychange → visible
- window.focus

When fired, both call safeStart() if wantRunningRef.current AND no
text turn is pending AND document.visibilityState === "visible".
InvalidStateError swallowed by safeStart if recognition is already
running.

Combined with KI-173 heartbeat as a safety net, the mic should stay
alive across:
- Tab switches → visibilitychange fires
- App switches → window.focus fires
- Screenshot tool grabbing focus → window.focus fires
- Notification overlays clearing → window.focus fires
- All other transient browser quirks → 4s heartbeat catches them

Verification: npx tsc --noEmit clean (exit 0).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

frontend/src/lib/useStreamingVoice.ts CHANGED
@@ -539,6 +539,42 @@ export function useStreamingVoice(
539
  return () => clearInterval(tick);
540
  }, [enabled, isSupported, isTextRequestPendingRef, safeStart]);
541
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
542
  // Unmount cleanup.
543
  useEffect(() => {
544
  return () => {
 
539
  return () => clearInterval(tick);
540
  }, [enabled, isSupported, isTextRequestPendingRef, safeStart]);
541
 
542
+ // KI-174 (2026-05-15) — immediate-revival on visibility/focus changes.
543
+ // User reported: "sometimes when I go away from clicking the text box,
544
+ // it seems to not input my voice anymore. I have to restart the whole
545
+ // voice thing." Root cause: Chrome's SpeechRecognition auto-stops
546
+ // when the tab loses visibility (tab switch, app switch, screenshot,
547
+ // OS modal). The KI-173 heartbeat is throttled to ~1Hz when the tab
548
+ // is hidden, so it takes several seconds to revive after returning.
549
+ // Force-revival on:
550
+ // - document `visibilitychange` → visible
551
+ // - window `focus`
552
+ // Both check wantRunningRef + isTextRequestPendingRef before firing.
553
+ useEffect(() => {
554
+ if (!enabled || !isSupported) return;
555
+ if (typeof window === "undefined" || typeof document === "undefined") return;
556
+
557
+ const tryRevive = (trigger: string) => {
558
+ if (
559
+ wantRunningRef.current
560
+ && !isTextRequestPendingRef.current
561
+ && document.visibilityState === "visible"
562
+ ) {
563
+ console.debug("[useStreamingVoice] revival trigger=" + trigger);
564
+ safeStart();
565
+ }
566
+ };
567
+
568
+ const onVisible = () => tryRevive("visibilitychange");
569
+ const onFocus = () => tryRevive("window.focus");
570
+ document.addEventListener("visibilitychange", onVisible);
571
+ window.addEventListener("focus", onFocus);
572
+ return () => {
573
+ document.removeEventListener("visibilitychange", onVisible);
574
+ window.removeEventListener("focus", onFocus);
575
+ };
576
+ }, [enabled, isSupported, isTextRequestPendingRef, safeStart]);
577
+
578
  // Unmount cleanup.
579
  useEffect(() => {
580
  return () => {