AbleSet 1.0.0: follow AbleSet's setlist over OSC #17

Merged
michael merged 3 commits from claude/proddash-ableset-module-6c2556 into main 2026-09-27 09:02:27 -04:00
2 changed files with 28 additions and 9 deletions
Showing only changes of commit 9192eed318 - Show all commits
+27 -8
View File
@@ -647,6 +647,27 @@ export default function create({ root, moduleApi }) {
return moduleApi.setStatus('ok', state.setlist.name ? `AbleSet · ${state.setlist.name}` : 'AbleSet'); return moduleApi.setStatus('ok', state.setlist.name ? `AbleSet · ${state.setlist.name}` : 'AbleSet');
} }
function applyState(next) {
if (!next || typeof next !== 'object') return;
takePos(next.pos);
state = next;
reportStatus();
render();
}
/* The page's one stream is shared, so a tile mounted onto it (a gear
change remounts the tile) joins mid-flow and hears nothing until
AbleSet next changes — which, stopped, may be never. Ask for the state. */
let stopped = false;
async function backfill() {
try {
const res = await moduleApi.fetch('/state');
if (!res.ok) return;
const body = await res.json();
if (!stopped) applyState(body.state);
} catch { /* the stream's error handler reports the outage */ }
}
function takePos(p) { function takePos(p) {
if (!p) return; if (!p) return;
pos = p; pos = p;
@@ -666,19 +687,15 @@ export default function create({ root, moduleApi }) {
sectionsView.syncButton(); sectionsView.syncButton();
} }
stream = moduleApi.sse('/stream', { stream = moduleApi.sse('/stream', {
open() {}, open() { backfill(); },
error() { error() {
moduleApi.setStatus('error', 'Lost the ProdDash stream — reconnecting'); moduleApi.setStatus('error', 'Lost the ProdDash stream — reconnecting');
}, },
events: { events: {
state(e) { state(e) {
try { let next;
const next = JSON.parse(e.data); try { next = JSON.parse(e.data); } catch { return; }
takePos(next.pos); applyState(next);
state = next;
} catch { return; }
reportStatus();
render();
}, },
pos(e) { pos(e) {
try { takePos(JSON.parse(e.data)); } catch { return; } try { takePos(JSON.parse(e.data)); } catch { return; }
@@ -687,6 +704,7 @@ export default function create({ root, moduleApi }) {
}, },
}, },
}); });
backfill();
resizeObserver = new ResizeObserver(() => { resizeObserver = new ResizeObserver(() => {
fitAll(); fitAll();
if (view === 'sections' && state) sectionsView.place(); if (view === 'sections' && state) sectionsView.place();
@@ -696,6 +714,7 @@ export default function create({ root, moduleApi }) {
tickTimer = setInterval(() => { if (state && !moving()) impl.frame(); }, 1000); tickTimer = setInterval(() => { if (state && !moving()) impl.frame(); }, 1000);
}, },
stop() { stop() {
stopped = true;
if (stream) stream.close(); if (stream) stream.close();
stream = null; stream = null;
cancelAnimationFrame(raf); cancelAnimationFrame(raf);
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"id": "ableset", "id": "ableset",
"name": "AbleSet", "name": "AbleSet",
"version": "1.0.0", "version": "1.0.1",
"proddash": ">=1.5.0", "proddash": ">=1.5.0",
"description": "The song AbleSet is playing — tempo, time left, sections, setlist and status", "description": "The song AbleSet is playing — tempo, time left, sections, setlist and status",
"permissions": [ "permissions": [