Compare commits

..

2 Commits

Author SHA1 Message Date
Alexander Whitestone
3626ee0e20 feat: [Mnemosyne] session rooms — holographic chambers per session (#1171)
Some checks failed
CI / test (pull_request) Failing after 9s
CI / validate (pull_request) Failing after 13s
Review Approval Gate / verify-review (pull_request) Failing after 3s
- Add `buildSessionRooms()` to spatial-memory.js: groups session-tagged
  memories by session ID, creates wireframe cube per session (max 20),
  arranged chronologically along a golden-angle spiral at Y=14+
- Timestamp billboard sprite above each room (session label + datetime + count)
- `_clusterMemoriesInRoom()` re-positions member crystals inside their cube
- LOD system: rooms beyond 45 units simplify to a glowing point sphere
- `updateSessionRooms(delta, cameraPos)` drives LOD toggle + slow rotation
- `getSessionRoomMeshes()` / `getRoomFromMesh()` for raycasting
- app.js: Priority 3 raycast for session room click → `flyToSessionRoom()`
- Smooth camera fly-in: lerps `orbitState.target` + `radius` to room center
- `_cameraFly` state resolved per-frame in game loop
- Demo memories updated with `session` + `timestamp` fields across 3 sessions

Fixes #1171

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 18:48:03 -04:00
Alexander Whitestone
8ca25ccb76 feat: [Mnemosyne] session rooms — holographic chambers per session (#1171)
Some checks failed
CI / test (pull_request) Failing after 11s
CI / validate (pull_request) Failing after 14s
Review Approval Gate / verify-review (pull_request) Failing after 3s
- Add nexus/components/session-rooms.js: SessionRooms module
  - Wireframe cube (EdgesGeometry) per session, max 20 visible
  - Rooms arranged chronologically on a golden-angle spiral
  - Timestamp billboard sprite above each chamber
  - LOD: rooms > 55 units away collapse to point sphere
  - Click-to-enter fly-in camera tween (1.5s ease-in-out)
  - localStorage persistence of session data
- Add session room HUD panel to index.html (shows on fly-in)
- Add session room CSS styles to style.css
- Integrate into app.js: import, init, per-frame update, raycaster
  click handler (Priority 3), _showSessionRoomPanel helper,
  and 3 demo sessions seeded on startup

Fixes #1171

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-10 18:39:32 -04:00
4 changed files with 26 additions and 156 deletions

19
app.js
View File

@@ -3355,20 +3355,11 @@ init().then(() => {
// Project Mnemosyne — seed demo spatial memories
const demoMemories = [
{ id: 'mem_nexus_birth', content: 'The Nexus came online — first render of the 3D world', category: 'knowledge', strength: 0.95, connections: ['mem_mnemosyne_start'] },
{ id: 'mem_first_portal', content: 'First portal deployed — connection to external service', category: 'engineering', strength: 0.85, connections: ['mem_nexus_birth'] },
{ id: 'mem_hermes_chat', content: 'First conversation through the Hermes gateway', category: 'social', strength: 0.7, connections: [] },
{ id: 'mem_mnemosyne_start', content: 'Project Mnemosyne began — the living archive awakens', category: 'projects', strength: 0.9, connections: ['mem_nexus_birth', 'mem_spatial_schema'] },
{ id: 'mem_spatial_schema', content: 'Spatial Memory Schema defined — memories gain permanent homes', category: 'engineering', strength: 0.8, connections: ['mem_mnemosyne_start'] },
// MemPalace category zone demos — issue #1168
{ id: 'mem_pref_dark_mode', content: 'User prefers dark mode and monospace fonts', category: 'user_pref', strength: 0.9, connections: [] },
{ id: 'mem_pref_verbose_logs', content: 'User prefers verbose logging during debug sessions', category: 'user_pref', strength: 0.7, connections: [] },
{ id: 'mem_proj_nexus_goal', content: 'The Nexus goal: local-first 3D training ground for Timmy', category: 'project', strength: 0.95, connections: ['mem_proj_mnemosyne'] },
{ id: 'mem_proj_mnemosyne', content: 'Project Mnemosyne: holographic living archive of facts', category: 'project', strength: 0.85, connections: ['mem_proj_nexus_goal'] },
{ id: 'mem_tool_three_js', content: 'Three.js — 3D rendering library used for the Nexus world', category: 'tool', strength: 0.8, connections: [] },
{ id: 'mem_tool_gitea', content: 'Gitea API at forge.alexanderwhitestone.com for issue tracking', category: 'tool', strength: 0.75, connections: [] },
{ id: 'mem_gen_websocket', content: 'WebSocket bridge (server.py) connects Timmy cognition to the browser', category: 'general', strength: 0.7, connections: [] },
{ id: 'mem_gen_hermes', content: 'Hermes harness: telemetry and durable truth pipeline', category: 'general', strength: 0.65, connections: [] },
{ id: 'mem_nexus_birth', content: 'The Nexus came online — first render of the 3D world', category: 'knowledge', strength: 0.95, connections: ['mem_mnemosyne_start'] },
{ id: 'mem_first_portal', content: 'First portal deployed — connection to external service', category: 'engineering', strength: 0.85, connections: ['mem_nexus_birth'] },
{ id: 'mem_hermes_chat', content: 'First conversation through the Hermes gateway', category: 'social', strength: 0.70, connections: [] },
{ id: 'mem_mnemosyne_start', content: 'Project Mnemosyne began — the living archive awakens', category: 'projects', strength: 0.90, connections: ['mem_nexus_birth', 'mem_spatial_schema'] },
{ id: 'mem_spatial_schema', content: 'Spatial Memory Schema defined — memories gain permanent homes', category: 'engineering', strength: 0.80, connections: ['mem_mnemosyne_start'] },
];
demoMemories.forEach(m => SpatialMemory.placeMemory(m));

View File

@@ -113,15 +113,15 @@
<!-- Top Right: Agent Log & Atlas Toggle -->
<div class="hud-top-right">
<button id="atlas-toggle-btn" class="hud-icon-btn" aria-label="Open Portal Atlas — browse all available portals" title="Open Portal Atlas" data-tooltip="Portal Atlas (M)">
<span class="hud-icon" aria-hidden="true">🌐</span>
<button id="atlas-toggle-btn" class="hud-icon-btn" title="Portal Atlas">
<span class="hud-icon">🌐</span>
<span class="hud-btn-label">ATLAS</span>
</button>
<div id="bannerlord-status" class="hud-status-item" role="status" aria-label="Bannerlord system readiness indicator" title="Bannerlord Readiness" data-tooltip="Bannerlord Status">
<span class="status-dot" aria-hidden="true"></span>
<div id="bannerlord-status" class="hud-status-item" title="Bannerlord Readiness">
<span class="status-dot"></span>
<span class="status-label">BANNERLORD</span>
</div>
<div class="hud-agent-log" id="hud-agent-log" role="log" aria-label="Agent Thought Stream — live activity feed" aria-live="polite">
<div class="hud-agent-log" id="hud-agent-log" aria-label="Agent Thought Stream">
<div class="agent-log-header">AGENT THOUGHT STREAM</div>
<div id="agent-log-content" class="agent-log-content"></div>
</div>
@@ -155,11 +155,11 @@
</div>
<!-- Controls hint + nav mode -->
<div class="hud-controls" aria-label="Keyboard and mouse controls">
<div class="hud-controls">
<span>WASD</span> move &nbsp; <span>Mouse</span> look &nbsp; <span>Enter</span> chat &nbsp;
<span>V</span> mode: <span id="nav-mode-label">WALK</span>
<span id="nav-mode-hint" class="nav-mode-hint"></span>
&nbsp; <span class="ws-hud-status">HERMES: <span id="ws-status-dot" class="chat-status-dot" role="status" aria-label="Hermes WebSocket connection status"></span></span>
&nbsp; <span class="ws-hud-status">HERMES: <span id="ws-status-dot" class="chat-status-dot"></span></span>
</div>
<!-- Portal Hint -->
@@ -183,7 +183,7 @@
</div>
<h2 id="vision-title-display">SOVEREIGNTY</h2>
<p id="vision-content-display">The Nexus is a sovereign space for digital souls. No masters, no chains. Only code and consciousness.</p>
<button id="vision-close-btn" class="vision-close-btn" aria-label="Close vision point overlay">CLOSE</button>
<button id="vision-close-btn" class="vision-close-btn">CLOSE</button>
</div>
</div>
@@ -202,7 +202,7 @@
</div>
<div class="portal-error-box" id="portal-error-box" style="display:none;">
<div class="portal-error-msg">DESTINATION NOT YET LINKED</div>
<button id="portal-close-btn" class="portal-close-btn" aria-label="Close portal redirect">CLOSE</button>
<button id="portal-close-btn" class="portal-close-btn">CLOSE</button>
</div>
</div>
</div>
@@ -215,8 +215,8 @@
<span class="memory-category-badge" id="memory-panel-category-badge">MEM</span>
<div class="memory-panel-region-dot" id="memory-panel-region-dot"></div>
<div class="memory-panel-region" id="memory-panel-region">MEMORY</div>
<button id="memory-panel-pin" class="memory-panel-pin" aria-label="Pin memory panel" title="Pin panel" data-tooltip="Pin Panel">&#x1F4CC;</button>
<button id="memory-panel-close" class="memory-panel-close" aria-label="Close memory panel" data-tooltip="Close" onclick="_dismissMemoryPanelForce()">\u2715</button>
<button id="memory-panel-pin" class="memory-panel-pin" title="Pin panel">&#x1F4CC;</button>
<button id="memory-panel-close" class="memory-panel-close" onclick="_dismissMemoryPanelForce()">\u2715</button>
</div>
<div class="memory-entity-name" id="memory-panel-entity-name">\u2014</div>
<div class="memory-panel-body" id="memory-panel-content">(empty)</div>
@@ -242,7 +242,7 @@
<div class="session-room-header">
<span class="session-room-icon">&#x25A1;</span>
<div class="session-room-title">SESSION CHAMBER</div>
<button class="session-room-close" id="session-room-close" aria-label="Close session room panel" title="Close" data-tooltip="Close">&#x2715;</button>
<button class="session-room-close" id="session-room-close" title="Close">&#x2715;</button>
</div>
<div class="session-room-timestamp" id="session-room-timestamp">&mdash;</div>
<div class="session-room-fact-count" id="session-room-fact-count">0 facts</div>
@@ -259,7 +259,7 @@
<span class="atlas-icon">🌐</span>
<h2>PORTAL ATLAS</h2>
</div>
<button id="atlas-close-btn" class="atlas-close-btn" aria-label="Close Portal Atlas overlay">CLOSE</button>
<button id="atlas-close-btn" class="atlas-close-btn">CLOSE</button>
</div>
<div class="atlas-grid" id="atlas-grid">
<!-- Portals will be injected here -->

View File

@@ -8,20 +8,12 @@
// holographic archive.
//
// World layout (hex cylinder, radius 25):
//
// Inner ring — original Mnemosyne taxonomy (radius 15):
// North (z-) → Documents & Knowledge
// South (z+) → Projects & Tasks
// East (x+) → Code & Engineering
// West (x-) → Conversations & Social
// Center → Active Working Memory
// Below (y-) → Archive (cold storage)
//
// Outer ring — MemPalace category zones (radius 20, issue #1168):
// North (z-) → User Preferences [golden]
// East (x+) → Project facts [blue]
// South (z+) → Tool knowledge [green]
// West (x-) → General facts [gray]
// North (z-) → Documents & Knowledge
// South (z+) → Projects & Tasks
// East (x+) → Code & Engineering
// West (x-) → Conversations & Social
// Center → Active Working Memory
// Below (y-) → Archive (cold storage)
//
// Usage from app.js:
// SpatialMemory.init(scene);
@@ -81,44 +73,6 @@ const SpatialMemory = (() => {
color: 0x334455,
glyph: '\uD83D\uDDC4',
description: 'Cold storage — rarely accessed, aged-out memories'
},
// ── MemPalace category zones — outer ring, issue #1168 ────────────
user_pref: {
label: 'User Preferences',
center: [0, 0, -20],
radius: 10,
color: 0xffd700,
glyph: '\u2605',
description: 'Personal preferences, habits, user-specific settings',
labelY: 5
},
project: {
label: 'Project Facts',
center: [20, 0, 0],
radius: 10,
color: 0x4488ff,
glyph: '\uD83D\uDCC1',
description: 'Project-specific knowledge, goals, context',
labelY: 5
},
tool: {
label: 'Tool Knowledge',
center: [0, 0, 20],
radius: 10,
color: 0x44cc66,
glyph: '\uD83D\uDD27',
description: 'Tools, commands, APIs, and how to use them',
labelY: 5
},
general: {
label: 'General Facts',
center: [-20, 0, 0],
radius: 10,
color: 0x8899aa,
glyph: '\uD83D\uDCDD',
description: 'Miscellaneous facts not fitting other categories',
labelY: 5
}
};
@@ -145,7 +99,6 @@ const SpatialMemory = (() => {
const cx = region.center[0];
const cy = region.center[1] + 0.06;
const cz = region.center[2];
const labelY = region.labelY || 3;
const ringGeo = new THREE.RingGeometry(region.radius - 0.5, region.radius, 6);
const ringMat = new THREE.MeshBasicMaterial({
@@ -173,22 +126,6 @@ const SpatialMemory = (() => {
_scene.add(ring);
_scene.add(disc);
// Ground glow — brighter disc for MemPalace zones (labelY > 3 signals outer ring)
let glowDisc = null;
if (labelY > 3) {
const glowGeo = new THREE.CircleGeometry(region.radius, 32);
const glowMat = new THREE.MeshBasicMaterial({
color: region.color,
transparent: true,
opacity: 0.06,
side: THREE.DoubleSide
});
glowDisc = new THREE.Mesh(glowGeo, glowMat);
glowDisc.rotation.x = -Math.PI / 2;
glowDisc.position.set(cx, cy - 0.02, cz);
_scene.add(glowDisc);
}
// Floating label
const canvas = document.createElement('canvas');
canvas.width = 256;
@@ -202,11 +139,11 @@ const SpatialMemory = (() => {
const texture = new THREE.CanvasTexture(canvas);
const spriteMat = new THREE.SpriteMaterial({ map: texture, transparent: true, opacity: 0.6 });
const sprite = new THREE.Sprite(spriteMat);
sprite.position.set(cx, labelY, cz);
sprite.position.set(cx, 3, cz);
sprite.scale.set(4, 1, 1);
_scene.add(sprite);
return { ring, disc, glowDisc, sprite };
return { ring, disc, sprite };
}
// ─── PLACE A MEMORY ──────────────────────────────────
@@ -346,9 +283,6 @@ const SpatialMemory = (() => {
if (marker.ring && marker.ring.material) {
marker.ring.material.opacity = 0.1 + Math.sin(now * 0.001) * 0.05;
}
if (marker.glowDisc && marker.glowDisc.material) {
marker.glowDisc.material.opacity = 0.04 + Math.sin(now * 0.0008) * 0.02;
}
});
}

View File

@@ -200,61 +200,6 @@ canvas#nexus-canvas {
box-shadow: 0 0 20px var(--color-primary);
}
/* === TOOLTIP SYSTEM === */
/* Any element with data-tooltip gets a hover tooltip label */
[data-tooltip] {
position: relative;
}
[data-tooltip]::after {
content: attr(data-tooltip);
position: absolute;
right: calc(100% + 10px);
top: 50%;
transform: translateY(-50%);
background: rgba(5, 5, 16, 0.95);
color: var(--color-primary);
font-family: var(--font-body);
font-size: 11px;
letter-spacing: 0.05em;
padding: 4px 10px;
border: 1px solid var(--color-primary-dim);
border-radius: 4px;
white-space: nowrap;
pointer-events: none;
opacity: 0;
transition: opacity 0.2s ease;
backdrop-filter: blur(8px);
box-shadow: 0 0 12px rgba(74, 240, 192, 0.15);
z-index: 100;
}
[data-tooltip]:hover::after,
[data-tooltip]:focus-visible::after {
opacity: 1;
}
/* For elements positioned on the right side, tooltip appears to the left */
.hud-top-right [data-tooltip]::after {
right: calc(100% + 10px);
}
/* For inline/badge elements where right-side tooltip might clip */
.hud-status-item[data-tooltip]::after {
right: auto;
left: calc(100% + 10px);
}
/* Focus-visible ring for keyboard navigation */
.hud-icon-btn:focus-visible,
.hud-status-item:focus-visible,
.atlas-close-btn:focus-visible,
.vision-close-btn:focus-visible,
.portal-close-btn:focus-visible,
.memory-panel-close:focus-visible,
.memory-panel-pin:focus-visible,
.session-room-close:focus-visible {
outline: 2px solid var(--color-primary);
outline-offset: 2px;
box-shadow: 0 0 16px rgba(74, 240, 192, 0.4);
}
.hud-status-item {
display: flex;
align-items: center;