generated from Labyricorn/labyricorn-project-template
Step 6.3 Complete — Generic Contract 5.3 Surface Discovery
- src/validation.js: add validateSurfaceCatalog() implementing Contract 5.3 §§31.2-31.5 normative validation order (validate individuals -> discard invalids -> evaluate primary invariant against working set) - src/host.js: emit xzbt:5.3 advisory (§6.5); store contractMinor; add surfaces=[] to resetView(); parse surfaces in refresh(true) - src/ui.js: add buildSurfaces() for generic descriptor-driven surface display; integrate renderedSurfaces tracking into render() - public/index.html: subtitle updated to Contract 5.2/5.3; add surfaces-section with surfaces div - public/style.css: add .surface, .surface-badge, .surface-meta styles - tests/surface-validation.test.js: 45 new tests covering all Part 8 malformed surface cases, lifecycle, registry refresh, 5.2 regression - docs/reference/XZBT-NGN-Step6.3-Surface-Discovery-Verification.md Test results: 88/88 pass (43 pre-existing + 45 new) Genericity scan: zero matches in src/, public/, server/ git diff --check: clean
This commit is contained in:
@@ -8,6 +8,7 @@ const byId = id => document.getElementById(id);
|
||||
const json = value => JSON.stringify(value, null, 2);
|
||||
const rows = new Map();
|
||||
let renderedCatalog = null;
|
||||
let renderedSurfaces = null;
|
||||
const connection = new ExhibitConnection({ host, base: location.href, transport: postMessageTransport, changed: render,
|
||||
createFrame() {
|
||||
const frame = document.createElement('iframe'); frame.title = 'Connected exhibit';
|
||||
@@ -100,6 +101,29 @@ function buildCatalog() {
|
||||
rows.set(target.id, row);
|
||||
}
|
||||
}
|
||||
function buildSurfaces() {
|
||||
const container = byId('surfaces'); container.replaceChildren();
|
||||
byId('surface-count').textContent = host.surfaces.length ? `(${host.surfaces.length})` : '';
|
||||
if (!host.surfaces.length) {
|
||||
element('p', host.sessionId ? 'No presentation surfaces advertised.' : 'No presentation surfaces advertised.', container);
|
||||
return;
|
||||
}
|
||||
for (const surface of host.surfaces) {
|
||||
const card = element('article', undefined, container); card.className = 'surface';
|
||||
element('h3', surface.label, card);
|
||||
element('code', surface.id, card);
|
||||
const badges = element('div', undefined, card);
|
||||
if (surface.primary) { const b = element('span', 'Primary', badges); b.className = 'surface-badge primary'; }
|
||||
if (surface.role) { const b = element('span', `Role: ${surface.role}`, badges); b.className = 'surface-badge'; }
|
||||
if (surface.category) { const b = element('span', `Category: ${surface.category}`, badges); b.className = 'surface-badge'; }
|
||||
const meta = [];
|
||||
if (surface.description) meta.push(surface.description);
|
||||
if (surface.url) meta.push(`URL: ${surface.url}`);
|
||||
if (surface.aspectRatio) meta.push(`Aspect ratio: ${surface.aspectRatio}`);
|
||||
if (surface.requires?.length) meta.push(`Requires: ${surface.requires.join(', ')}`);
|
||||
if (meta.length) { const p = element('p', meta.join(' · '), card); p.className = 'surface-meta'; }
|
||||
}
|
||||
}
|
||||
function render() {
|
||||
byId('status').textContent = connection.loading ? 'Loading exhibit…' : `${host.status} · ${host.sync}`;
|
||||
byId('status').dataset.state = host.status;
|
||||
@@ -123,6 +147,7 @@ function render() {
|
||||
byId('reconnect').disabled = !connection.url || connection.loading || host.status === 'negotiating';
|
||||
byId('disconnect').disabled = !connection.frame;
|
||||
if (host.catalog !== renderedCatalog) { renderedCatalog = host.catalog; buildCatalog(); }
|
||||
if (host.surfaces !== renderedSurfaces) { renderedSurfaces = host.surfaces; buildSurfaces(); }
|
||||
for (const row of rows.values()) {
|
||||
row.current.textContent = host.values.has(row.target.id) ? `Reported value: ${json(host.values.get(row.target.id))}` : 'No persistent value reported.';
|
||||
if (!row.initialized && row.valueEditor && host.values.has(row.target.id)) {
|
||||
|
||||
Reference in New Issue
Block a user