[manus] Sovereign Heartbeat — Real-time State Broadcaster (#39) (#40)
Some checks failed
Deploy Nexus / deploy (push) Has been cancelled
Some checks failed
Deploy Nexus / deploy (push) Has been cancelled
Co-authored-by: manus <manus@noreply.143.198.27.163> Co-committed-by: manus <manus@noreply.143.198.27.163>
This commit was merged in pull request #40.
This commit is contained in:
173
app.js
173
app.js
@@ -22,6 +22,68 @@ const NEXUS = {
|
||||
}
|
||||
};
|
||||
|
||||
// ═══ SOVEREIGN STATE (The Heartbeat) ═══
|
||||
const STATE = {
|
||||
metrics: {
|
||||
fps: 0,
|
||||
drawCalls: 0,
|
||||
triangles: 0,
|
||||
uptime: 0,
|
||||
activeLoops: 5,
|
||||
cpu: 12,
|
||||
mem: 4.2
|
||||
},
|
||||
agents: {
|
||||
timmy: 'RUNNING',
|
||||
kimi: 'STANDBY',
|
||||
claude: 'ACTIVE',
|
||||
perplexity: 'STANDBY'
|
||||
},
|
||||
thoughts: [
|
||||
'ANALYZING WORLD...',
|
||||
'SYNCING MEMORY...',
|
||||
'WAITING FOR INPUT',
|
||||
'SOUL ON BITCOIN'
|
||||
],
|
||||
lastUpdate: 0,
|
||||
pulseRate: 1.0 // Hz
|
||||
};
|
||||
|
||||
// ═══ STATE BROADCASTER ═══
|
||||
const Broadcaster = {
|
||||
listeners: [],
|
||||
subscribe(fn) { this.listeners.push(fn); },
|
||||
broadcast() { this.listeners.forEach(fn => fn(STATE)); }
|
||||
};
|
||||
|
||||
// ═══ STATE UPDATER ═══
|
||||
function updateSovereignState(elapsed) {
|
||||
STATE.metrics.uptime = elapsed;
|
||||
|
||||
// Simulate some jitter/activity
|
||||
if (Math.random() > 0.95) {
|
||||
STATE.metrics.cpu = 10 + Math.floor(Math.random() * 15);
|
||||
STATE.metrics.activeLoops = 4 + Math.floor(Math.random() * 3);
|
||||
|
||||
// Random thought shift
|
||||
if (Math.random() > 0.7) {
|
||||
const newThoughts = [
|
||||
'DECENTRALIZING COGNITION',
|
||||
'ZAPPING CONTRIBUTORS',
|
||||
'MAPPING SPATIAL LOOPS',
|
||||
'REFINING LORA WEIGHTS',
|
||||
'OBSERVING ALEXANDER',
|
||||
'NEXUS INTEGRITY: 100%',
|
||||
'HERMES LINK STABLE'
|
||||
];
|
||||
STATE.thoughts.shift();
|
||||
STATE.thoughts.push(newThoughts[Math.floor(Math.random() * newThoughts.length)]);
|
||||
}
|
||||
|
||||
Broadcaster.broadcast();
|
||||
}
|
||||
}
|
||||
|
||||
// ═══ STATE ═══
|
||||
let camera, scene, renderer, composer;
|
||||
let clock, playerPos, playerRot;
|
||||
@@ -346,22 +408,23 @@ function createBatcaveTerminal() {
|
||||
const terminalGroup = new THREE.Group();
|
||||
terminalGroup.position.set(0, 0, -8);
|
||||
|
||||
const panelData = [
|
||||
{ title: 'NEXUS COMMAND', color: NEXUS.colors.primary, rot: -0.4, x: -6, y: 3, lines: ['> STATUS: NOMINAL', '> UPTIME: 142.4h', '> HARNESS: STABLE', '> MODE: SOVEREIGN'] },
|
||||
{ title: 'DEV QUEUE', color: NEXUS.colors.gold, rot: -0.2, x: -3, y: 3, lines: ['> ISSUE #4: CORE', '> ISSUE #5: PORTAL', '> ISSUE #6: TERMINAL', '> ISSUE #7: TIMMY'] },
|
||||
{ title: 'METRICS', color: NEXUS.colors.secondary, rot: 0, x: 0, y: 3, lines: ['> CPU: 12% [||....]', '> MEM: 4.2GB', '> COMMITS: 842', '> ACTIVE LOOPS: 5'] },
|
||||
{ title: 'THOUGHTS', color: NEXUS.colors.primary, rot: 0.2, x: 3, y: 3, lines: ['> ANALYZING WORLD...', '> SYNCING MEMORY...', '> WAITING FOR INPUT', '> SOUL ON BITCOIN'] },
|
||||
{ title: 'AGENT STATUS', color: NEXUS.colors.gold, rot: 0.4, x: 6, y: 3, lines: ['> TIMMY: ● RUNNING', '> KIMI: ○ STANDBY', '> CLAUDE: ● ACTIVE', '> PERPLEXITY: ○'] },
|
||||
const panels = [
|
||||
{ id: 'command', title: 'NEXUS COMMAND', color: NEXUS.colors.primary, rot: -0.4, x: -6, y: 3 },
|
||||
{ id: 'queue', title: 'DEV QUEUE', color: NEXUS.colors.gold, rot: -0.2, x: -3, y: 3 },
|
||||
{ id: 'metrics', title: 'METRICS', color: NEXUS.colors.secondary, rot: 0, x: 0, y: 3 },
|
||||
{ id: 'thoughts',title: 'THOUGHTS', color: NEXUS.colors.primary, rot: 0.2, x: 3, y: 3 },
|
||||
{ id: 'agents', title: 'AGENT STATUS', color: NEXUS.colors.gold, rot: 0.4, x: 6, y: 3 },
|
||||
];
|
||||
|
||||
panelData.forEach(data => {
|
||||
createTerminalPanel(terminalGroup, data.x, data.y, data.rot, data.title, data.color, data.lines);
|
||||
panels.forEach(data => {
|
||||
createTerminalPanel(terminalGroup, data);
|
||||
});
|
||||
|
||||
scene.add(terminalGroup);
|
||||
}
|
||||
|
||||
function createTerminalPanel(parent, x, y, rot, title, color, lines) {
|
||||
function createTerminalPanel(parent, data) {
|
||||
const { x, y, rot, title, color, id } = data;
|
||||
const w = 2.8, h = 3.5;
|
||||
const group = new THREE.Group();
|
||||
group.position.set(x, y, 0);
|
||||
@@ -392,25 +455,6 @@ function createTerminalPanel(parent, x, y, rot, title, color, lines) {
|
||||
textCanvas.height = 640;
|
||||
const ctx = textCanvas.getContext('2d');
|
||||
|
||||
// Header
|
||||
ctx.fillStyle = '#' + new THREE.Color(color).getHexString();
|
||||
ctx.font = 'bold 32px "Orbitron", sans-serif';
|
||||
ctx.fillText(title, 20, 45);
|
||||
ctx.fillRect(20, 55, 472, 2);
|
||||
|
||||
// Lines
|
||||
ctx.font = '20px "JetBrains Mono", monospace';
|
||||
ctx.fillStyle = '#a0b8d0';
|
||||
lines.forEach((line, i) => {
|
||||
// Color active indicators
|
||||
let fillColor = '#a0b8d0';
|
||||
if (line.includes('● RUNNING') || line.includes('● ACTIVE')) fillColor = '#4af0c0';
|
||||
else if (line.includes('○ STANDBY')) fillColor = '#5a6a8a';
|
||||
else if (line.includes('NOMINAL')) fillColor = '#4af0c0';
|
||||
ctx.fillStyle = fillColor;
|
||||
ctx.fillText(line, 20, 100 + i * 40);
|
||||
});
|
||||
|
||||
const textTexture = new THREE.CanvasTexture(textCanvas);
|
||||
textTexture.minFilter = THREE.LinearFilter;
|
||||
const textMat = new THREE.MeshBasicMaterial({
|
||||
@@ -423,6 +467,56 @@ function createTerminalPanel(parent, x, y, rot, title, color, lines) {
|
||||
textMesh.position.z = 0.01;
|
||||
group.add(textMesh);
|
||||
|
||||
// Update function for this specific panel
|
||||
const updatePanel = (state) => {
|
||||
ctx.clearRect(0, 0, 512, 640);
|
||||
|
||||
// Header
|
||||
ctx.fillStyle = '#' + new THREE.Color(color).getHexString();
|
||||
ctx.font = 'bold 32px "Orbitron", sans-serif';
|
||||
ctx.fillText(title, 20, 45);
|
||||
ctx.fillRect(20, 55, 472, 2);
|
||||
|
||||
ctx.font = '20px "JetBrains Mono", monospace';
|
||||
ctx.fillStyle = '#a0b8d0';
|
||||
|
||||
let lines = [];
|
||||
if (id === 'command') {
|
||||
lines = [
|
||||
`> STATUS: NOMINAL`,
|
||||
`> UPTIME: ${state.metrics.uptime.toFixed(1)}s`,
|
||||
`> HARNESS: STABLE`,
|
||||
`> MODE: SOVEREIGN`
|
||||
];
|
||||
} else if (id === 'queue') {
|
||||
lines = ['> ISSUE #4: CORE', '> ISSUE #5: PORTAL', '> ISSUE #6: TERMINAL', '> ISSUE #39: HEART'];
|
||||
} else if (id === 'metrics') {
|
||||
lines = [
|
||||
`> CPU: ${state.metrics.cpu}%`,
|
||||
`> MEM: ${state.metrics.mem}GB`,
|
||||
`> LOOPS: ${state.metrics.activeLoops}`,
|
||||
`> FPS: ${state.metrics.fps}`
|
||||
];
|
||||
} else if (id === 'thoughts') {
|
||||
lines = state.thoughts.map(t => `> ${t}`);
|
||||
} else if (id === 'agents') {
|
||||
lines = Object.entries(state.agents).map(([name, status]) => `> ${name.toUpperCase()}: ${status}`);
|
||||
}
|
||||
|
||||
lines.forEach((line, i) => {
|
||||
let fillColor = '#a0b8d0';
|
||||
if (line.includes('RUNNING') || line.includes('ACTIVE')) fillColor = '#4af0c0';
|
||||
ctx.fillStyle = fillColor;
|
||||
ctx.fillText(line, 20, 100 + i * 40);
|
||||
});
|
||||
|
||||
textTexture.needsUpdate = true;
|
||||
};
|
||||
|
||||
// Initial draw
|
||||
updatePanel(STATE);
|
||||
Broadcaster.subscribe(updatePanel);
|
||||
|
||||
// Scanline effect overlay
|
||||
const scanGeo = new THREE.PlaneGeometry(w, h);
|
||||
const scanMat = new THREE.ShaderMaterial({
|
||||
@@ -458,18 +552,6 @@ function createTerminalPanel(parent, x, y, rot, title, color, lines) {
|
||||
scanMesh.position.z = 0.02;
|
||||
group.add(scanMesh);
|
||||
|
||||
// Glow behind panel
|
||||
const glowGeo = new THREE.PlaneGeometry(w + 0.5, h + 0.5);
|
||||
const glowMat = new THREE.MeshBasicMaterial({
|
||||
color: color,
|
||||
transparent: true,
|
||||
opacity: 0.06,
|
||||
side: THREE.DoubleSide,
|
||||
});
|
||||
const glowMesh = new THREE.Mesh(glowGeo, glowMat);
|
||||
glowMesh.position.z = -0.05;
|
||||
group.add(glowMesh);
|
||||
|
||||
parent.add(group);
|
||||
batcaveTerminals.push({ group, scanMat, borderMat });
|
||||
}
|
||||
@@ -877,6 +959,9 @@ function gameLoop() {
|
||||
const delta = Math.min(clock.getDelta(), 0.1);
|
||||
const elapsed = clock.elapsedTime;
|
||||
|
||||
// ─── Sovereign State Update ─────────────────────────────────────
|
||||
updateSovereignState(elapsed);
|
||||
|
||||
// ─── Navigation update ───────────────────────────────────────────
|
||||
const mode = NAV_MODES[navModeIdx];
|
||||
const chatActive = document.activeElement === document.getElementById('chat-input');
|
||||
@@ -993,6 +1078,7 @@ function gameLoop() {
|
||||
core.position.y = 2.5 + Math.sin(elapsed * 1.2) * 0.3;
|
||||
core.rotation.y = elapsed * 0.4;
|
||||
core.rotation.x = elapsed * 0.2;
|
||||
// Core pulses in sync with state updates (simulated heartbeat)
|
||||
core.material.emissiveIntensity = 1.5 + Math.sin(elapsed * 2) * 0.5;
|
||||
}
|
||||
|
||||
@@ -1006,11 +1092,14 @@ function gameLoop() {
|
||||
fps = frameCount;
|
||||
frameCount = 0;
|
||||
lastFPSTime = now;
|
||||
// Update state metrics
|
||||
STATE.metrics.fps = fps;
|
||||
STATE.metrics.drawCalls = renderer.info.render.calls;
|
||||
STATE.metrics.triangles = renderer.info.render.triangles;
|
||||
}
|
||||
if (debugOverlay) {
|
||||
const info = renderer.info;
|
||||
debugOverlay.textContent =
|
||||
`FPS: ${fps} Draw: ${info.render?.calls} Tri: ${info.render?.triangles} [${performanceTier}]\n` +
|
||||
`FPS: ${fps} Draw: ${renderer.info.render.calls} Tri: ${renderer.info.render.triangles} [${performanceTier}]\n` +
|
||||
`Pos: ${playerPos.x.toFixed(1)}, ${playerPos.y.toFixed(1)}, ${playerPos.z.toFixed(1)} NAV: ${NAV_MODES[navModeIdx]}`;
|
||||
}
|
||||
renderer.info.reset();
|
||||
|
||||
Reference in New Issue
Block a user