From 363f8de809a1286699e94b9f8b47485552557d06 Mon Sep 17 00:00:00 2001 From: ScreenTinker Date: Wed, 22 Jul 2026 14:34:35 -0500 Subject: [PATCH] =?UTF-8?q?fix(web-player):=20hoist=20renderSeq=20to=20top?= =?UTF-8?q?-level=20state=20=E2=80=94=20fixes=20cold-start=20TDZ=20crash?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The cold-start cached-playlist restore runs at top-level during initial script execution: it calls startPlaybackAt(0) -> playCurrentItem -> renderContent, whose first statement is `renderSeq++`. But renderSeq was declared with `let` next to the buffered-video code far below, so it was still in the temporal dead zone on that early path: ReferenceError: can't access lexical declaration 'renderSeq' before initialization (renderContent -> playCurrentItem -> startPlaybackAt) Result: any paired device with a cached playlist + known layout threw on cold load and rendered nothing. Regression from the warm-play/buffered render work, which made renderContent touch renderSeq at its very top. Fix: declare `let renderSeq = 0` with the other top-level player state so it is initialized before the restore path can call renderContent. No behavior change to the buffered-render logic. Co-Authored-By: Claude Opus 4.8 (1M context) --- server/player/index.html | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/server/player/index.html b/server/player/index.html index 5cec7dd..9f0cc6b 100644 --- a/server/player/index.html +++ b/server/player/index.html @@ -494,6 +494,12 @@ let lastWallSync = null; let currentVideoEl = null; let currentItemStartedAt = 0; + // Bumped on every renderContent dispatch; a buffered (async warm-play) render captures it at start + // and bails if it changes. MUST be declared here with the other top-level state — the cold-start + // cached-playlist restore calls renderContent (→ renderSeq++) during initial script execution, which + // is long before line ~2400 where the buffered-video code lives. A `let` down there left renderSeq in + // the temporal dead zone on that early path → "can't access 'renderSeq' before initialization". + let renderSeq = 0; // Followers in a video wall must stay silent — N copies of the same audio // slightly out of sync produce a flanged echo across the wall. Only the // leader is allowed to make sound. This helper is the single source of @@ -2405,10 +2411,10 @@ // frame as the wipe's `to`, run the GL wipe, then mount + resume the real