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.
218 lines
7.0 KiB
JavaScript
218 lines
7.0 KiB
JavaScript
/*
|
|
* Aquarium — XZBT Exhibit Contract 5.3 adapter.
|
|
*
|
|
* This file is the only place in the exhibit that knows the contract exists.
|
|
* It does three things and nothing else:
|
|
*
|
|
* 1. declares the public target catalog against the exhibit's real model
|
|
* 2. binds each target to a real exhibit service (setter / reader / action)
|
|
* 3. routes the native UI through the canonical mutation path
|
|
*
|
|
* It holds no state of its own. Every value it reports is read live from the
|
|
* exhibit, so there is no shadow copy that can drift (Authoring Guide §W).
|
|
*/
|
|
(function () {
|
|
'use strict';
|
|
|
|
var Core = window.XZBTContractCore;
|
|
|
|
var IDENTITY = {
|
|
product: 'Aquarium',
|
|
version: '1.0.0',
|
|
build: 'reference-exhibit'
|
|
};
|
|
|
|
/* ------------------------------------------------------------------ *
|
|
* Public target catalog.
|
|
*
|
|
* IDs are canonical and domain-shaped. They are not DOM ids, CSS
|
|
* selectors, or widget names (Authoring Guide §W).
|
|
* ------------------------------------------------------------------ */
|
|
|
|
var DESCRIPTORS = [
|
|
{
|
|
id: 'tank.species',
|
|
kind: 'selection',
|
|
label: 'Species',
|
|
description: 'The species currently stocked in the tank.',
|
|
options: [
|
|
{ value: 'clownfish', label: 'Clownfish' },
|
|
{ value: 'blue-tang', label: 'Blue Tang' },
|
|
{ value: 'neon-tetra', label: 'Neon Tetra' },
|
|
{ value: 'angelfish', label: 'Angelfish' },
|
|
{ value: 'guppy', label: 'Guppy' }
|
|
],
|
|
readable: true, writable: true, restorable: true,
|
|
category: 'tank', requires: []
|
|
},
|
|
{
|
|
id: 'tank.population',
|
|
kind: 'range',
|
|
label: 'Population',
|
|
description: 'Number of fish in the tank.',
|
|
min: 1, max: 40, step: 1, unit: 'fish',
|
|
readable: true, writable: true, restorable: true,
|
|
category: 'tank', requires: []
|
|
},
|
|
{
|
|
id: 'tank.lighting',
|
|
kind: 'selection',
|
|
label: 'Lighting',
|
|
description: 'Lighting programme for the tank.',
|
|
options: [
|
|
{ value: 'daylight', label: 'Daylight' },
|
|
{ value: 'twilight', label: 'Twilight' },
|
|
{ value: 'moonlight', label: 'Moonlight' },
|
|
{ value: 'blackwater', label: 'Blackwater' }
|
|
],
|
|
readable: true, writable: true, restorable: true,
|
|
category: 'tank', requires: []
|
|
},
|
|
{
|
|
id: 'tank.current',
|
|
kind: 'range',
|
|
label: 'Water current',
|
|
description: 'Strength of the circulation pump, 0 (still) to 1 (strong).',
|
|
min: 0, max: 1, step: 0.01,
|
|
readable: true, writable: true, restorable: true,
|
|
category: 'tank', requires: []
|
|
},
|
|
{
|
|
id: 'tank.temperature',
|
|
kind: 'range',
|
|
label: 'Temperature',
|
|
description: 'Heater setting, 0 (18 C) to 1 (28 C).',
|
|
min: 0, max: 1, step: 0.01,
|
|
readable: true, writable: true, restorable: true,
|
|
category: 'tank', requires: []
|
|
},
|
|
{
|
|
id: 'tank.paused',
|
|
kind: 'state',
|
|
valueType: 'boolean',
|
|
label: 'Paused',
|
|
description: 'Whether the simulation is frozen.',
|
|
readable: true, writable: true, restorable: true,
|
|
category: 'tank', requires: []
|
|
},
|
|
{
|
|
id: 'action.feed',
|
|
kind: 'impulse',
|
|
label: 'Feed',
|
|
description: 'Scatter food across the surface.',
|
|
readable: false, writable: false, restorable: false,
|
|
category: 'action', requires: []
|
|
},
|
|
{
|
|
id: 'action.startle',
|
|
kind: 'impulse',
|
|
label: 'Startle',
|
|
description: 'Send a pressure wave through the tank.',
|
|
readable: false, writable: false, restorable: false,
|
|
category: 'action', requires: []
|
|
},
|
|
{
|
|
id: 'action.clean-glass',
|
|
kind: 'impulse',
|
|
label: 'Clean glass',
|
|
description: 'Run the glass-cleaning cycle.',
|
|
readable: false, writable: false, restorable: false,
|
|
category: 'action', requires: []
|
|
},
|
|
{
|
|
id: 'telemetry.fish-count',
|
|
kind: 'state',
|
|
valueType: 'number',
|
|
label: 'Fish count',
|
|
description: 'Live number of fish currently rendered.',
|
|
readable: true, writable: false, restorable: false,
|
|
category: 'telemetry', requires: []
|
|
},
|
|
{
|
|
id: 'telemetry.food-level',
|
|
kind: 'state',
|
|
valueType: 'number',
|
|
label: 'Food level',
|
|
description: 'Remaining food in the water column, 0 to 1.',
|
|
readable: true, writable: false, restorable: false,
|
|
category: 'telemetry', requires: []
|
|
},
|
|
{
|
|
id: 'telemetry.water-temp',
|
|
kind: 'state',
|
|
valueType: 'number',
|
|
unit: 'C',
|
|
label: 'Water temperature',
|
|
description: 'Actual water temperature in degrees Celsius.',
|
|
readable: true, writable: false, restorable: false,
|
|
category: 'telemetry', requires: []
|
|
}
|
|
];
|
|
|
|
/* ------------------------------------------------------------------ *
|
|
* Wiring
|
|
* ------------------------------------------------------------------ */
|
|
|
|
function createAquariumContract(exhibit, ui) {
|
|
var catalog = new Core.Catalog(DESCRIPTORS);
|
|
|
|
/* The exhibit has no capability that can fail: it is pure canvas and
|
|
* needs no device, no network, and no permission. Reporting `ready`
|
|
* once is honest; inventing loading/error transitions would not be
|
|
* (Authoring Guide §M). */
|
|
var capabilities = new Core.CapabilityRegistry();
|
|
capabilities.declare('render', 'ready');
|
|
|
|
var setters = {
|
|
'tank.species': function (v) { return exhibit.setSpecies(v); },
|
|
'tank.population': function (v) { return exhibit.setPopulation(v); },
|
|
'tank.lighting': function (v) { return exhibit.setLighting(v); },
|
|
'tank.current': function (v) { return exhibit.setCurrent(v); },
|
|
'tank.temperature': function (v) { return exhibit.setTemperature(v); },
|
|
'tank.paused': function (v) { return exhibit.setPaused(v); }
|
|
};
|
|
|
|
var readers = {};
|
|
var ids = catalog.ids();
|
|
for (var i = 0; i < ids.length; i++) {
|
|
(function (id) {
|
|
readers[id] = function () { return exhibit.read(id); };
|
|
})(ids[i]);
|
|
}
|
|
|
|
var actions = {
|
|
'action.feed': function () { return exhibit.feed(); },
|
|
'action.startle': function () { return exhibit.startle(); },
|
|
'action.clean-glass': function () { return exhibit.cleanGlass(); }
|
|
};
|
|
|
|
var core = new Core.ContractCore({
|
|
identity: IDENTITY,
|
|
catalog: catalog,
|
|
capabilities: capabilities,
|
|
setters: setters,
|
|
readers: readers,
|
|
actions: actions
|
|
});
|
|
|
|
/* Route the native UI through the canonical path. From here on, a
|
|
* slider drag and a host `set` are the same transaction, and both are
|
|
* reported with the same source semantics (Authoring Guide §H). */
|
|
ui.dispatchOverride = function (targetId, value) {
|
|
core.applyMutation(targetId, value, 'ui');
|
|
ui.sync();
|
|
};
|
|
ui.invokeOverride = function (targetId) {
|
|
core.invokeAction(targetId, {}, 'ui');
|
|
ui.sync();
|
|
};
|
|
|
|
/* Keep the controls honest when the exhibit changes for any reason. */
|
|
exhibit.onChange = function () { ui.sync(); };
|
|
|
|
return core;
|
|
}
|
|
|
|
window.AquariumContract = { create: createAquariumContract, IDENTITY: IDENTITY };
|
|
})();
|