Compare commits
3 Commits
claude/iss
...
mimo/build
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7fde7569e4 | ||
| cc4af009c7 | |||
| 089b06b6f8 |
85
app.js
85
app.js
@@ -48,6 +48,9 @@ let chatOpen = true;
|
||||
let loadProgress = 0;
|
||||
let performanceTier = 'high';
|
||||
|
||||
// ═══ UI MODE (VISITOR / OPERATOR) ═══
|
||||
let uiMode = 'visitor'; // 'visitor' | 'operator'
|
||||
|
||||
// ═══ HERMES WS STATE ═══
|
||||
let hermesWs = null;
|
||||
let wsReconnectTimer = null;
|
||||
@@ -1867,6 +1870,9 @@ function setupControls() {
|
||||
if (e.key.toLowerCase() === 'v' && document.activeElement !== document.getElementById('chat-input')) {
|
||||
cycleNavMode();
|
||||
}
|
||||
if (e.key.toLowerCase() === 'o' && document.activeElement !== document.getElementById('chat-input')) {
|
||||
toggleUIMode();
|
||||
}
|
||||
if (e.key.toLowerCase() === 'f' && activePortal && !portalOverlayActive) {
|
||||
activatePortal(activePortal);
|
||||
}
|
||||
@@ -1964,18 +1970,9 @@ function setupControls() {
|
||||
});
|
||||
document.getElementById('chat-send').addEventListener('click', () => sendChatMessage());
|
||||
|
||||
// Add MemPalace mining button
|
||||
// Add MemPalace mining button (operator-only)
|
||||
document.querySelector('.chat-quick-actions').innerHTML += `
|
||||
<button class="quick-action-btn" onclick="mineMemPalaceContent()">Mine Chat</button>
|
||||
<div id="mem-palace-stats" class="mem-palace-stats">
|
||||
<div>Compression: <span id="compression-ratio">--</span>x</div>
|
||||
<div>Docs: <span id="docs-mined">0</span></div>
|
||||
<div>AAAK: <span id="aaak-size">0B</span></div>
|
||||
<div>Compression: <span id="compression-ratio">--</span>x</div>
|
||||
<div>Docs: <span id="docs-mined">0</span></div>
|
||||
<div>AAAK: <span id="aaak-size">0B</span></div>
|
||||
<div class="mem-palace-logs" style="margin-top:4px; font-size:10px; color:#4af0c0;">Logs: <span id="mem-logs">0</span></div>
|
||||
</div>
|
||||
<button class="quick-action-btn operator-only" onclick="mineMemPalaceContent()">Mine Chat</button>
|
||||
`;
|
||||
|
||||
// Chat quick actions
|
||||
@@ -2006,6 +2003,53 @@ function setupControls() {
|
||||
|
||||
document.getElementById('atlas-toggle-btn').addEventListener('click', openPortalAtlas);
|
||||
document.getElementById('atlas-close-btn').addEventListener('click', closePortalAtlas);
|
||||
|
||||
// ═══ VISITOR / OPERATOR MODE TOGGLE ═══
|
||||
// Restore saved mode from localStorage
|
||||
const savedMode = localStorage.getItem('nexus-ui-mode');
|
||||
if (savedMode === 'operator') {
|
||||
uiMode = 'operator';
|
||||
}
|
||||
applyUIMode();
|
||||
|
||||
// Create and append mode toggle button to the HUD controls area
|
||||
const modeBtn = document.createElement('button');
|
||||
modeBtn.id = 'mode-toggle-btn';
|
||||
modeBtn.className = 'mode-toggle-btn';
|
||||
modeBtn.setAttribute('data-mode', uiMode);
|
||||
modeBtn.innerHTML = modeToggleLabel();
|
||||
modeBtn.title = uiMode === 'visitor' ? 'Switch to Operator mode' : 'Switch to Visitor mode';
|
||||
modeBtn.addEventListener('click', toggleUIMode);
|
||||
|
||||
const hudEl = document.getElementById('hud');
|
||||
if (hudEl) hudEl.appendChild(modeBtn);
|
||||
}
|
||||
|
||||
function toggleUIMode() {
|
||||
uiMode = uiMode === 'visitor' ? 'operator' : 'visitor';
|
||||
localStorage.setItem('nexus-ui-mode', uiMode);
|
||||
applyUIMode();
|
||||
|
||||
// Update button
|
||||
const btn = document.getElementById('mode-toggle-btn');
|
||||
if (btn) {
|
||||
btn.setAttribute('data-mode', uiMode);
|
||||
btn.innerHTML = modeToggleLabel();
|
||||
btn.title = uiMode === 'visitor' ? 'Switch to Operator mode' : 'Switch to Visitor mode';
|
||||
}
|
||||
|
||||
addChatMessage('system', `Mode: ${uiMode.toUpperCase()}. ${uiMode === 'operator' ? 'Operator panels enabled.' : 'Visitor view. Clean and focused.'}`);
|
||||
}
|
||||
|
||||
function modeToggleLabel() {
|
||||
if (uiMode === 'visitor') {
|
||||
return '<span class="mode-icon">👁</span> VISITOR';
|
||||
}
|
||||
return '<span class="mode-icon">⚙</span> OPERATOR';
|
||||
}
|
||||
|
||||
function applyUIMode() {
|
||||
document.body.classList.toggle('visitor-mode', uiMode === 'visitor');
|
||||
}
|
||||
|
||||
function sendChatMessage(overrideText = null) {
|
||||
@@ -3355,11 +3399,20 @@ 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.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'] },
|
||||
{ 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: [] },
|
||||
];
|
||||
demoMemories.forEach(m => SpatialMemory.placeMemory(m));
|
||||
|
||||
|
||||
@@ -157,7 +157,8 @@
|
||||
<!-- Controls hint + nav mode -->
|
||||
<div class="hud-controls">
|
||||
<span>WASD</span> move <span>Mouse</span> look <span>Enter</span> chat
|
||||
<span>V</span> mode: <span id="nav-mode-label">WALK</span>
|
||||
<span>V</span> nav: <span id="nav-mode-label">WALK</span>
|
||||
<span>O</span> toggle mode
|
||||
<span id="nav-mode-hint" class="nav-mode-hint"></span>
|
||||
<span class="ws-hud-status">HERMES: <span id="ws-status-dot" class="chat-status-dot"></span></span>
|
||||
</div>
|
||||
|
||||
@@ -8,12 +8,20 @@
|
||||
// holographic archive.
|
||||
//
|
||||
// World layout (hex cylinder, radius 25):
|
||||
// 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)
|
||||
//
|
||||
// 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]
|
||||
//
|
||||
// Usage from app.js:
|
||||
// SpatialMemory.init(scene);
|
||||
@@ -73,6 +81,44 @@ 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
|
||||
}
|
||||
};
|
||||
|
||||
@@ -99,6 +145,7 @@ 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({
|
||||
@@ -126,6 +173,22 @@ 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;
|
||||
@@ -139,11 +202,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, 3, cz);
|
||||
sprite.position.set(cx, labelY, cz);
|
||||
sprite.scale.set(4, 1, 1);
|
||||
_scene.add(sprite);
|
||||
|
||||
return { ring, disc, sprite };
|
||||
return { ring, disc, glowDisc, sprite };
|
||||
}
|
||||
|
||||
// ─── PLACE A MEMORY ──────────────────────────────────
|
||||
@@ -283,6 +346,9 @@ 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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
81
style.css
81
style.css
@@ -1580,3 +1580,84 @@ canvas#nexus-canvas {
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
/* === VISITOR / OPERATOR MODE TOGGLE === */
|
||||
.mode-toggle-btn {
|
||||
position: absolute;
|
||||
bottom: var(--space-3);
|
||||
right: var(--space-3);
|
||||
pointer-events: auto;
|
||||
background: rgba(10, 15, 40, 0.7);
|
||||
border: 1px solid var(--color-primary);
|
||||
color: var(--color-primary);
|
||||
padding: 6px 14px;
|
||||
font-family: var(--font-display);
|
||||
font-size: 10px;
|
||||
font-weight: 600;
|
||||
letter-spacing: 0.12em;
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
backdrop-filter: blur(5px);
|
||||
transition: all var(--transition-ui);
|
||||
z-index: 20;
|
||||
}
|
||||
|
||||
.mode-toggle-btn:hover {
|
||||
background: var(--color-primary);
|
||||
color: var(--color-bg);
|
||||
box-shadow: 0 0 15px var(--color-primary);
|
||||
}
|
||||
|
||||
.mode-toggle-btn .mode-icon {
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.mode-toggle-btn[data-mode="operator"] {
|
||||
border-color: var(--color-gold);
|
||||
color: var(--color-gold);
|
||||
box-shadow: 0 0 8px rgba(255, 215, 0, 0.15);
|
||||
}
|
||||
|
||||
.mode-toggle-btn[data-mode="operator"]:hover {
|
||||
background: var(--color-gold);
|
||||
color: var(--color-bg);
|
||||
box-shadow: 0 0 15px var(--color-gold);
|
||||
}
|
||||
|
||||
/* Visitor mode: hide operator-only surfaces */
|
||||
body.visitor-mode .gofai-hud,
|
||||
body.visitor-mode .hud-agent-log,
|
||||
body.visitor-mode .hud-debug,
|
||||
body.visitor-mode .bannerlord-hud,
|
||||
body.visitor-mode #mem-palace-status,
|
||||
body.visitor-mode #mem-palace-controls,
|
||||
body.visitor-mode #mempalace-results,
|
||||
body.visitor-mode .mem-palace-ui,
|
||||
body.visitor-mode .mem-palace-stats,
|
||||
body.visitor-mode .ws-hud-status,
|
||||
body.visitor-mode .chat-quick-actions .operator-only,
|
||||
body.visitor-mode .nexus-footer {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
/* Visitor mode: simplify controls hint */
|
||||
body.visitor-mode .hud-controls {
|
||||
font-size: var(--text-xs);
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* Visitor mode: cleaner chat */
|
||||
body.visitor-mode .chat-panel {
|
||||
width: 320px;
|
||||
max-height: 300px;
|
||||
}
|
||||
|
||||
/* Operator badge in operator mode */
|
||||
body:not(.visitor-mode) .hud-controls::after {
|
||||
content: ' OPERATOR';
|
||||
color: var(--color-gold);
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.1em;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user