generated from Labyricorn/labyricorn-project-template
One commit for the work accumulated in the working tree since Step 6.3, which had never been split into per-step commits: - src/local-surfaces.js + src/surface-url.js (new); src/ui.js, src/validation.js, src/connection.js and public/index.html updated for local-surface hosting and generic surface rendering - tests: local-surfaces (20), scifi-surfaces (24) and postmessage-interop (7) new; connection/museum-gallery/surface-validation suites updated - reference exhibits: shared/contract-core.js defaults to Contract 5.3 (major 5, minor 3, xzbt 5.3); museum-gallery advertises its surface catalog; aquarium/haunted-house/planetarium adapters updated - SciFi-XZBT (Step 6.7A/6.7B): surface-mode.js + surface-bus.js, Observation-surface boot branch, local-change hooks, view.pillars / view.warp-flight targets; fixture byte-identical to G:/.vibe/SciFi-XZBT - SciFi-XZBT contract adapter handshake fix: the inbound bridge filter no longer gates on an exact advisory xzbt value (Contract 5.3 §6.5), only on its presence/type, matching the host's own envelope validation; the adapter now advertises contract minor 3 / version 5.3.0, which it already implemented via the 5.3 surfaces field. Root cause of the five failing postmessage-interop tests (host hello was silently dropped). - docs: architecture 6.4 and 6.7A, reference 6.6 and 6.7; evidence logs; test-fixtures/PROVENANCE.md resync record Test results: NGN 154/154 (was 149/154); postmessage-interop 7/7 (was 2/7); SciFi contract harness 21/21, real-adapter suite 32/32. git diff --check clean for changed files; two pre-existing trailing-whitespace lines remain in test-fixtures/reference-exhibits/scifi/index.html, copied verbatim from the authoritative SciFi source. Step 6.7 live verification (browser Observation, packaged standalone) is still pending and is not claimed here.
87 lines
3.9 KiB
JavaScript
87 lines
3.9 KiB
JavaScript
/*
|
|
* Museum Gallery — Artifact Display boot (non-primary surface).
|
|
*
|
|
* This document deliberately never loads contract-core.js, exhibit.js, or
|
|
* contract-adapter.js. It has no way to construct an Exhibit State Core --
|
|
* it can only attach to the one the Control Room document created, over
|
|
* surface-bus.js. If the Control Room is not open, this degrades to a clear
|
|
* waiting state rather than inventing its own state (Contract 5.3 Section
|
|
* 31.8, Step 6.1 Section 7.2).
|
|
*/
|
|
(function () {
|
|
'use strict';
|
|
|
|
var Shell = window.XZBTShell;
|
|
var el = Shell.el;
|
|
var statusEl = el('status');
|
|
var announcer = new Shell.Announcer({ node: el('announcement'), idleText: 'Waiting for shared state.' });
|
|
var cycleButton = el('cycle-button');
|
|
|
|
var ARTIFACT_ORDER = ['the-orrery', 'star-map', 'meteorite'];
|
|
var ARTIFACT_LABELS = { 'the-orrery': 'The Orrery', 'star-map': 'Star Map', meteorite: 'Meteorite Fragment' };
|
|
|
|
/* This is a read-only MIRROR of authoritative state, not a second copy of
|
|
* exhibit state: it is discarded and re-seeded from a fresh snapshot on
|
|
* every attach, and every write attempt is a mutate() call routed back
|
|
* through the owner's canonical mutation path -- this document never
|
|
* writes to `latest` in response to a user action, only in response to
|
|
* a snapshot or an event relayed from the one Core. */
|
|
var latest = null;
|
|
|
|
function render() {
|
|
if (!latest) return;
|
|
Shell.setText(el('artifact-name'), ARTIFACT_LABELS[latest['artifact.selected']] || latest['artifact.selected']);
|
|
Shell.setText(el('lighting-readout'), Shell.formatPercent(latest['lighting.level']));
|
|
Shell.setText(el('rotation-readout'), latest['rotation.speed'].toFixed(1) + 'x');
|
|
}
|
|
|
|
var link = window.MuseumGallerySurfaceBus.attach({
|
|
timeoutMs: 1500,
|
|
onSnapshot: function (snapshot) {
|
|
latest = snapshot.values;
|
|
Shell.setText(statusEl, 'Attached to Control Room.');
|
|
statusEl.classList.remove('is-waiting');
|
|
cycleButton.disabled = false;
|
|
render();
|
|
},
|
|
onEvent: function (event) {
|
|
if (!latest) return;
|
|
if (event.type === 'state.changed' || event.type === 'selection.changed') {
|
|
latest[event.target] = event.value;
|
|
render();
|
|
} else if (event.type === 'action.executed' && event.target === 'action.spotlight-flash') {
|
|
announcer.flash('Spotlight flashed.');
|
|
}
|
|
},
|
|
onTimeout: function () {
|
|
Shell.setText(statusEl, 'Waiting for the Control Room surface to be open…');
|
|
statusEl.classList.add('is-waiting');
|
|
cycleButton.disabled = true;
|
|
}
|
|
});
|
|
|
|
cycleButton.addEventListener('click', function () {
|
|
if (!latest) return;
|
|
var idx = ARTIFACT_ORDER.indexOf(latest['artifact.selected']);
|
|
var next = ARTIFACT_ORDER[(idx + 1) % ARTIFACT_ORDER.length];
|
|
/* Routed to the owner's canonical mutation path -- this is the required
|
|
* proof that a native interaction on a non-primary surface obeys the
|
|
* same mutation/revision/source/event semantics as primary UI or a
|
|
* contract set/invoke (Contract 5.3 Section 31.7). */
|
|
link.mutate('set', 'artifact.selected', next);
|
|
});
|
|
|
|
/* `beforeunload` is best-effort only -- it does not reliably fire when
|
|
* NGN removes/replaces this frame's iframe element directly (as opposed
|
|
* to a real navigation), so it is not the correctness mechanism here.
|
|
* `__xzbtSurfaceDispose` is a well-known, optional hook: local-surfaces.js
|
|
* calls it synchronously, if present, on this frame's contentWindow right
|
|
* before removing the iframe -- deterministic because it is a direct
|
|
* same-origin call, not a queued postMessage that a doomed frame might
|
|
* never get to process. detach() is idempotent, so it is safe for both
|
|
* this hook and beforeunload to fire (or neither, or either alone). */
|
|
window.__xzbtSurfaceDispose = function () { link.detach(); };
|
|
window.addEventListener('beforeunload', function () { link.detach(); });
|
|
window.__museumGalleryDebug = { link: link };
|
|
})();
|