[claude] Phase 1: Core Foundation — scene, ticker, theme, state (#410) #463

Merged
claude merged 1 commits from claude/issue-410 into main 2026-03-24 22:16:50 +00:00
4 changed files with 133 additions and 34 deletions

35
app.js
View File

@@ -1,9 +1,12 @@
// === THE NEXUS — Main Entry Point ===
// Thin orchestrator: imports core modules, wires updates, starts the ticker.
import * as THREE from 'three';
import { S, Broadcaster } from './modules/state.js';
import { NEXUS } from './modules/constants.js';
import { scene, camera, renderer, composer } from './modules/scene-setup.js';
import { clock, warpPass } from './modules/warp.js';
import { scene, composer } from './modules/core/scene.js';
import { subscribe, start } from './modules/core/ticker.js';
import { NEXUS } from './modules/core/theme.js'; // eslint-disable-line no-unused-vars
import { state } from './modules/core/state.js'; // eslint-disable-line no-unused-vars
import { S } from './modules/state.js';
import { warpPass } from './modules/warp.js';
import { nostr } from './modules/nostr.js';
import { createNostrPanelTexture } from './modules/nostr-panel.js';
@@ -17,13 +20,9 @@ nostrPanel.position.set(-6, 3.5, -7.5);
nostrPanel.rotation.y = 0.4;
scene.add(nostrPanel);
// === MAIN ANIMATION LOOP ===
function animate() {
requestAnimationFrame(animate);
const delta = clock.getDelta();
const elapsed = clock.elapsedTime;
// Update Nostr UI periodically or on event
// === MAIN UPDATE — subscribed to the single RAF loop in ticker.js ===
subscribe((elapsed, delta) => {
// Update Nostr UI periodically
if (Math.random() > 0.95) {
updateNostrUI();
nostrTexture.needsUpdate = true;
@@ -35,8 +34,14 @@ function animate() {
if (S.energyBeamPulse < 0) S.energyBeamPulse = 0;
}
composer.render();
}
// Warp pass time uniform
if (S.isWarping) {
warpPass.uniforms['time'].value = elapsed;
}
animate();
console.log('Nexus Sovereign Node: NOSTR CONNECTED.');
composer.render();
});
// === START THE SINGLE RAF LOOP ===
start();
console.log('Nexus Sovereign Node: ONLINE.');

12
modules/core/scene.js Normal file
View File

@@ -0,0 +1,12 @@
// modules/core/scene.js — Canonical scene exports
// Provides THREE.Scene, camera, renderer, OrbitControls, and resize handler
// for use by app.js and any module that needs scene primitives.
//
// Implementation detail: the actual objects live in ../scene-setup.js and
// ../controls.js until those modules are absorbed here in a later phase.
export { scene, camera, renderer, raycaster, forwardVector,
ambientLight, overheadLight,
stars, starMaterial, constellationLines,
STAR_BASE_OPACITY, STAR_PEAK_OPACITY, STAR_PULSE_DECAY } from '../scene-setup.js';
export { orbitControls, composer, bokehPass, exitZoom, WARP_DURATION } from '../controls.js';

View File

@@ -1,17 +1,78 @@
// modules/core/theme.js — NEXUS visual constants
// Single source of truth for all colors, fonts, line weights, glow params.
// No module may use inline hex codes or hardcoded font strings.
/** NEXUS — the canonical theme object used by all visual modules */
export const NEXUS = {
/** Numeric hex colors for THREE.js materials */
colors: {
bg: 0x000008,
starCore: 0xffffff,
starDim: 0x8899cc,
constellationLine: 0x334488,
constellationFade: 0x112244,
accent: 0x4488ff,
},
/** All canvas/CSS/string visual constants */
theme: {
// Accent (hex number + CSS string pair)
accent: 0x4488ff,
accentStr: '#4488ff',
// Panel surfaces
panelBg: '#0a1428',
panelText: '#4af0c0',
panelDim: '#7b9bbf',
panelVeryDim: '#3a5070',
panelBorderFaint: '#1a3050',
// Agent status colors (CSS strings for canvas)
agentWorking: '#4af0c0',
agentIdle: '#7b5cff',
agentDormant: '#2a4060',
agentDormantHex: 0x2a4060,
agentDead: '#3a2040',
// Sovereignty meter
sovereignHigh: '#4af0c0',
sovereignHighHex: 0x4af0c0,
sovereignMid: '#ffd700',
sovereignMidHex: 0xffd700,
sovereignLow: '#ff4444',
sovereignLowHex: 0xff4444,
// Holographic earth
earthOcean: '#0a2040',
earthLand: '#1a4020',
earthAtm: '#204070',
earthGlow: '#4488ff',
// LoRA panel
loraActive: '#4af0c0',
loraInactive: '#3a5070',
loraAccent: '#7b5cff',
// Typography
fontMono: 'monospace',
},
};
/** THEME — glass / text presets (kept for SovOS.js and other legacy consumers) */
export const THEME = {
glass: {
color: 0x112244,
opacity: 0.35,
roughness: 0.05,
metalness: 0.1,
color: 0x112244,
opacity: 0.35,
roughness: 0.05,
metalness: 0.1,
transmission: 0.95,
thickness: 0.8,
ior: 1.5
thickness: 0.8,
ior: 1.5,
},
text: {
primary: '#4af0c0',
primary: '#4af0c0',
secondary: '#7b5cff',
white: '#ffffff',
dim: '#a0b8d0'
}
white: '#ffffff',
dim: '#a0b8d0',
},
};

View File

@@ -1,10 +1,31 @@
export class Ticker {
constructor() {
this.callbacks = [];
}
subscribe(fn) { this.callbacks.push(fn); }
tick(delta, elapsed) {
this.callbacks.forEach(fn => fn(delta, elapsed));
}
// modules/core/ticker.js — Global Animation Clock
// Single requestAnimationFrame loop. No module may call RAF directly.
// All modules subscribe their update(elapsed, delta) function here.
import * as THREE from 'three';
const _clock = new THREE.Clock();
const _subs = [];
/** Register an update function: fn(elapsed, delta) */
export function subscribe(fn) {
if (!_subs.includes(fn)) _subs.push(fn);
}
/** Remove a previously registered update function */
export function unsubscribe(fn) {
const i = _subs.indexOf(fn);
if (i !== -1) _subs.splice(i, 1);
}
function _tick() {
requestAnimationFrame(_tick);
const delta = _clock.getDelta();
const elapsed = _clock.getElapsedTime();
for (const fn of _subs) fn(elapsed, delta);
}
/** Start the single RAF loop. Call once from app.js. */
export function start() {
_clock.start();
_tick();
}
export const globalTicker = new Ticker();