diff --git a/app.js b/app.js index 60689a0..2176054 100644 --- a/app.js +++ b/app.js @@ -115,6 +115,9 @@ function init() { setTimeout(() => { document.getElementById('loading-screen').remove(); }, 900); }, 600); + // Tower Log + initTowerLog(); + // Start loop requestAnimationFrame(gameLoop); } @@ -872,6 +875,124 @@ function addChatMessage(type, text) { container.scrollTop = container.scrollHeight; } +// ═══ TOWER LOG ═══ +const TOWER_LOG = { + maxEntries: 60, + open: true, + seedEvents: [ + { type: 'system', msg: 'Tower Log initialized — monitoring all systems' }, + { type: 'commit', msg: '⎇ feat: add procedural nebula skybox shader' }, + { type: 'commit', msg: '⎇ fix: portal torus rotation clamping' }, + { type: 'agent', msg: '🤖 Kimi completed: nexus-v1 layout pass' }, + { type: 'training', msg: '⚙ Training cycle #14892 completed — loss 0.0031' }, + { type: 'merge', msg: '⬡ PR #47 merged: batcave terminal panels' }, + { type: 'visitor', msg: '👁 Visitor arrived: Alexander Whitestone' }, + { type: 'commit', msg: '⎇ chore: update README with architecture notes' }, + { type: 'agent', msg: '🤖 Claude Code completed: floor grid optimization' }, + { type: 'commit', msg: '⎇ feat: ambient crystal formations' }, + { type: 'training', msg: '⚙ Thought cycle #14893 — priority rebalance' }, + { type: 'merge', msg: '⬡ PR #48 merged: post-processing bloom pass' }, + ], + liveTemplates: { + commit: [ + '⎇ fix: resolve shader uniform timing drift', + '⎇ feat: add runestone float animation', + '⎇ refactor: extract portal swirl to helper', + '⎇ chore: bump three.js to 0.183', + '⎇ feat: dust particle emitter tuning', + '⎇ fix: memory leak in canvas texture disposal', + '⎇ feat: nexus core emissive pulse', + ], + merge: [ + '⬡ PR #49 merged: harness agent loop v2', + '⬡ PR #50 merged: world navigation scaffolding', + '⬡ PR #51 merged: WebSocket chat integration', + '⬡ PR #52 merged: tower log narrative feed', + ], + agent: [ + '🤖 Hermes: routed 7 new tasks to queue', + '🤖 Kimi completed: issue triage pass #204', + '🤖 Claude Code: opened PR for #11 tower log', + '🤖 Gemini: woke from standby — code review cycle', + '🤖 Perplexity: world snapshot saved', + ], + visitor: [ + '👁 Visitor arrived: Alexander Whitestone', + '👁 Visitor arrived: anonymous scout', + '👁 Observer detected in sector 7', + ], + training: [ + '⚙ Training cycle complete — model saved', + '⚙ Thought stream checkpoint written', + '⚙ Memory consolidation pass finished', + '⚙ Attention layer tuning: Δ0.0004', + ], + }, +}; + +function towerLogTimestamp() { + const d = new Date(); + return d.toTimeString().slice(0, 8); +} + +function addTowerEntry(type, msg) { + const container = document.getElementById('tower-log-entries'); + if (!container) return; + + const entry = document.createElement('div'); + entry.className = `tower-log-entry type-${type}`; + entry.innerHTML = + `${towerLogTimestamp()}` + + `${msg}`; + + // Prepend so newest is at top (column-reverse layout handles visual order) + container.appendChild(entry); + + // Trim old entries + while (container.children.length > TOWER_LOG.maxEntries) { + container.removeChild(container.firstChild); + } +} + +function initTowerLog() { + // Seed with historical events (oldest first) + TOWER_LOG.seedEvents.forEach(e => addTowerEntry(e.type, e.msg)); + + // Toggle collapse + document.getElementById('tower-log-toggle')?.addEventListener('click', () => { + TOWER_LOG.open = !TOWER_LOG.open; + document.getElementById('tower-log').classList.toggle('collapsed', !TOWER_LOG.open); + }); + + // Schedule live events + scheduleLiveEvents(); +} + +function scheduleLiveEvents() { + const types = Object.keys(TOWER_LOG.liveTemplates); + // Weights: commits most frequent, visitors rare + const weighted = [ + ...Array(5).fill('commit'), + ...Array(2).fill('agent'), + ...Array(2).fill('training'), + ...Array(1).fill('merge'), + ...Array(1).fill('visitor'), + ]; + + function fireNext() { + const type = weighted[Math.floor(Math.random() * weighted.length)]; + const msgs = TOWER_LOG.liveTemplates[type]; + const msg = msgs[Math.floor(Math.random() * msgs.length)]; + addTowerEntry(type, msg); + // Next event in 8–25 seconds + const delay = 8000 + Math.random() * 17000; + setTimeout(fireNext, delay); + } + + // First live event after 5–10s + setTimeout(fireNext, 5000 + Math.random() * 5000); +} + // ═══ GAME LOOP ═══ function gameLoop() { requestAnimationFrame(gameLoop); diff --git a/index.html b/index.html index 3a2c6ea..7b6bd99 100644 --- a/index.html +++ b/index.html @@ -95,6 +95,16 @@ + +
+
+ + TOWER LOG + +
+
+
+
WASD move   Mouse look   Enter chat diff --git a/style.css b/style.css index 519b05e..8c39ac1 100644 --- a/style.css +++ b/style.css @@ -359,3 +359,100 @@ canvas#nexus-canvas { display: none; } } + +/* === TOWER LOG === */ +.tower-log { + position: absolute; + top: var(--space-4); + right: var(--space-4); + width: 340px; + max-height: 320px; + background: var(--color-surface); + backdrop-filter: blur(var(--panel-blur)); + border: 1px solid rgba(123, 92, 255, 0.3); + border-radius: var(--panel-radius); + display: flex; + flex-direction: column; + overflow: hidden; + pointer-events: auto; + transition: max-height var(--transition-ui); +} +.tower-log.collapsed { + max-height: 36px; +} +.tower-log-header { + display: flex; + align-items: center; + gap: var(--space-2); + padding: var(--space-2) var(--space-3); + border-bottom: 1px solid rgba(123, 92, 255, 0.2); + font-family: var(--font-display); + font-size: var(--text-xs); + letter-spacing: 0.12em; + font-weight: 600; + color: var(--color-secondary); + flex-shrink: 0; +} +.tower-log-icon { + font-size: 12px; + animation: spin-slow 12s linear infinite; + color: var(--color-secondary); +} +.tower-log-title { + flex: 1; +} +.tower-log-toggle-btn { + background: none; + border: none; + color: var(--color-text-muted); + font-size: 12px; + cursor: pointer; + transition: transform var(--transition-ui); + padding: 0 var(--space-1); +} +.tower-log.collapsed .tower-log-toggle-btn { + transform: rotate(180deg); +} +.tower-log-entries { + flex: 1; + overflow-y: auto; + padding: var(--space-2) var(--space-3); + display: flex; + flex-direction: column-reverse; + gap: 2px; + scrollbar-width: thin; + scrollbar-color: rgba(123, 92, 255, 0.2) transparent; +} +.tower-log-entry { + font-size: 11px; + line-height: 1.5; + padding: 3px 0; + border-bottom: 1px solid rgba(255,255,255,0.03); + display: flex; + gap: var(--space-2); + animation: log-entry-in 0.3s ease-out; + opacity: 0.9; +} +@keyframes log-entry-in { + from { opacity: 0; transform: translateX(8px); } + to { opacity: 0.9; transform: translateX(0); } +} +.tower-log-entry:last-child { + border-bottom: none; +} +.tower-log-ts { + color: var(--color-text-muted); + white-space: nowrap; + font-variant-numeric: tabular-nums; + flex-shrink: 0; +} +.tower-log-body { + flex: 1; + color: var(--color-text); +} +.tower-log-entry.type-commit .tower-log-body { color: #4af0c0; } +.tower-log-entry.type-merge .tower-log-body { color: #7b5cff; } +.tower-log-entry.type-agent .tower-log-body { color: #ff8844; } +.tower-log-entry.type-visitor .tower-log-body { color: #ffd700; } +.tower-log-entry.type-training .tower-log-body { color: #44aaff; } +.tower-log-entry.type-system .tower-log-body { color: var(--color-text-muted); }