Create 6 panel modules under modules/panels/ plus supporting core infrastructure (state.js, theme.js, ticker.js). Each panel: - Exports init(scene, state, theme) and update(elapsed, delta) - Uses NEXUS.theme for all colors/fonts (no inline hex codes) - Reads from state.js (no direct API calls) - Subscribes to ticker for animation Panel modules: panels/heatmap.js — Commit heatmap floor overlay (DATA-TETHERED) panels/agent-board.js — Agent status holographic board (REAL) panels/dual-brain.js — Dual-brain panel (HONEST-OFFLINE) panels/lora-panel.js — LoRA adapter panel (HONEST-OFFLINE) panels/sovereignty.js — Sovereignty meter arc gauge (REAL manual) panels/earth.js — Holographic Earth, activity-tethered (DATA-TETHERED) Core infrastructure (consumed by panels): core/state.js — shared reactive data bus core/theme.js — NEXUS.theme design system core/ticker.js — single RAF loop + subscribe/unsubscribe API All files pass `node --check`. app.js unchanged. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
47 lines
1.1 KiB
JavaScript
47 lines
1.1 KiB
JavaScript
// modules/core/ticker.js — Global Animation Clock
|
|
// Single requestAnimationFrame loop. All modules subscribe here.
|
|
// No module may call requestAnimationFrame directly.
|
|
|
|
import * as THREE from 'three';
|
|
|
|
const _clock = new THREE.Clock();
|
|
const _subscribers = [];
|
|
|
|
let _running = false;
|
|
let _elapsed = 0;
|
|
|
|
/**
|
|
* Subscribe a callback to the animation loop.
|
|
* @param {(elapsed: number, delta: number) => void} fn
|
|
*/
|
|
export function subscribe(fn) {
|
|
_subscribers.push(fn);
|
|
}
|
|
|
|
/**
|
|
* Unsubscribe a callback from the animation loop.
|
|
* @param {(elapsed: number, delta: number) => void} fn
|
|
*/
|
|
export function unsubscribe(fn) {
|
|
const idx = _subscribers.indexOf(fn);
|
|
if (idx !== -1) _subscribers.splice(idx, 1);
|
|
}
|
|
|
|
/** Start the animation loop. Called once by app.js after all modules are init'd. */
|
|
export function start() {
|
|
if (_running) return;
|
|
_running = true;
|
|
_tick();
|
|
}
|
|
|
|
function _tick() {
|
|
if (!_running) return;
|
|
requestAnimationFrame(_tick);
|
|
const delta = _clock.getDelta();
|
|
_elapsed += delta;
|
|
for (const fn of _subscribers) fn(_elapsed, delta);
|
|
}
|
|
|
|
/** Current elapsed time in seconds (read-only). */
|
|
export function elapsed() { return _elapsed; }
|