From af6de11540a3b2547a5b6d36a997d824ed662695 Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Fri, 5 Jun 2026 13:30:51 -0400 Subject: [PATCH] zebra-spaces: fix TDZ error on transcribe restore + self-capture diagnostics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fox 2026-06-05: "self transcribe not working, also 1:28:59 PM JS unhandled: can't access lexical declaration 'transcribeEnabled' before initialization." The restore block (button-binding site, ~line 1635) was reading transcribeEnabled before its `let` declaration further down (~line 2225). Script execution order: TDZ violated, the whole restore + worker pre-warm + capture-kick branch never ran. Subsequent code continued (the error fired async on click) but the restore-time initialization was skipped. Fix: hoist the const TRANSCRIBE_KEY + let transcribeEnabled + the localStorage load up to BEFORE the click-handler binding. Now the restore block has a valid initialized value to read. Plus diagnostic logging in startSelfCapture for the early-return branches (no micStream / already running / worklet module load failed) — so we can SEE why self capture isn't engaging if it still isn't after this fix. Self-transcribe issue should also be addressed by this TDZ fix: the restore branch is where startSelfCapture would have been called on page-load with state=on, but the TDZ throw skipped it. After this lands, refreshes preserve transcribe state AND self capture engages alongside remote captures. --- web/zebra-spaces.html | 29 ++++++++++++++++++----------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/web/zebra-spaces.html b/web/zebra-spaces.html index 1c0e4af..6739acb 100644 --- a/web/zebra-spaces.html +++ b/web/zebra-spaces.html @@ -1625,6 +1625,16 @@ function renderIdentity(){ if (el) el.textContent = myKeys ? myKeys.pubHex : ''; } +/* Transcribe-state declarations live up here (not next to the Whisper + * code further down) so the click-handler binding + restore block + * just below CAN read transcribeEnabled without tripping a TDZ + * error. Model is cached after first download so subsequent loads + * spin the worker up fast. Fox 2026-06-05: "transcribe states + * should be saved in localstorage to survive on refresh." */ +const TRANSCRIBE_KEY = 'zspc:transcribe-on'; +let transcribeEnabled = false; +try { if (localStorage.getItem(TRANSCRIBE_KEY) === '1') transcribeEnabled = true; } catch(_){} + $('btn-vault').addEventListener('click', () => $('vault-panel').classList.toggle('hidden')); $('btn-transcribe').addEventListener('click', () => toggleTranscribe().catch(e => logLine('err','transcribe toggle: '+e.message))); /* If transcribe was on at the previous session, restore the state @@ -2217,13 +2227,9 @@ function transcribeViaWorker(chunk){ }); } -/* Transcribe state persists in localStorage so refresh restores the - * user's preference. Model is cached after first download so subsequent - * loads spin the worker up fast. Fox 2026-06-05: "transcribe states - * should be saved in localstorage to survive on refresh." */ -const TRANSCRIBE_KEY = 'zspc:transcribe-on'; -let transcribeEnabled = false; -try { if (localStorage.getItem(TRANSCRIBE_KEY) === '1') transcribeEnabled = true; } catch(_){} +/* (transcribeEnabled + TRANSCRIBE_KEY are declared earlier near the + * top of the script so the restore-from-localStorage block at the + * button-binding site doesn't trip a TDZ error.) */ /* whisper-tiny.en hallucinations on silence / low-energy chunks. The * RMS gate in the capture worklet catches dead silence; this set * catches what gets past it — quiet-room ambient with the same @@ -2336,7 +2342,8 @@ async function handleWhisperChunk(uuid, chunk){ * the user's handle. */ let _selfCapture = null; async function startSelfCapture(){ - if (_selfCapture || !micStream) return; + if (_selfCapture){ logLine('', 'whisper: self capture already running'); return; } + if (!micStream){ logLine('', 'whisper: self capture skip (no micStream yet)'); return; } /* audioCtx may not exist yet for a host alone in the room (no * remote speakers have triggered attachAudioStreamViaWorklet). Make * sure it's up before we try to attach a worklet to it. Resume it @@ -2350,7 +2357,7 @@ async function startSelfCapture(){ try { audioCtx.resume(); } catch(_){} } const ok = await loadWhisperCaptureWorklet(audioCtx); - if (!ok) return; + if (!ok){ logLine('err','self capture: worklet module load failed'); return; } try { const src = audioCtx.createMediaStreamSource(micStream); const cap = new AudioWorkletNode(audioCtx, 'whisper-capture'); @@ -7413,8 +7420,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');