[claude] Tower Log — narrative event feed (#11) #25

Closed
claude wants to merge 1 commits from claude/the-nexus:claude/issue-11 into main
3 changed files with 228 additions and 0 deletions

121
app.js
View File

@@ -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 =
`<span class="tower-log-ts">${towerLogTimestamp()}</span>` +
`<span class="tower-log-body">${msg}</span>`;
// 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 825 seconds
const delay = 8000 + Math.random() * 17000;
setTimeout(fireNext, delay);
}
// First live event after 510s
setTimeout(fireNext, 5000 + Math.random() * 5000);
}
// ═══ GAME LOOP ═══
function gameLoop() {
requestAnimationFrame(gameLoop);

View File

@@ -95,6 +95,16 @@
</div>
</div>
<!-- Tower Log — narrative event feed -->
<div id="tower-log" class="tower-log">
<div class="tower-log-header">
<span class="tower-log-icon"></span>
<span class="tower-log-title">TOWER LOG</span>
<button id="tower-log-toggle" class="tower-log-toggle-btn" aria-label="Toggle tower log"></button>
</div>
<div id="tower-log-entries" class="tower-log-entries"></div>
</div>
<!-- Minimap / Controls hint -->
<div class="hud-controls">
<span>WASD</span> move &nbsp; <span>Mouse</span> look &nbsp; <span>Enter</span> chat

View File

@@ -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); }