Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>CAPT Memory Palace - Live</title> | |
| <link rel="icon" type="image/svg+xml" href="assets/favicon.svg"> | |
| <link rel="stylesheet" href="css/styles.css"> | |
| <style> | |
| @import url('https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@300;400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap'); | |
| * { margin: 0; padding: 0; box-sizing: border-box; } | |
| body { | |
| font-family: 'Space Grotesk', sans-serif; | |
| background: #0a0a12; | |
| color: #fff; | |
| overflow: hidden; | |
| } | |
| #canvas-container { | |
| position: fixed; | |
| top: 0; left: 0; | |
| width: 100%; height: 100%; | |
| z-index: 1; | |
| } | |
| /* Narrative Overlay - Top Center */ | |
| #narrative-overlay { | |
| position: fixed; | |
| top: 20px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| z-index: 50; | |
| text-align: center; | |
| pointer-events: none; | |
| width: 90%; | |
| max-width: 600px; | |
| } | |
| #phase-indicator { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 8px; | |
| padding: 8px 16px; | |
| background: rgba(0,0,0,0.7); | |
| border: 1px solid rgba(139,92,246,0.4); | |
| border-radius: 20px; | |
| font-size: 12px; | |
| text-transform: uppercase; | |
| letter-spacing: 1px; | |
| margin-bottom: 12px; | |
| backdrop-filter: blur(10px); | |
| } | |
| #phase-dot { | |
| width: 8px; height: 8px; | |
| border-radius: 50%; | |
| background: #6b7280; | |
| animation: pulse 1.5s ease-in-out infinite; | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { opacity: 0.5; transform: scale(1); } | |
| 50% { opacity: 1; transform: scale(1.2); } | |
| } | |
| #narrative-text { | |
| font-size: 24px; | |
| font-weight: 500; | |
| color: #fff; | |
| text-shadow: 0 2px 20px rgba(139,92,246,0.5); | |
| background: rgba(0,0,0,0.6); | |
| padding: 16px 24px; | |
| border-radius: 16px; | |
| backdrop-filter: blur(10px); | |
| border: 1px solid rgba(255,255,255,0.1); | |
| } | |
| #narrative-text .highlight { | |
| color: #8b5cf6; | |
| font-weight: 600; | |
| } | |
| /* Progress Bar */ | |
| #progress-container { | |
| position: fixed; | |
| top: 0; | |
| left: 0; | |
| width: 100%; | |
| height: 3px; | |
| background: rgba(255,255,255,0.1); | |
| z-index: 60; | |
| } | |
| #progress-bar { | |
| height: 100%; | |
| width: 0%; | |
| background: linear-gradient(90deg, #8b5cf6, #ec4899); | |
| transition: width 0.3s ease; | |
| } | |
| /* Query Input - Bottom Center */ | |
| #query-panel { | |
| position: fixed; | |
| bottom: 30px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| z-index: 50; | |
| display: flex; | |
| gap: 12px; | |
| width: 90%; | |
| max-width: 600px; | |
| } | |
| #query-input { | |
| flex: 1; | |
| padding: 16px 20px; | |
| background: rgba(0,0,0,0.8); | |
| border: 1px solid rgba(139,92,246,0.4); | |
| border-radius: 12px; | |
| color: #fff; | |
| font-family: 'Space Grotesk', sans-serif; | |
| font-size: 16px; | |
| outline: none; | |
| backdrop-filter: blur(10px); | |
| } | |
| #query-input:focus { | |
| border-color: rgba(139,92,246,0.8); | |
| box-shadow: 0 0 20px rgba(139,92,246,0.3); | |
| } | |
| #query-input::placeholder { | |
| color: #6b7280; | |
| } | |
| #submit-btn { | |
| padding: 16px 24px; | |
| background: linear-gradient(135deg, #8b5cf6, #ec4899); | |
| border: none; | |
| border-radius: 12px; | |
| color: #fff; | |
| font-family: 'Space Grotesk', sans-serif; | |
| font-size: 14px; | |
| font-weight: 600; | |
| cursor: pointer; | |
| transition: transform 0.2s, box-shadow 0.2s; | |
| } | |
| #submit-btn:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 8px 24px rgba(139,92,246,0.4); | |
| } | |
| /* Stats Panel - Top Left */ | |
| #stats-panel { | |
| position: fixed; | |
| top: 20px; | |
| left: 20px; | |
| z-index: 50; | |
| background: rgba(0,0,0,0.7); | |
| border: 1px solid rgba(139,92,246,0.3); | |
| border-radius: 12px; | |
| padding: 16px; | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 12px; | |
| backdrop-filter: blur(10px); | |
| } | |
| .stat-row { | |
| display: flex; | |
| justify-content: space-between; | |
| gap: 20px; | |
| margin-bottom: 8px; | |
| } | |
| .stat-row:last-child { margin-bottom: 0; } | |
| .stat-label { color: #6b7280; } | |
| .stat-value { color: #8b5cf6; } | |
| /* Control Panel - Top Right */ | |
| #control-panel { | |
| position: fixed; | |
| top: 20px; | |
| right: 20px; | |
| z-index: 50; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 8px; | |
| } | |
| .ctrl-btn { | |
| padding: 10px 16px; | |
| background: rgba(0,0,0,0.7); | |
| border: 1px solid rgba(255,255,255,0.1); | |
| color: #fff; | |
| border-radius: 8px; | |
| font-size: 13px; | |
| cursor: pointer; | |
| backdrop-filter: blur(10px); | |
| transition: all 0.2s; | |
| } | |
| .ctrl-btn:hover { | |
| background: rgba(139,92,246,0.3); | |
| border-color: rgba(139,92,246,0.5); | |
| } | |
| .ctrl-btn.active { | |
| background: rgba(139,92,246,0.4); | |
| border-color: #8b5cf6; | |
| } | |
| /* Onboarding - Centered overlay */ | |
| #onboarding { | |
| position: fixed; | |
| top: 0; left: 0; | |
| width: 100%; height: 100%; | |
| z-index: 100; | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| background: linear-gradient(135deg, #0a0a12 0%, #1a1025 50%, #0d0d18 100%); | |
| opacity: 1; | |
| transition: opacity 0.8s ease-out, visibility 0.8s; | |
| } | |
| #onboarding.hidden { | |
| opacity: 0; | |
| visibility: hidden; | |
| pointer-events: none; | |
| } | |
| .onboarding-content { | |
| text-align: center; | |
| max-width: 600px; | |
| padding: 40px; | |
| } | |
| .logo { | |
| font-size: 72px; | |
| margin-bottom: 16px; | |
| } | |
| .tagline { | |
| font-size: 32px; | |
| font-weight: 300; | |
| color: #e0d6eb; | |
| margin-bottom: 16px; | |
| line-height: 1.3; | |
| } | |
| .tagline-accent { | |
| background: linear-gradient(135deg, #8b5cf6, #ec4899); | |
| -webkit-background-clip: text; | |
| -webkit-text-fill-color: transparent; | |
| background-clip: text; | |
| } | |
| .description { | |
| font-size: 16px; | |
| color: #6b7280; | |
| margin-bottom: 40px; | |
| line-height: 1.6; | |
| } | |
| .cta-buttons { | |
| display: flex; | |
| gap: 16px; | |
| justify-content: center; | |
| flex-wrap: wrap; | |
| } | |
| .btn { | |
| padding: 16px 32px; | |
| font-size: 16px; | |
| font-weight: 600; | |
| border: none; | |
| border-radius: 12px; | |
| cursor: pointer; | |
| transition: all 0.3s ease; | |
| font-family: inherit; | |
| } | |
| .btn-primary { | |
| background: linear-gradient(135deg, #8b5cf6, #ec4899); | |
| color: #fff; | |
| box-shadow: 0 4px 24px rgba(139,92,246,0.4); | |
| } | |
| .btn-primary:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 8px 32px rgba(139,92,246,0.5); | |
| } | |
| .btn-secondary { | |
| background: rgba(255,255,255,0.08); | |
| color: #e0d6eb; | |
| border: 1px solid rgba(255,255,255,0.12); | |
| } | |
| .btn-secondary:hover { | |
| background: rgba(255,255,255,0.12); | |
| } | |
| /* Demo animation */ | |
| .demo-grid { | |
| display: grid; | |
| grid-template-columns: repeat(4, 1fr); | |
| gap: 12px; | |
| margin-bottom: 32px; | |
| } | |
| .demo-module { | |
| padding: 16px; | |
| background: rgba(255,255,255,0.03); | |
| border-radius: 12px; | |
| border: 1px solid rgba(139,92,246,0.2); | |
| animation: module-pulse 2s ease-in-out infinite; | |
| } | |
| .demo-module:nth-child(1) { animation-delay: 0s; } | |
| .demo-module:nth-child(2) { animation-delay: 0.3s; } | |
| .demo-module:nth-child(3) { animation-delay: 0.6s; } | |
| .demo-module:nth-child(4) { animation-delay: 0.9s; } | |
| @keyframes module-pulse { | |
| 0%, 100% { border-color: rgba(139,92,246,0.2); transform: scale(1); } | |
| 50% { border-color: rgba(139,92,246,0.8); transform: scale(1.02); } | |
| } | |
| .demo-module-name { | |
| font-family: 'JetBrains Mono', monospace; | |
| font-size: 14px; | |
| color: #8b5cf6; | |
| } | |
| .demo-module-status { | |
| font-size: 11px; | |
| color: #6b7280; | |
| margin-top: 4px; | |
| } | |
| /* Keyboard hints */ | |
| .keyboard-hints { | |
| position: fixed; | |
| bottom: 100px; | |
| left: 50%; | |
| transform: translateX(-50%); | |
| display: flex; | |
| gap: 16px; | |
| font-size: 11px; | |
| color: #4b5563; | |
| } | |
| .kbd { | |
| padding: 4px 8px; | |
| background: rgba(255,255,255,0.1); | |
| border-radius: 4px; | |
| font-family: 'JetBrains Mono', monospace; | |
| } | |
| /* Message history */ | |
| #message-history { | |
| position: fixed; | |
| bottom: 100px; | |
| left: 20px; | |
| z-index: 40; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 8px; | |
| max-width: 300px; | |
| } | |
| .history-msg { | |
| padding: 8px 12px; | |
| background: rgba(0,0,0,0.5); | |
| border-radius: 8px; | |
| font-size: 12px; | |
| color: #6b7280; | |
| border-left: 2px solid #8b5cf6; | |
| animation: slideIn 0.3s ease-out; | |
| } | |
| @keyframes slideIn { | |
| from { opacity: 0; transform: translateX(-10px); } | |
| to { opacity: 1; transform: translateX(0); } | |
| } | |
| @keyframes pulse { | |
| 0%, 100% { opacity: 1; } | |
| 50% { opacity: 0.5; } | |
| } | |
| .modal-content { | |
| animation: modalIn 0.3s ease; | |
| } | |
| @keyframes modalIn { | |
| from { transform: scale(0.9); opacity: 0; } | |
| to { transform: scale(1); opacity: 1; } | |
| } | |
| /* Skip link */ | |
| .skip-link { | |
| position: absolute; | |
| top: -40px; | |
| left: 0; | |
| background: #8b5cf6; | |
| color: white; | |
| padding: 8px; | |
| z-index: 100; | |
| border-radius: 0 0 8px 8px; | |
| transition: top 0.3s; | |
| } | |
| .skip-link:focus { | |
| top: 0; | |
| } | |
| /* Focus visible outlines */ | |
| *:focus-visible { | |
| outline: 2px solid #8b5cf6; | |
| outline-offset: 2px; | |
| } | |
| /* Button focus states */ | |
| .ctrl-btn:focus-visible { | |
| background: rgba(139, 92, 246, 0.2); | |
| } | |
| /* Touch-friendly minimum size */ | |
| .ctrl-btn { | |
| min-height: 44px; | |
| min-width: 44px; | |
| } | |
| /* Reduce motion for accessibility */ | |
| @media (prefers-reduced-motion: reduce) { | |
| * { | |
| animation-duration: 0.001ms ; | |
| animation-iteration-count: 1 ; | |
| transition-duration: 0.001ms ; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body><a href="#main-content" class="skip-link">Skip to main content</a> | |
| <!-- Loading Overlay --> | |
| <div id="loading-overlay" style="position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: #0a0a12; z-index: 9999; display: flex; align-items: center; justify-content: center; flex-direction: column; transition: opacity 0.5s;"> | |
| <div style="font-size: 48px; margin-bottom: 16px;">π§ </div> | |
| <div style="color: #8b5cf6; font-size: 18px; font-family: 'Space Grotesk', sans-serif;">Loading Memory Palace...</div> | |
| <div style="color: #666; font-size: 12px; margin-top: 8px;">Initializing cognitive architecture</div> | |
| </div> | |
| <!-- Progress Bar --> | |
| <div id="progress-container"> | |
| <div id="progress-bar"></div> | |
| </div> | |
| <!-- Narrative Overlay --> | |
| <div id="narrative-overlay" aria-live="polite" aria-atomic="true"> | |
| <div id="phase-indicator"> | |
| <span id="phase-dot"></span> | |
| <span id="phase-name">READY</span> | |
| </div> | |
| <div id="narrative-text">Enter a query below to begin...</div> | |
| </div> | |
| <!-- Stats Panel --> | |
| <div id="stats-panel"> | |
| <div class="stat-row"> | |
| <span class="stat-label">Sweep</span> | |
| <span class="stat-value" id="stat-sweep">β</span> | |
| </div> | |
| <div class="stat-row"> | |
| <span class="stat-label">Active</span> | |
| <span class="stat-value" id="stat-modules">0</span> | |
| </div> | |
| <div class="stat-row"> | |
| <span class="stat-label">Confidence</span> | |
| <span class="stat-value" id="stat-confidence">β</span> | |
| </div> | |
| <div class="stat-row"> | |
| <span class="stat-label">Input</span> | |
| <span class="stat-value" id="stat-input">β</span> | |
| </div> | |
| </div> | |
| <!-- Control Panel - Top Right --> | |
| <div id="control-panel"> | |
| <button class="ctrl-btn" onclick="toggleCaptPanel()" aria-label="Show/hide CAPT connection panel">π CAPT</button> | |
| <button class="ctrl-btn" id="btn-tour" aria-label="Toggle cinematic tour" onclick="toggleTour()">π¬ Tour</button> | |
| <button class="ctrl-btn" id="btn-audio" aria-label="Toggle sound" onclick="toggleAudio()">π Sound</button> | |
| <button class="ctrl-btn" onclick="openJsonEditor()">π Edit</button> | |
| <button class="ctrl-btn" onclick="showBubbleViewer()">π«§ Modules</button> | |
| <button class="ctrl-btn" id="btn-fullscreen" aria-label="Toggle fullscreen mode" onclick="toggleFullscreen()">βΆ Full</button> | |
| </div> | |
| <div id="canvas-container"></div> | |
| <!-- Onboarding --> | |
| <div id="onboarding"> | |
| <div class="onboarding-content"> | |
| <div class="logo">π§ </div> | |
| <h1 class="tagline">Watch <span class="tagline-accent">AI cognition</span> unfold in real-time</h1> | |
| <p class="description"> | |
| See how CAPT processes your query: from pattern recognition to consensus building. | |
| Watch modules activate, compete, and synthesize in 3D. | |
| </p> | |
| <div class="demo-grid"> | |
| <div class="demo-module"> | |
| <div class="demo-module-name">PULSE</div> | |
| <div class="demo-module-status">Processing input...</div> | |
| </div> | |
| <div class="demo-module"> | |
| <div class="demo-module-name">NEDA</div> | |
| <div class="demo-module-status">Finding patterns</div> | |
| </div> | |
| <div class="demo-module"> | |
| <div class="demo-module-name">QIPC</div> | |
| <div class="demo-module-status">Building consensus</div> | |
| </div> | |
| <div class="demo-module"> | |
| <div class="demo-module-name">NDS</div> | |
| <div class="demo-module-status">Generating output</div> | |
| </div> | |
| </div> | |
| <div class="cta-buttons"> | |
| <button class="btn btn-primary" onclick="startDemo()">βΆοΈ Watch Demo</button> | |
| <button class="btn btn-secondary" onclick="skipOnboarding()">Skip to Editor</button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Query Input --> | |
| <div id="query-panel"> | |
| <select id="query-presets" onchange="loadPresetQuery()" style="padding: 16px; background: rgba(0,0,0,0.8); border: 1px solid rgba(139,92,246,0.4); border-radius: 12px; color: #8b5cf6; font-family: 'Space Grotesk', sans-serif; font-size: 14px; outline: none;"> | |
| <option value="">Try a preset...</option> | |
| <option value="What is consciousness?">What is consciousness?</option> | |
| <option value="Explain quantum entanglement">Explain quantum entanglement</option> | |
| <option value="Write a poem about AI">Write a poem about AI</option> | |
| <option value="How does memory work?">How does memory work?</option> | |
| <option value="What is the meaning of life?">What is the meaning of life?</option> | |
| <option value="Describe the taste of strawberries">Describe the taste of strawberries</option> | |
| <option value="Why do we dream?">Why do we dream?</option> | |
| </select> | |
| <input type="text" id="query-input" placeholder="Or type your own question..." autocomplete="off"> | |
| <button id="submit-btn" onclick="submitQuery()">β</button> | |
| </div> | |
| <!-- Keyboard Hints --> | |
| <div class="keyboard-hints"> | |
| <span><span class="kbd">Space</span> Tour</span> | |
| <span><span class="kbd">WASD</span> Move</span> | |
| <span><span class="kbd">M</span> Sound</span> | |
| <span><span class="kbd">R</span> Reset</span> | |
| </div> | |
| <!-- Message History --> | |
| <div id="message-history"></div> | |
| <!-- JSON Editor Modal --> | |
| <div id="json-editor-modal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.9); z-index: 200; align-items: center; justify-content: center;"> | |
| <div style="width: 90%; max-width: 800px; max-height: 80vh; background: #12121a; border: 1px solid rgba(139,92,246,0.4); border-radius: 16px; display: flex; flex-direction: column; overflow: hidden;"> | |
| <div style="padding: 16px 20px; border-bottom: 1px solid rgba(255,255,255,0.1); display: flex; justify-content: space-between; align-items: center;"> | |
| <h3 style="margin: 0; color: #8b5cf6;">Palace JSON Editor</h3> | |
| <button onclick="closeJsonEditor()" style="background: none; border: none; color: #888; font-size: 24px; cursor: pointer;">×</button> | |
| </div> | |
| <textarea id="json-editor-textarea" style="flex: 1; background: #0a0a12; color: #e0d6eb; border: none; padding: 20px; font-family: 'JetBrains Mono', monospace; font-size: 13px; resize: none; min-height: 400px;"></textarea> | |
| <div style="padding: 16px 20px; border-top: 1px solid rgba(255,255,255,0.1); display: flex; gap: 12px; justify-content: flex-end;"> | |
| <button onclick="closeJsonEditor()" style="padding: 10px 20px; background: rgba(255,255,255,0.1); border: 1px solid rgba(255,255,255,0.2); color: #fff; border-radius: 8px; cursor: pointer;">Cancel</button> | |
| <button onclick="applyJsonEditor()" style="padding: 10px 20px; background: linear-gradient(135deg, #8b5cf6, #ec4899); border: none; color: #fff; border-radius: 8px; cursor: pointer;">Apply</button> | |
| <button onclick="downloadJson()" style="padding: 10px 20px; background: rgba(139,92,246,0.2); border: 1px solid rgba(139,92,246,0.4); color: #8b5cf6; border-radius: 8px; cursor: pointer;">Download</button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Bubble Viewer Modal --> | |
| <div id="bubble-viewer-modal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.95); z-index: 200; align-items: center; justify-content: center;"> | |
| <div style="width: 90%; max-width: 900px; max-height: 80vh; background: #12121a; border: 1px solid rgba(139,92,246,0.4); border-radius: 16px; display: flex; flex-direction: column; overflow: hidden;"> | |
| <div style="padding: 16px 20px; border-bottom: 1px solid rgba(255,255,255,0.1); display: flex; justify-content: space-between; align-items: center;"> | |
| <h3 style="margin: 0; color: #d946ef;">CAPT Module States</h3> | |
| <button onclick="closeBubbleViewer()" style="background: none; border: none; color: #888; font-size: 24px; cursor: pointer;">×</button> | |
| </div> | |
| <div id="bubble-content" style="flex: 1; padding: 20px; overflow-y: auto; display: grid; grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 16px;"></div> | |
| </div> | |
| </div> | |
| <!-- Three.js (bundled locally for HF Spaces compatibility) --> | |
| <script src="vendor/three.min.js"></script> | |
| <script src="js/narrative.js"></script> | |
| <script src="js/simulator.js"></script> | |
| <script src="js/audio.js"></script> | |
| <script src="js/tour.js"></script> | |
| <script src="js/api.js"></script> | |
| <script src="js/main.js"></script> | |
| <script> | |
| // Global state | |
| let isDemoRunning = false; | |
| let audioEnabled = false; | |
| let currentNarrative = null; | |
| // Global state for features | |
| window.captActivity = 0; | |
| window.echoMemories = []; | |
| window.sweepEvents = []; | |
| // Module info database | |
| const MODULE_INFO = { | |
| 'PULSE': { name: 'Language Processing', desc: 'Tokenizes and processes raw input, breaking down queries into semantic units.', color: '#8b5cf6' }, | |
| 'NEDA': { name: 'Pattern Recognition', desc: 'Matches input against learned patterns, finding relevant associations.', color: '#ec4899' }, | |
| 'HMC': { name: 'Memory Encoding', desc: 'Encodes new information into the memory palace using spatial navigation.', color: '#f59e0b' }, | |
| 'QIPC': { name: 'Consensus Builder', desc: 'Reconciles competing interpretations through weighted voting.', color: '#22d3ee' }, | |
| 'ECHO': { name: 'Long-term Memory', desc: 'Retrieves relevant memories and synthesizes with current context.', color: '#4ade80' }, | |
| 'NDS': { name: 'Output Synthesis', desc: 'Generates final coherent output from consensus pathways.', color: '#60a5fa' }, | |
| 'BARK': { name: 'Security Filter', desc: 'Validates output against safety guidelines and policies.', color: '#f472b6' }, | |
| 'SPAR': { name: 'Sparse Retrieval', desc: 'Fast approximate memory lookup using embedding similarity.', color: '#a78bfa' } | |
| }; | |
| // Show module detail popup | |
| function showModulePopup(moduleId) { | |
| const info = MODULE_INFO[moduleId] || { name: moduleId, desc: 'Unknown module', color: '#888' }; | |
| document.getElementById('popup-title').textContent = moduleId + ' - ' + info.name; | |
| document.getElementById('popup-title').style.color = info.color; | |
| document.getElementById('popup-desc').textContent = info.desc; | |
| document.getElementById('popup-status').textContent = Math.random() > 0.3 ? 'Active' : 'Idle'; | |
| document.getElementById('popup-activations').textContent = Math.floor(Math.random() * 1000); | |
| document.getElementById('popup-sweep').textContent = '#' + Math.floor(Math.random() * 10000); | |
| document.getElementById('module-popup').style.display = 'block'; | |
| } | |
| function closeModulePopup() { | |
| document.getElementById('module-popup').style.display = 'none'; | |
| } | |
| // Theme switching | |
| function setTheme(themeName) { | |
| CONFIG.theme = themeName; | |
| applyThemeColors(themeName); | |
| repositionRooms(themeName); | |
| console.log('[Palace] Theme:', themeName); | |
| } | |
| function applyThemeColors(theme) { | |
| const colors = { | |
| cinematic: { bg: 0x0a0a12, primary: 0x8b5cf6, secondary: 0xec4899 }, | |
| neural: { bg: 0x0f172a, primary: 0xec4899, secondary: 0x8b5cf6 }, | |
| radial: { bg: 0x071013, primary: 0x22d3ee, secondary: 0x4ade80 }, | |
| grid: { bg: 0x0a0f0a, primary: 0x4ade80, secondary: 0x22d3ee } | |
| }; | |
| const c = colors[theme] || colors.cinematic; | |
| if (scene) scene.background = new THREE.Color(c.bg); | |
| } | |
| function repositionRooms(theme) { | |
| if (!palaceRooms || palaceRooms.length === 0) return; | |
| const layouts = { | |
| cinematic: [{x:0,y:0,z:0}, {x:3,y:1,z:-2}, {x:-3,y:1,z:-2}, {x:0,y:2,z:-4}, {x:0,y:-1,z:-3}, {x:2,y:0,z:-5}, {x:-2,y:0,z:-5}, {x:0,y:1,z:-6}], | |
| neural: [{x:0,y:0,z:0}, {x:2,y:0,z:-2}, {x:-2,y:0,z:-2}, {x:1,y:2,z:-3}, {x:-1,y:2,z:-3}, {x:0,y:-2,z:-3}, {x:3,y:-1,z:-4}, {x:-3,y:-1,z:-4}], | |
| radial: [{x:0,y:0,z:0}, {x:4,y:0,z:0}, {x:-4,y:0,z:0}, {x:0,y:4,z:-2}, {x:0,y:-4,z:-2}, {x:2.8,y:2.8,z:-3}, {x:-2.8,y:2.8,z:-3}, {x:2.8,y:-2.8,z:-3}], | |
| grid: [{x:-3,y:0,z:0}, {x:0,y:0,z:0}, {x:3,y:0,z:0}, {x:-3,y:0,z:-3}, {x:0,y:0,z:-3}, {x:3,y:0,z:-3}, {x:-3,y:0,z:-6}, {x:0,y:0,z:-6}] | |
| }; | |
| const layout = layouts[theme] || layouts.cinematic; | |
| palaceRooms.forEach((room, i) => { | |
| if (layout[i]) { | |
| room.position.set(layout[i].x, layout[i].y, layout[i].z); | |
| } | |
| }); | |
| } | |
| // JSON Editor functions | |
| function openJsonEditor() { | |
| const config = { schemaVersion: "3.0.0", name: "CAPT Cognitive Architecture", theme: CONFIG.theme, rooms: [] }; | |
| palaceRooms.forEach(r => { | |
| if (r.userData && r.userData.roomId) { | |
| config.rooms.push({ id: r.userData.roomId, position: { x: r.position.x, y: r.position.y, z: r.position.z } }); | |
| } | |
| }); | |
| document.getElementById('json-input').value = JSON.stringify(config, null, 2); | |
| document.getElementById('json-editor').style.display = 'block'; | |
| } | |
| function closeJsonEditor() { | |
| document.getElementById('json-editor').style.display = 'none'; | |
| } | |
| function applyJsonConfig() { | |
| try { | |
| const config = JSON.parse(document.getElementById('json-input').value); | |
| if (config.theme) setTheme(config.theme); | |
| alert('Config applied! (Visual update requires restart in demo mode)'); | |
| } catch(e) { | |
| alert('Invalid JSON: ' + e.message); | |
| } | |
| } | |
| function exportJsonConfig() { | |
| const config = document.getElementById('json-input').value; | |
| const blob = new Blob([config], { type: 'application/json' }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = 'palace-config.json'; | |
| a.click(); | |
| } | |
| function importJsonConfig() { | |
| const input = document.createElement('input'); | |
| input.type = 'file'; | |
| input.accept = '.json'; | |
| input.onchange = (e) => { | |
| const file = e.target.files[0]; | |
| const reader = new FileReader(); | |
| reader.onload = (ev) => { | |
| document.getElementById('json-input').value = ev.target.result; | |
| }; | |
| reader.readAsText(file); | |
| }; | |
| input.click(); | |
| } | |
| // Toggle panels | |
| function toggleThemePanel() { | |
| const panel = document.getElementById('theme-panel'); | |
| panel.style.display = panel.style.display === 'none' ? 'block' : 'none'; | |
| } | |
| function toggleEchoPanel() { | |
| const panel = document.getElementById('echo-panel'); | |
| panel.style.display = panel.style.display === 'none' ? 'block' : 'none'; | |
| if (panel.style.display === 'block') refreshEchoMemories(); | |
| } | |
| // CAPT Connection functions | |
| let captApiConnected = false; | |
| function toggleCaptPanel() { | |
| const panel = document.getElementById('capt-panel'); | |
| panel.style.display = panel.style.display === 'none' ? 'block' : 'none'; | |
| } | |
| function connectToCAPT() { | |
| const endpoint = document.getElementById('capt-endpoint').value; | |
| const token = document.getElementById('capt-token').value; | |
| if (!endpoint) { | |
| document.getElementById('capt-status').innerHTML = 'Status: <span style="color:#ef4444">Please enter endpoint</span>'; | |
| return; | |
| } | |
| CAPT_API.configure({ endpoint, token }); | |
| CAPT_API.test().then(ok => { | |
| if (ok) { | |
| captApiConnected = true; | |
| document.getElementById('capt-status').innerHTML = 'Status: <span style="color:#4ade80">Connected β</span>'; | |
| // Start polling for state | |
| CAPT_API.startPolling(2000, (state) => { | |
| if (state && state.modules) { | |
| window.captActivity = 0.8; | |
| updatePalaceFromCAPT(state); | |
| } | |
| }); | |
| } else { | |
| document.getElementById('capt-status').innerHTML = 'Status: <span style="color:#ef4444">Connection failed</span>'; | |
| } | |
| }); | |
| } | |
| function disconnectFromCAPT() { | |
| CAPT_API.stopPolling(); | |
| captApiConnected = false; | |
| document.getElementById('capt-status').innerHTML = 'Status: Disconnected'; | |
| } | |
| function updatePalaceFromCAPT(state) { | |
| // Update rooms based on CAPT state | |
| if (state.modules && palaceRooms) { | |
| state.modules.forEach((mod, i) => { | |
| if (palaceRooms[i]) { | |
| const scale = 1 + (mod.activation || 0.5) * 0.5; | |
| palaceRooms[i].scale.setScalar(scale); | |
| } | |
| }); | |
| } | |
| } | |
| function toggleSweepTicker() { | |
| const ticker = document.getElementById('sweep-ticker'); | |
| ticker.style.display = ticker.style.display === 'none' ? 'block' : 'none'; | |
| if (ticker.style.display === 'block') startSweepTicker(); | |
| } | |
| // Echo memories (simulated for demo) | |
| function refreshEchoMemories() { | |
| const container = document.getElementById('echo-memories'); | |
| const memories = [ | |
| { time: '2m ago', text: 'Query: "What is consciousness?" β Pattern matched: philosophy_001' }, | |
| { time: '5m ago', text: 'Memory encoded: neural_pathway_42' }, | |
| { time: '8m ago', text: 'Consensus reached: QIPC score 0.87' }, | |
| { time: '12m ago', text: 'Query: "Explain quantum entanglement" β routed to NEDA' }, | |
| { time: '15m ago', text: 'Security filter passed: BARK clean' } | |
| ]; | |
| container.innerHTML = memories.map(m => | |
| `<div style="padding: 8px; border-bottom: 1px solid rgba(139,92,246,0.2);"> | |
| <span style="color: #666; font-size: 10px;">${m.time}</span> | |
| <div>${m.text}</div> | |
| </div>` | |
| ).join(''); | |
| } | |
| // Sweep ticker (simulated) | |
| function startSweepTicker() { | |
| const container = document.getElementById('sweep-events'); | |
| const events = [ | |
| 'PULSE: Processing input tokens...', | |
| 'NEDA: Found 3 matching patterns', | |
| 'HMC: Encoding memory trace', | |
| 'QIPC: Voting on interpretations', | |
| 'ECHO: Retrieving related memories', | |
| 'NDS: Synthesizing output', | |
| 'BARK: Security check passed' | |
| ]; | |
| setInterval(() => { | |
| const event = events[Math.floor(Math.random() * events.length)]; | |
| const time = new Date().toLocaleTimeString(); | |
| const div = document.createElement('div'); | |
| div.innerHTML = `<span style="color: #666;">${time}</span> ${event}`; | |
| div.style.padding = '5px 0'; | |
| container.insertBefore(div, container.firstChild); | |
| if (container.children.length > 10) container.lastChild.remove(); | |
| }, 2000); | |
| } | |
| // Initialize on load - main.js handles scene + config | |
| document.addEventListener('DOMContentLoaded', () => { | |
| // main.js will call initScene() and load config automatically | |
| // Just set up UI event handlers here | |
| // Set up query input | |
| const queryInput = document.getElementById('query-input'); | |
| queryInput.addEventListener('keypress', (e) => { | |
| if (e.key === 'Enter') submitQuery(); | |
| }); | |
| // Keyboard shortcuts | |
| document.addEventListener('keydown', (e) => { | |
| if (e.code === 'Space' && !e.target.matches('input')) { | |
| e.preventDefault(); | |
| toggleTour(); | |
| } | |
| if (e.key.toLowerCase() === 'm') { | |
| toggleAudio(); | |
| } | |
| }); | |
| }); | |
| // Start demo mode | |
| function startDemo() { | |
| document.getElementById('onboarding').classList.add('hidden'); | |
| isDemoRunning = true; | |
| // Start cognitive simulator | |
| CognitiveSimulator.start((state) => { | |
| updateVisualization(state); | |
| updateNarrative(state); | |
| updateStats(state); | |
| }); | |
| // Start audio (requires user interaction) | |
| AudioEngine.init(); | |
| } | |
| function skipOnboarding() { | |
| document.getElementById('onboarding').classList.add('hidden'); | |
| initScene(); | |
| } | |
| // Submit a query | |
| function submitQuery() { | |
| const input = document.getElementById('query-input'); | |
| const query = input.value.trim(); | |
| if (!query) return; | |
| // Hide onboarding if visible | |
| document.getElementById('onboarding').classList.add('hidden'); | |
| // Submit to simulator | |
| CognitiveSimulator.submitQuery(query); | |
| // Clear input | |
| input.value = ''; | |
| // Reset preset dropdown | |
| document.getElementById('query-presets').value = ''; | |
| } | |
| // Load preset query | |
| function loadPresetQuery() { | |
| const select = document.getElementById('query-presets'); | |
| const input = document.getElementById('query-input'); | |
| const value = select.value; | |
| if (value) { | |
| input.value = value; | |
| input.focus(); | |
| } | |
| } | |
| // Update 3D visualization from cognitive state | |
| function updateVisualization(state) { | |
| if (!window.loadPalace) return; | |
| // Convert cognitive state to palace format | |
| const config = { | |
| schemaVersion: "3.0.0", | |
| name: "Live Cognitive State", | |
| rooms: state.modules.map((m, i) => ({ | |
| id: m.id, | |
| label: Narrative.moduleNames[m.id] || m.id, | |
| type: m.type || 'processing', | |
| position: { | |
| x: Math.cos(i * Math.PI * 2 / Math.max(state.modules.length, 1)) * 5, | |
| y: m.activation * 2, | |
| z: Math.sin(i * Math.PI * 2 / Math.max(state.modules.length, 1)) * 5 | |
| }, | |
| color: m.color || '#8b5cf6', | |
| capacity: Math.round(m.activation * 100) | |
| })), | |
| connections: state.modules.slice(0, -1).map((m, i) => ({ | |
| from: m.id, | |
| to: state.modules[i + 1]?.id, | |
| type: 'forward' | |
| })).filter(c => c.to) | |
| }; | |
| window.loadPalace(config); | |
| } | |
| // Update narrative overlay | |
| function updateNarrative(state) { | |
| const narrativeState = Narrative.update(state.modules); | |
| currentNarrative = narrativeState; | |
| // Update phase indicator | |
| const phaseName = document.getElementById('phase-name'); | |
| const phaseDot = document.getElementById('phase-dot'); | |
| const phaseColor = narrativeState.phase.color; | |
| phaseName.textContent = narrativeState.phase.title; | |
| phaseName.style.color = phaseColor; | |
| phaseDot.style.background = phaseColor; | |
| // Update narrative text | |
| const narrativeText = document.getElementById('narrative-text'); | |
| narrativeText.innerHTML = narrativeState.message; | |
| // Update progress bar | |
| const progressBar = document.getElementById('progress-bar'); | |
| progressBar.style.width = `${narrativeState.progress}%`; | |
| progressBar.style.background = `linear-gradient(90deg, ${phaseColor}, #ec4899)`; | |
| // Play audio for phase changes | |
| if (audioEnabled) { | |
| AudioEngine.playPhaseSound(narrativeState.phase.title.toLowerCase()); | |
| // Play module sounds | |
| state.modules.forEach(m => { | |
| if (m.activation > 0.7) { | |
| AudioEngine.playModuleActivation(m.id, m.activation); | |
| } | |
| }); | |
| // Consensus sound | |
| if (narrativeState.phase.title === 'Consensus') { | |
| AudioEngine.playConsensusSound(); | |
| } | |
| } | |
| // Update message history | |
| addToHistory(narrativeState.message); | |
| } | |
| // Update stats panel | |
| function updateStats(state) { | |
| document.getElementById('stat-sweep').textContent = state.sweep || 'β'; | |
| document.getElementById('stat-modules').textContent = state.modules.length; | |
| const avgConfidence = state.modules.reduce((sum, m) => sum + (m.confidence || 0), 0) / Math.max(state.modules.length, 1); | |
| document.getElementById('stat-confidence').textContent = avgConfidence > 0 ? `${Math.round(avgConfidence * 100)}%` : 'β'; | |
| const inputPreview = state.input ? (state.input.length > 15 ? state.input.slice(0, 15) + '...' : state.input) : 'β'; | |
| document.getElementById('stat-input').textContent = inputPreview; | |
| } | |
| // Add message to history | |
| function addToHistory(msg) { | |
| const history = document.getElementById('message-history'); | |
| const el = document.createElement('div'); | |
| el.className = 'history-msg'; | |
| el.textContent = msg; | |
| history.insertBefore(el, history.firstChild); | |
| // Keep only last 5 | |
| while (history.children.length > 5) { | |
| history.removeChild(history.lastChild); | |
| } | |
| } | |
| // Toggle tour | |
| function toggleTour() { | |
| if (window.Tour) { | |
| Tour.toggle(camera, { x: 0, y: 0, z: 0 }); | |
| document.getElementById('btn-tour').classList.toggle('active', Tour.isActive); | |
| } | |
| } | |
| // Toggle audio | |
| function toggleAudio() { | |
| audioEnabled = AudioEngine.toggle(); | |
| const btn = document.getElementById('btn-audio'); | |
| btn.textContent = audioEnabled ? 'π Sound' : 'π Sound'; | |
| btn.classList.toggle('active', audioEnabled); | |
| } | |
| // Toggle fullscreen | |
| function toggleFullscreen() { | |
| if (!document.fullscreenElement) { | |
| document.documentElement.requestFullscreen(); | |
| document.getElementById('btn-fullscreen').textContent = 'βΆ Exit'; | |
| } else { | |
| document.exitFullscreen(); | |
| document.getElementById('btn-fullscreen').textContent = 'βΆ Full'; | |
| } | |
| } | |
| // Bubble viewer | |
| function showBubbleViewer() { | |
| const state = CognitiveSimulator.getState(); | |
| const modal = document.getElementById('bubble-viewer-modal'); | |
| const content = document.getElementById('bubble-content'); | |
| const allModules = ['PULSE', 'NEDA', 'HMC', 'CIG', 'HDR', 'QIPC', 'ECHO', 'META', 'IMMU', 'NDS']; | |
| const activeIds = state.modules.map(m => m.id); | |
| content.innerHTML = allModules.map(m => { | |
| const active = state.modules.find(s => s.id === m); | |
| const isActive = !!active; | |
| const activation = active?.activation || 0; | |
| const colors = { | |
| processing: '#8b5cf6', | |
| memory: '#d946ef', | |
| quorum: '#ec4899', | |
| validation: '#10b981', | |
| output: '#06b6d4' | |
| }; | |
| return ` | |
| <div style="background: rgba(255,255,255,0.03); border: 1px solid ${isActive ? colors[active.type] || '#8b5cf6' : '#333'}40; border-radius: 12px; padding: 16px; text-align: center;"> | |
| <div style="font-size: 24px; margin-bottom: 8px;">${isActive ? 'π£' : 'βͺ'}</div> | |
| <div style="color: #fff; font-weight: 600; font-family: 'JetBrains Mono', monospace;">${m}</div> | |
| <div style="color: ${isActive ? colors[active.type] || '#8b5cf6' : '#666'}; font-size: 11px; margin-top: 4px;"> | |
| ${isActive ? active.type.toUpperCase() : 'DORMANT'} | |
| </div> | |
| <div style="height: 4px; background: #1a1a2e; border-radius: 2px; margin-top: 8px; overflow: hidden;"> | |
| <div style="height: 100%; width: ${activation * 100}%; background: ${isActive ? colors[active.type] || '#8b5cf6' : '#333'};"></div> | |
| </div> | |
| <div style="color: #666; font-size: 10px; margin-top: 4px;">${Math.round(activation * 100)}%</div> | |
| </div> | |
| `; | |
| }).join(''); | |
| modal.style.display = 'flex'; | |
| } | |
| function closeBubbleViewer() { | |
| document.getElementById('bubble-viewer-modal').style.display = 'none'; | |
| } | |
| // JSON Editor | |
| function openJsonEditor() { | |
| const state = CognitiveSimulator.getState(); | |
| const config = { | |
| schemaVersion: "3.0.0", | |
| name: "My Research Palace", | |
| theme: "cinematic", | |
| rooms: state.modules.map(r => ({ | |
| id: r.id, | |
| label: Narrative.moduleNames[r.id] || r.id, | |
| type: r.type, | |
| color: r.color, | |
| capacity: r.capacity | |
| })), | |
| connections: [] | |
| }; | |
| document.getElementById('json-editor-textarea').value = JSON.stringify(config, null, 2); | |
| document.getElementById('json-editor-modal').style.display = 'flex'; | |
| } | |
| function closeJsonEditor() { | |
| document.getElementById('json-editor-modal').style.display = 'none'; | |
| } | |
| function applyJsonEditor() { | |
| try { | |
| const config = JSON.parse(document.getElementById('json-editor-textarea').value); | |
| if (window.loadPalace) window.loadPalace(config); | |
| closeJsonEditor(); | |
| } catch (e) { | |
| alert('Invalid JSON: ' + e.message); | |
| } | |
| } | |
| function downloadJson() { | |
| const content = document.getElementById('json-editor-textarea').value; | |
| const blob = new Blob([content], { type: 'application/json' }); | |
| const url = URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.href = url; | |
| a.download = 'palace.json'; | |
| a.click(); | |
| URL.revokeObjectURL(url); | |
| } | |
| </script> | |
| <!-- Module Detail Popup --> | |
| <div id="module-popup" class="modal" style="display: none; position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8);"> | |
| <div class="modal-content" style="margin: 10% auto; padding: 30px; background: linear-gradient(135deg, #1a1025 0%, #0a0a12 100%); border: 1px solid rgba(139,92,246,0.4); border-radius: 16px; max-width: 500px; color: #fff;"> | |
| <span onclick="closeModulePopup()" style="float: right; font-size: 28px; font-weight: bold; color: #888; cursor: pointer;">×</span> | |
| <h2 id="popup-title" style="color: #8b5cf6; margin-bottom: 10px;"></h2> | |
| <p id="popup-desc" style="color: #aaa; line-height: 1.6;"></p> | |
| <div id="popup-stats" style="margin-top: 20px; padding: 15px; background: rgba(0,0,0,0.4); border-radius: 8px;"> | |
| <div style="display: flex; justify-content: space-between; margin: 8px 0;"> | |
| <span style="color: #888;">Status:</span> | |
| <span id="popup-status" style="color: #4ade80;"></span> | |
| </div> | |
| <div style="display: flex; justify-content: space-between; margin: 8px 0;"> | |
| <span style="color: #888;">Activations:</span> | |
| <span id="popup-activations" style="color: #f472b6;"></span> | |
| </div> | |
| <div style="display: flex; justify-content: space-between; margin: 8px 0;"> | |
| <span style="color: #888;">Last Sweep:</span> | |
| <span id="popup-sweep" style="color: #60a5fa;"></span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Theme Selector --> | |
| <div id="theme-panel" style="display: none; position: fixed; top: 70px; right: 20px; background: linear-gradient(135deg, #1a1025 0%, #0a0a12 100%); border: 1px solid rgba(139,92,246,0.4); border-radius: 12px; padding: 20px; z-index: 100;"> | |
| <h3 style="color: #8b5cf6; margin: 0 0 15px 0; font-size: 14px;">ποΈ Palace Theme</h3> | |
| <button onclick="setTheme('cinematic')" style="display: block; width: 100%; padding: 10px; margin: 5px 0; background: rgba(139,92,246,0.2); border: 1px solid rgba(139,92,246,0.4); border-radius: 6px; color: #fff; cursor: pointer;">π¬ Cinematic</button> | |
| <button onclick="setTheme('neural')" style="display: block; width: 100%; padding: 10px; margin: 5px 0; background: rgba(236,72,153,0.2); border: 1px solid rgba(236,72,153,0.4); border-radius: 6px; color: #fff; cursor: pointer;">π§ Neural</button> | |
| <button onclick="setTheme('radial')" style="display: block; width: 100%; padding: 10px; margin: 5px 0; background: rgba(34,211,238,0.2); border: 1px solid rgba(34,211,238,0.4); border-radius: 6px; color: #fff; cursor: pointer;">π Radial</button> | |
| <button onclick="setTheme('grid')" style="display: block; width: 100%; padding: 10px; margin: 5px 0; background: rgba(74,222,128,0.2); border: 1px solid rgba(74,222,128,0.4); border-radius: 6px; color: #fff; cursor: pointer;">π² Grid</button> | |
| </div> | |
| <!-- JSON Editor Modal --> | |
| <div id="json-editor" class="modal" style="display: none; position: fixed; z-index: 1000; left: 0; top: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.9);"> | |
| <div style="margin: 5% auto; padding: 20px; background: #0a0a12; border: 1px solid rgba(139,92,246,0.4); border-radius: 16px; max-width: 800px; max-height: 80vh; overflow: auto;"> | |
| <span onclick="closeJsonEditor()" style="float: right; font-size: 28px; color: #888; cursor: pointer;">×</span> | |
| <h2 style="color: #8b5cf6;">π Palace Config Editor</h2> | |
| <textarea id="json-input" style="width: 100%; height: 400px; background: #1a1025; border: 1px solid rgba(139,92,246,0.4); border-radius: 8px; color: #fff; font-family: 'JetBrains Mono', monospace; padding: 15px; font-size: 12px;"></textarea> | |
| <div style="margin-top: 15px;"> | |
| <button onclick="applyJsonConfig()" style="padding: 12px 24px; background: #8b5cf6; border: none; border-radius: 8px; color: #fff; cursor: pointer; font-weight: bold;">β Apply</button> | |
| <button onclick="exportJsonConfig()" style="padding: 12px 24px; background: rgba(74,222,128,0.2); border: 1px solid rgba(74,222,128,0.4); border-radius: 8px; color: #4ade80; cursor: pointer; margin-left: 10px;">πΎ Export</button> | |
| <button onclick="importJsonConfig()" style="padding: 12px 24px; background: rgba(34,211,238,0.2); border: 1px solid rgba(34,211,238,0.4); border-radius: 8px; color: #22d3ee; cursor: pointer; margin-left: 10px;">π Import</button> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- Sweep Ticker --> | |
| <div id="sweep-ticker" style="display: none; position: fixed; bottom: 20px; left: 20px; background: linear-gradient(135deg, #1a1025 0%, #0a0a12 100%); border: 1px solid rgba(139,92,246,0.4); border-radius: 12px; padding: 15px 20px; max-width: 400px; z-index: 100;"> | |
| <div style="display: flex; align-items: center; margin-bottom: 10px;"> | |
| <span style="color: #8b5cf6; font-weight: bold;">π‘ Live Sweeps</span> | |
| <span id="sweep-status" style="margin-left: 10px; width: 8px; height: 8px; background: #4ade80; border-radius: 50%; animation: pulse 1s infinite;"></span> | |
| </div> | |
| <div id="sweep-events" style="max-height: 150px; overflow-y: auto; font-size: 12px; color: #aaa;"></div> | |
| </div> | |
| <!-- Echo Memory Panel --> | |
| <div id="echo-panel" style="display: none; position: fixed; top: 70px; left: 20px; background: linear-gradient(135deg, #1a1025 0%, #0a0a12 100%); border: 1px solid rgba(139,92,246,0.4); border-radius: 12px; padding: 20px; max-width: 350px; max-height: 60vh; overflow-y: auto; z-index: 100;"> | |
| <h3 style="color: #ec4899; margin: 0 0 15px 0; font-size: 14px;">π ECHO Memories</h3> | |
| <div id="echo-memories" style="font-size: 12px; color: #aaa;"></div> | |
| </div> | |
| <!-- CAPT Connection Panel --> | |
| <div id="capt-panel" style="display: none; position: fixed; top: 70px; right: 20px; background: linear-gradient(135deg, #1a1025 0%, #0a0a12 100%); border: 1px solid rgba(139,92,246,0.4); border-radius: 12px; padding: 20px; width: 300px; z-index: 100;"> | |
| <h3 style="color: #8b5cf6; margin: 0 0 15px 0; font-size: 14px;">π CAPT Symbiote</h3> | |
| <div style="margin-bottom: 15px;"> | |
| <label style="display: block; color: #888; font-size: 12px; margin-bottom: 5px;">API Endpoint</label> | |
| <input type="text" id="capt-endpoint" placeholder="http://localhost:9877/v1" style="width: 100%; padding: 8px; background: rgba(0,0,0,0.4); border: 1px solid rgba(139,92,246,0.3); border-radius: 6px; color: #fff; font-size: 12px;"> | |
| </div> | |
| <div style="margin-bottom: 15px;"> | |
| <label style="display: block; color: #888; font-size: 12px; margin-bottom: 5px;">API Token</label> | |
| <input type="password" id="capt-token" placeholder="Optional" style="width: 100%; padding: 8px; background: rgba(0,0,0,0.4); border: 1px solid rgba(139,92,246,0.3); border-radius: 6px; color: #fff; font-size: 12px;"> | |
| </div> | |
| <div style="display: flex; gap: 10px;"> | |
| <button onclick="connectToCAPT()" style="flex: 1; padding: 10px; background: #8b5cf6; border: none; border-radius: 6px; color: #fff; cursor: pointer; font-size: 12px;">π Connect</button> | |
| <button onclick="disconnectFromCAPT()" style="flex: 1; padding: 10px; background: rgba(239,68,68,0.2); border: 1px solid rgba(239,68,68,0.4); border-radius: 6px; color: #ef4444; cursor: pointer; font-size: 12px;">Disconnect</button> | |
| </div> | |
| <div id="capt-status" style="margin-top: 15px; padding: 10px; background: rgba(0,0,0,0.4); border-radius: 6px; font-size: 11px; color: #888;"> | |
| Status: Disconnected | |
| </div> | |
| </div> | |
| <!-- Help Modal --> | |
| <div id="help-modal" style="display: none; position: fixed; top: 0; left: 0; width: 100%; height: 100%; background: rgba(0,0,0,0.8); backdrop-filter: blur(4px); z-index: 200; display: flex; align-items: center; justify-content: center;"> | |
| <div style="background: rgba(10,10,18,0.9); border: 1px solid rgba(139,92,246,0.4); border-radius: 16px; padding: 32px; width: 90%; max-width: 500px; color: #fff; position: relative;"> | |
| <button style="position: absolute; top: 16px; right: 16px; background: transparent; border: none; color: #8b5cf6; font-size: 20px; cursor: pointer;" onclick="document.getElementById('help-modal').style.display = 'none'" aria-label="Close help modal">×</button> | |
| <h2 style="color: #8b5cf6; margin-top: 0; text-align: center;">π§ CAPT Memory Palace Help</h2> | |
| <div style="margin-top: 24px;"> | |
| <h3 style="color: #ec4899; margin-bottom: 12px;">π― Getting Started</h3> | |
| <p>1. Click <strong>"Watch Demo"</strong> to see a simulated cognitive cycle<br> | |
| 2. Enter your own question in the input box<br> | |
| 3. Watch modules activate in real-time as CAPT processes your query</p> | |
| <h3 style="color: #ec4899; margin: 20px 0 12px;">β¨οΈ Keyboard Controls</h3> | |
| <div style="display: grid; grid-template-columns: 1fr 1fr; gap: 12px; font-size: 14px;"> | |
| <div>Space β Toggle cinematic tour</div> | |
| <div>WASD/QE β Navigate 3D space (WASD move, QE rotate)</div> | |
| <div>M β Toggle sound</div> | |
| <div>R β Reset camera to default position</div> | |
| <div>F β Toggle fullscreen</div> | |
| <div>H β Show/hide this help</div> | |
| <div>T β Show/hide theme panel</div> | |
| <div>C β Show/hide CAPT connection panel</div> | |
| <div>E β Open JSON editor</div> | |
| <div>? β Show/hide help</div> | |
| </div> | |
| <h3 style="color: #ec4899; margin: 20px 0 12px;">π Live CAPT Connection</h3> | |
| <p>Click the π CAPT button to connect to a running CAPT symbiote for real cognitive data. Enter your endpoint and optional token.</p> | |
| <h3 style="color: #ec4899; margin: 20px 0 12px;">π¨ Customization</h3> | |
| <p>Use the π Edit button to modify the palace configuration in JSON. You can change themes, room positions, colors, and connections.</p> | |
| <div style="margin-top: 24px; text-align: center;"> | |
| <button onclick="document.getElementById('help-modal').style.display = 'none'" aria-label="Close help modal" style="padding: 10px 24px; background: #8b5cf6; border: none; border-radius: 6px; color: #fff; cursor: pointer; font-size: 14px;">Got it!</button> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| <!-- CAPT Brain Status --> | |
| <script> | |
| const BRAINS = { | |
| capt: "https://capt-brain-01.knowurknottty.workers.dev", | |
| biocapt: "https://capt-brain-02-biocapt.knowurknottty.workers.dev", | |
| frankencapt:"https://capt-brain-03-frankencapt.knowurknottty.workers.dev", | |
| synthesis: "https://capt-brain-04-synthesis.knowurknottty.workers.dev", | |
| council: "https://capt-brain-05-council.knowurknottty.workers.dev", | |
| }; | |
| async function checkBrains() { | |
| const results = {}; | |
| for (const [id, url] of Object.entries(BRAINS)) { | |
| try { | |
| const r = await fetch(url + "/health", { mode: "cors", signal: AbortSignal.timeout(5000) }); | |
| results[id] = r.ok ? "online" : "error"; | |
| } catch (e) { results[id] = "offline"; } | |
| } | |
| const online = Object.values(results).filter(s => s === "online").length; | |
| const el = document.getElementById("capt-brain-status"); | |
| if (el) el.innerHTML = "CAPT Brains: " + online + "/5 online"; | |
| console.log("CAPT brains:", results); | |
| } | |
| checkBrains(); | |
| setInterval(checkBrains, 30000); | |
| </script> | |
| </body> | |
| </html> | |