/** * XZBT Control Bus * * XZBTControlBus is a small registry of named, settable console targets * (ranges, toggles and triggers). Anything that wants to drive the console * from outside the DOM — contract adapter, hotkeys, future transports — * talks to the bus rather than reaching into app.js internals. * * MIDI NOTE: Web MIDI control belonged to standalone SciFi-XZBT until * Step 3.5. External MIDI integration will be handled by XZBT-NGN, which * will send canonical contract commands (set / invoke) to this exhibit. * The control bus itself is transport-agnostic and is intentionally kept here. */ class XZBTControlBus { constructor() { this.targets = new Map(); this.listeners = new Set(); } register(id, spec) { this.targets.set(id, { id, ...spec }); } set(id, value, source = 'external') { const t = this.targets.get(id); if (!t) return false; let v = value; if (t.type === 'range') { v = Math.max(t.min, Math.min(t.max, Number(value))); if (t.step) v = Math.round(v / t.step) * t.step; } if (t.apply) t.apply(v, source); this.listeners.forEach(fn => fn({ id, value: v, source, target: t })); return true; } trigger(id, source = 'external') { const t = this.targets.get(id); if (!t) return false; if (t.apply) t.apply(true, source); this.listeners.forEach(fn => fn({ id, value: true, source, target: t })); return true; } list() { return [...this.targets.values()]; } } window.XZBTControlBus = XZBTControlBus;