feat(web): the Display tab is an ES-module page, with a page-visibility service (stage 4) (#771)

* feat(web): the Display tab is an ES-module page, with a page-visibility service (stage 4)

- core/visibility.js: each page gets ctx.visibility (whileVisible, every,
  isVisible). Work registered there runs only while the page's tab is
  active and the browser tab visible, and ends when the page is swapped
  out. It reads the active tab from window.LEDVisibility, so it agrees
  with the classic partials. The registry gained a mountContext option for
  per-mount services.
- pages/display.js replaces display.html's two inline scripts. The 5 s
  sync status poll runs through ctx.visibility.every; the status and
  scroll-speed hint requests go through ctx.api with ctx.signal, as does
  the Vegas order widget's plugin-list request. The Advanced toggle is a
  delegated data-action; window.updateSyncUI is a deprecated alias.
- New DOM suites test_visibility_service.js and test_display_page.js;
  test_display_partial_ids.js imports the module.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

* refactor(web): no computed keys in the Display page's readout and destroy

Codacy's object-injection rule flagged v[id] and ctx.state[name].

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Chuck
2026-10-05 09:53:43 -04:00
committed by GitHub
co-authored by Claude Opus 5.5
parent 6fb2dc3595
commit c20c0beac2
15 changed files with 1223 additions and 378 deletions
+40
View File
@@ -242,6 +242,46 @@ function recorder(log) {
ok('has()', reg.has('dup') && !reg.has('nope'));
}
console.log('\n10. mountContext adds per-mount fields, after the shared ones');
{
const doc = new Doc();
const panel = doc.body.appendChild(new El('div', { id: 'panel' }));
panel.appendChild(new El('div', { id: 'a', [PAGE_ATTRIBUTE]: 'demo' }));
const seen = [];
const made = [];
const reg = createRegistry({
document: doc, context: { api: 'shared' }, logger: quiet,
mountContext(ctx) {
made.push([ctx.name, ctx.root.getAttribute('id'), !!ctx.signal, ctx.api]);
return { bound: { root: ctx.root, signal: ctx.signal } };
},
});
reg.register('demo', { init(root, ctx) { seen.push(ctx); } });
await reg.start();
await tick();
ok('mountContext sees the mount\'s name, root, signal and the shared services',
made.length === 1 && made[0].join() === 'demo,a,true,shared', made);
ok('...and its fields reach init()', seen.length === 1 && seen[0].bound && seen[0].bound.root === seen[0].root, seen.length);
fire(panel, 'htmx:beforeSwap', { target: panel, shouldSwap: true });
panel.replaceChildren(new El('div', { id: 'b', [PAGE_ATTRIBUTE]: 'demo' }));
fire(panel, 'htmx:afterSwap', { target: panel });
await tick();
ok('called again for each new mount, with that mount\'s signal',
made.length === 2 && seen.length === 2 && !!seen[1].bound && seen[1].bound.signal === seen[1].signal
&& seen[0].signal.aborted, made);
const errors = [];
const doc2 = new Doc();
doc2.body.appendChild(new El('div', { id: 'c', [PAGE_ATTRIBUTE]: 'demo' }));
const reg2 = createRegistry({ document: doc2, logger: { error: (...a) => errors.push(a.join(' ')) },
mountContext() { throw new Error('boom'); } });
let started = 0;
reg2.register('demo', { init() { started++; } });
await reg2.start();
await tick();
ok('a throwing mountContext is logged and the page still starts', started === 1 && errors.length === 1, errors);
}
console.log(`\n${pass} passed, ${fail} failed`);
process.exit(fail ? 1 : 0);
})().catch(e => { console.error(e); process.exit(1); });