From 9345975c586c107ee895192c8f52fd8f3d5bcc6b Mon Sep 17 00:00:00 2001 From: Russell Ballestrini Date: Fri, 5 Jun 2026 13:35:36 -0400 Subject: [PATCH] zebra-spaces: defer transcribe-restore past script-eval to avoid TDZ chain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fox 2026-06-05: "1:34:48 PM JS unhandled: can't access lexical declaration 'whisperWorker' before initialization." The button-binding restore block ran synchronously at script eval time, BEFORE the rest of the file's let/const declarations had been initialized. transcribeEnabled and TRANSCRIBE_KEY got hoisted earlier in the previous fix, but the restore body itself reaches further down — into whisperWorker, listenerAudioNodes, audioCtx, micStream, and a chain of helper functions whose bodies access more `let`s. Any of those is enough to throw. Wrap the whole restore in setTimeout(fn, 0). The current synchronous script finishes (all decls initialized), then the queued task runs. Now ensureWhisperWorker, startCaptureForUuid, startSelfCapture etc. all see fully-initialized state. Also added a try/catch around the block so any remaining edge-case error gets logged instead of bubbling to window.onerror. --- web/zebra-spaces.html | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/web/zebra-spaces.html b/web/zebra-spaces.html index cee1daa..131aca5 100644 --- a/web/zebra-spaces.html +++ b/web/zebra-spaces.html @@ -1637,23 +1637,24 @@ try { if (localStorage.getItem(TRANSCRIBE_KEY) === '1') transcribeEnabled = true $('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 - * now (UI label, transcript-log visibility, worker pre-warm). - * Captures attach lazily as remote speakers arrive and self capture - * fires when micStream is available — both paths already check the - * transcribeEnabled flag. */ +/* If transcribe was on at the previous session, restore the state. + * Deferred via setTimeout so the rest of the script (including + * whisperWorker, listenerAudioNodes, audioCtx, micStream and the + * Whisper helper functions further down) has finished initializing + * — otherwise the restore body trips TDZ on those `let`/`const`s. + * Fox 2026-06-05: "can't access lexical declaration 'whisperWorker' + * before initialization." */ if (transcribeEnabled){ - /* call setTranscribe with the SAME value but force-apply UI + spin - * the worker. The function's "same-state" branch refreshes UI and - * returns without flipping the flag. */ - applyTranscribeUI(); - ensureWhisperWorker(); - /* If captures need to be installed (e.g. listenerAudioNodes - * populated before this code ran), kick them off. */ - for (const [uuid] of listenerAudioNodes){ - startCaptureForUuid(uuid).catch(()=>{}); - } - startSelfCapture().catch(()=>{}); + setTimeout(() => { + try { + applyTranscribeUI(); + ensureWhisperWorker(); + for (const [uuid] of listenerAudioNodes){ + startCaptureForUuid(uuid).catch(()=>{}); + } + startSelfCapture().catch(()=>{}); + } catch(e){ logLine('err', 'transcribe restore: '+e.message); } + }, 0); } /* log out — destructive: wipes Ed25519 + handle from localStorage and @@ -7426,8 +7427,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');