Files
the-nexus/modules/core/state.js
Alexander Whitestone 4f207605ce
All checks were successful
CI / validate (pull_request) Successful in 6s
CI / auto-merge (pull_request) Successful in 0s
feat: extract core foundation modules — scene, ticker, theme, state (#420)
Phase 1 of app.js modularization (Refs #409, Fixes #420).

Adds four modules under modules/core/:

- theme.js: exports NEXUS with full NEXUS.theme (colors, fonts, lineWeights,
  glowParams, opacity). NEXUS.colors preserved as backwards-compat alias.

- state.js: exports zoneIntensity (shared mutable object), state (agentCount,
  blockHeight, starPulseIntensity, weather), and totalActivity().

- scene.js: creates and exports scene, camera, renderer, orbitControls,
  raycaster, lighting. Registers camera+renderer resize handler. Intentionally
  does not append renderer.domElement — app.js controls DOM insertion order.

- ticker.js: single requestAnimationFrame loop. Exports subscribe(), unsubscribe(),
  start(), stop(). app.js registers animate() via tickerSubscribe and calls
  tickerStart() instead of RAF directly.

app.js changes:
- Imports NEXUS from theme.js (removes inline NEXUS definition)
- Imports scene/camera/renderer/orbitControls/raycaster from scene.js
  (removes duplicate creation blocks)
- Imports zoneIntensity/state/totalActivity from state.js (removes local defs)
- animate() is now a ticker subscriber function, not a recursive RAF caller
- _activeAgentCount → state.agentCount
- _starPulseIntensity → state.starPulseIntensity
- lastKnownBlockHeight → state.blockHeight
- EffectComposer resize listener preserved separately in app.js

node --check app.js passes. No visual regressions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-24 14:24:00 -04:00

50 lines
1.6 KiB
JavaScript

/**
* modules/core/state.js — Shared reactive data bus.
*
* Data modules write here; visual modules read from here.
* No fetch() calls live in this file — state is populated by data/ modules.
*
* Design:
* - zoneIntensity is exported directly so callers can mutate it by reference.
* - state holds scalar/object fields; mutate properties directly (state.agentCount = N).
* - totalActivity() is a derived getter over zoneIntensity.
*/
/**
* Per-zone commit activity intensity values (0..1).
* Keys are zone names matching HEATMAP_ZONES entries in app.js.
* Populated at init time once HEATMAP_ZONES is available.
* @type {Record<string, number>}
*/
export const zoneIntensity = {};
/**
* Global shared state — all fields are mutable by their respective data sources.
*/
export const state = {
/** Current Bitcoin block height. null until first fetch. @type {number|null} */
blockHeight: null,
/** Weather payload from Open-Meteo. null until first fetch. @type {object|null} */
weather: null,
/** Number of agents currently in 'working' status. Updated by agent status fetch. */
agentCount: 0,
/**
* Star pulse intensity driven by new Bitcoin block events.
* 0 = base brightness, 1 = peak. Decays each frame.
*/
starPulseIntensity: 0,
};
/**
* Returns mean activity level [0..1] across all agent zones.
* Used by matrix rain, energy beam, holographic earth, etc.
* @returns {number}
*/
export function totalActivity() {
const vals = Object.values(zoneIntensity);
return vals.reduce((s, v) => s + v, 0) / Math.max(vals.length, 1);
}