From e0bdd3b65ca3efe400d89faedc94f327a63e20d2 Mon Sep 17 00:00:00 2001 From: Claude Date: Thu, 30 Jul 2026 20:17:22 -0500 Subject: [PATCH] Web player: re-render a widget whose content was edited MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The signature fix was necessary but not sufficient, and only a browser showed it. The update arrived and was applied — the console logged "Playlist changed, updating" and playlist[0].widget_rev held the NEW revision — but the iframe on screen still carried the old one. Two guards were swallowing it. Continuity keeps a surviving item playing and deliberately does not re-render ("Just retarget the index pointer - no re-render, no interrupt"), and identity is content/widget ID, which does not change when a widget is EDITED. So the edited widget counted as surviving. And the fallback that would eventually notice does not apply either: a solo widget is deliberately never re-rendered on a timer, because that would reset a directory board's scroll. Between them the new revision sat in the playlist, unused, indefinitely. Now a surviving WIDGET whose rev changed is re-rendered through the buffered swap — which builds the new iframe hidden and reveals it on load, so it is flash-free by design and this costs nothing visually. Non-widget items and unedited widgets are untouched, so the continuity behaviour that guard exists for is intact. Verified in headless Chrome driving the real player: paired, widget assigned, then edited with no page reload and no restart. rev 1785460578 -> 1785460589 on the live iframe. Also caught here: my first attempt called renderItem(), which does not exist — the console.log fired and the exception ate the rest of the handler, which looked exactly like the fix not working. The function is renderContent(item). Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Uaeo9MvzKoyXuN6ZsbhtkL --- server/player/index.html | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/server/player/index.html b/server/player/index.html index a107dbc..f855c62 100644 --- a/server/player/index.html +++ b/server/player/index.html @@ -1977,6 +1977,18 @@ if (stillThereIdx !== -1) { currentIndex = stillThereIdx; isPlaying = true; + // ...unless it is a WIDGET whose content was edited. Identity is content/widget id, and + // editing a widget does not change its id, so an edited widget "survives" and the + // re-render is skipped — which is exactly why an edit never reached the screen. The new + // revision sat in the playlist unused, because a solo widget deliberately never + // re-renders on a timer (that would reset a directory board's scroll). Re-render through + // the buffered swap, which is flash-free by design, so this costs nothing visually. + const oldItem = oldPlaylist[oldAnchorIdx]; + const newItem = playlist[stillThereIdx]; + if (newItem && newItem.widget_id && oldItem && (oldItem.widget_rev || 0) !== (newItem.widget_rev || 0)) { + console.log('Widget edited - re-rendering in place'); + renderContent(newItem); + } return; } }