Files
XZBT-NGN/test-fixtures/reference-exhibits/museum-gallery/control.boot.js
T
Labyricorn 745912e451 Steps 6.4-6.7B — Local surfaces, reference-exhibit validation, SciFi Observation surface
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.
2026-09-14 19:45:27 -07:00

107 lines
4.5 KiB
JavaScript

/*
* Museum Gallery — Control Room boot.
*
* This is the ONLY document in this exhibit that constructs an Exhibit
* State Core. Every other surface (artifact.html, info-wall.html) attaches
* to the Core this document creates, over the exhibit-internal bus in
* surface-bus.js -- see README.md.
*
* This document remains fully standalone: nothing below requires any other
* surface to be open, and nothing below requires XZBT-NGN. The optional
* HostTransport at the bottom lets an NGN attach later, exactly like every
* other reference exhibit -- it changes nothing about ordinary operation
* when no host ever connects.
*/
(function () {
'use strict';
var Shell = window.XZBTShell;
var el = Shell.el;
var gallery = new window.MuseumGalleryExhibit.Gallery();
var core = window.MuseumGalleryContract.create(gallery);
var announcer = new Shell.Announcer({ node: el('announcement'), idleText: 'Control Room standing by.' });
var artifactSelect = el('artifact-select');
var artifactDescriptor = core.catalog.get('artifact.selected');
artifactDescriptor.options.forEach(function (opt) {
var option = document.createElement('option');
option.value = opt.value;
option.textContent = opt.label;
artifactSelect.appendChild(option);
});
var lightingRange = el('lighting-range');
var rotationRange = el('rotation-range');
var labelsButton = el('labels-button');
var subscriberCountEl = el('subscriber-count');
function sync() {
var values = core.stateSnapshot().values;
Shell.setText(el('artifact-name'), window.MuseumGalleryExhibit.ARTIFACT_LABELS[values['artifact.selected']]);
artifactSelect.value = values['artifact.selected'];
Shell.setText(el('lighting-readout'), Shell.formatPercent(values['lighting.level']));
Shell.setText(el('lighting-panel-readout'), Shell.formatPercent(values['lighting.level']));
lightingRange.value = values['lighting.level'];
Shell.setText(el('rotation-readout'), values['rotation.speed'].toFixed(1) + 'x');
Shell.setText(el('rotation-panel-readout'), values['rotation.speed'].toFixed(1) + 'x');
rotationRange.value = values['rotation.speed'];
Shell.setText(el('labels-readout'), values['labels.enabled'] ? 'ON' : 'OFF');
labelsButton.setAttribute('aria-pressed', values['labels.enabled'] ? 'true' : 'false');
}
sync();
/* Every native control here calls the SAME core.applyMutation/invokeAction
* chokepoint a contract set/invoke (from NGN) or a bus-relayed mutation
* (from a non-primary surface) would call. There is exactly one path. */
artifactSelect.addEventListener('change', function () {
core.applyMutation('artifact.selected', artifactSelect.value, 'ui');
});
lightingRange.addEventListener('input', function () {
core.applyMutation('lighting.level', parseFloat(lightingRange.value), 'ui');
});
rotationRange.addEventListener('input', function () {
core.applyMutation('rotation.speed', parseFloat(rotationRange.value), 'ui');
});
labelsButton.addEventListener('click', function () {
var current = core.stateSnapshot().values['labels.enabled'];
core.applyMutation('labels.enabled', !current, 'ui');
});
el('flash-button').addEventListener('click', function () {
core.invokeAction('action.spotlight-flash', {}, 'ui');
announcer.flash('Spotlight flashed.');
});
/* The bus owner forwards every core event (however it originated -- this
* document's own controls, or a mutate request relayed from another
* surface) to attached surfaces, and answers their attach requests with a
* fresh snapshot read straight from this Core -- never a second
* computation. */
function updateSubscriberCount(count) {
Shell.setText(subscriberCountEl, String(count));
}
var bus = window.MuseumGallerySurfaceBus.createOwner(core, {
onConnectionChange: updateSubscriberCount
});
updateSubscriberCount(bus.attachedCount());
var priorOnEvent = core.onEvent;
core.onEvent = function (event) {
if (typeof priorOnEvent === 'function') priorOnEvent(event);
sync();
};
/* Optional NGN attachment -- same optional transport every reference
* exhibit includes. Museum Gallery Phase 6.2 does not exercise this path;
* see the verification document's known-limitations section. */
var transport = new window.XZBTHostTransport({
core: core,
onMessage: function () { el('contract-status').textContent = 'host attached'; }
});
window.__museumGalleryDebug = { core: core, gallery: gallery, bus: bus, transport: transport, sync: sync };
})();