### Open index.html locally Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt This command opens the index.html file, simulating the start of a local server. This is a placeholder for actual server start commands like 'python -m http.server' or 'npm start'. ```bash echo "๐Ÿš€ Opening the gate..." ``` -------------------------------- ### Set Up Environment Variables Source: https://github.com/mnemonice/mind-palace-dome/blob/main/README.md Copy the example environment file and edit it with your API keys. ```bash cp .env.example .env # Edit .env with your API keys ``` -------------------------------- ### Set Up Environment Variables Source: https://github.com/mnemonice/mind-palace-dome/blob/main/dev/guides/getting_started.md Copy the example environment file to create your own. Optionally, add your Google Gemini API Key for AI image generation. ```bash cp .env.example .env ``` -------------------------------- ### Launch the Application Source: https://github.com/mnemonice/mind-palace-dome/blob/main/README.md Start the local HTTP server to launch the application. This script simply runs `python3 -m http.server`. ```bash ./startup.sh ``` -------------------------------- ### Setup Microphone Button Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/delta_snapshot.txt Initializes a microphone button to handle audio recording using 'hold' mode. It starts recording on mousedown and stops on mouseup, providing a transcript via a callback. ```javascript function setupMicButton(buttonElement, onTranscript) { if (!buttonElement) return; // Remove any existing signal to prevent duplicated events if (buttonElement._abortController) { buttonElement._abortController.abort(); } buttonElement._abortController = new AbortController(); const signal = buttonElement._abortController.signal; let isRecording = false; buttonElement.addEventListener('mousedown', async () => { const mode = settingsManager.get('audio', 'recordingMode'); if (mode === 'hold') { console.log("๐ŸŽค Hold Mode: Start"); const result = await audioEngine.startRecording(); if (result.success) { isRecording = true; } else { alert(`Cannot record: ${result.error}`); } } }); buttonElement.addEventListener('mouseup', async () => { // ... (rest of mouseup logic) ... }); } ``` -------------------------------- ### Test Update and Get Setting Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Tests the ability to update a specific setting ('visuals.theme' to 'light') and then retrieve it to confirm the change. ```javascript console.log('Test 2: Update Setting'); settingsManager.update('visuals', 'theme', 'light'); assert.strictEqual(settingsManager.get('visuals', 'theme'), 'light', 'Theme should be updated to light'); console.log('โœ… Update Setting verified.'); ``` -------------------------------- ### Setup UI Event Listeners Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Initializes event listeners for various UI elements including settings toggles, room selection, adding new rooms, adding items, and initiating ritual mode. It also sets up a custom event listener for UI refreshes. ```javascript // --- SETTINGS & UI WIRING --- function setupEventListeners() { // Top Menu document.getElementById('btn-settings').addEventListener('click', toggleSettings); document.getElementById('close-settings').addEventListener('click', toggleSettings); // Room Controls const roomSelect = document.getElementById('room-select'); if (roomSelect) { roomSelect.addEventListener('change', (e) => { currentRoomId = e.target.value; currentLociIndex = 0; renderCarousel(currentRoomId); }); } const btnNewRoom = document.getElementById('btn-new-room'); if (btnNewRoom) { btnNewRoom.addEventListener('click', () => { const name = prompt("Name your new Room (e.g., 'The Library')"); if (name) { const newRoom = stateManager.createRoom(name); if (newRoom) { refreshRoomList(); currentRoomId = newRoom.id; currentLociIndex = 0; renderCarousel(currentRoomId); } else { alert("Room already exists or invalid name."); } } }); } const btnAddItem = document.getElementById('btn-add-item'); if (btnAddItem) { btnAddItem.addEventListener('click', () => { openEditModal(null); // Create Mode }); } // Ritual Mode Button document.getElementById('btn-ritual').addEventListener('click', () => { const ritual = new RitualMode(); ritual.start(); }); // Custom Event Listener for Ritual Completion (Refresh UI) document.addEventListener('dome:refresh', () => { renderCarousel(currentRoomId); }); } ``` -------------------------------- ### Check for .env file Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt This script checks if the .env file exists in the current directory. If not, it prints a warning message instructing the user to create it from the example file and add their keys. ```bash if [ ! -f .env ]; then echo "โš ๏ธ Warning: .env file not found. Please copy .env.example to .env and add your keys." fi ``` -------------------------------- ### Handle 'toggle' recording mode Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/delta_snapshot.txt This snippet manages the 'toggle' mode for audio recording. It checks the recording state and updates the UI accordingly, logging the start or stop action. ```javascript const mode = settingsManager.get('audio', 'recordingMode'); if (mode === 'toggle') { if (!isRecording) { console.log("๐ŸŽค Toggle Mode: Start"); buttonElement.classList.add('recording'); } else { console.log("๐ŸŽค Toggle Mode: Stop"); buttonElement.classList.remove('recording'); } } ``` -------------------------------- ### Start Recording for Whisper STT Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Initiates audio recording using the MediaDevices API, preparing for transcription with Whisper. Requires user permission for microphone access. Returns success status and any error encountered. ```javascript async startRecording() { if (!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia) { console.error("Media Devices API not supported."); return { success: false, error: "Media Devices API not supported in this browser." }; } try { const stream = await navigator.mediaDevices.getUserMedia({ audio: true }); this.mediaRecorder = new MediaRecorder(stream); this.audioChunks = []; this.mediaRecorder.ondataavailable = (event) => { this.audioChunks.push(event.data); }; this.mediaRecorder.start(); console.log("๐ŸŽ™๏ธ Recording started..."); return { success: true }; } catch (err) { console.error("Could not start recording:", err); return { success: false, error: err.message }; } } ``` -------------------------------- ### Start Carousel Tour Mode Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Initiates an automatic carousel tour by setting an interval to advance to the next item every 3 seconds. Requires at least two items in the room. ```javascript function startTour() { if (tourInterval) return; const room = stateManager.getRoom(currentRoomId); if (!room || room.items.length < 2) return; tourInterval = setInterval(() => { nextLoci(); }, 3000); renderCarousel(currentRoomId); } ``` -------------------------------- ### Toggle Carousel Tour Mode Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Switches between starting and stopping the carousel tour mode. If a tour is active, it stops; otherwise, it starts. ```javascript function toggleTour() { if (tourInterval) stopTour(); else startTour(); } ``` -------------------------------- ### Clone Repository and Navigate Source: https://github.com/mnemonice/mind-palace-dome/blob/main/README.md Clone the dome-icile repository and change into the project directory. ```bash git clone https://github.com/yourusername/dome-icile.git cd dome-icile ``` -------------------------------- ### Initialize Ritual Walk UI Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Sets up the event listener for closing the ritual walk and displays the overlay. ```javascript document.getElementById('ritual-close').addEventListener('click', () => this.end()); this.overlay.classList.remove('hidden'); ``` -------------------------------- ### Run the Application with Python Source: https://github.com/mnemonice/mind-palace-dome/blob/main/dev/guides/getting_started.md Serve the application using Python's built-in HTTP server. This is necessary due to the use of ES6 Modules. ```bash ./startup.sh # Or manually: python3 -m http.server 8000 ``` -------------------------------- ### Agent Roster Table Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Defines the different agent personas used in the codebase simulation, outlining their roles, focus areas, and guiding mantras for decision-making. ```markdown | Agent | Role | Focus | Mantra | | :--- | :--- | :--- | :--- | | **Brain** | Team Lead | Consensus | "Synthesize and Decide." | | **Bolt** | Performance | Speed/Latency | "Speed is a feature." | | **Boom** | Product | Features/Shipping | "Ship it." | | **Sentinel** | Security | Safety/Validation | "Trust nothing." | | **Palette** | UX/UI | Accessibility/Feel | "Make it feel human." | | **Scribe** | Docs | Maintainability | "Write it down." | | **Scope** | QA | Edge Cases | "Everything breaks." | | **Orbit** | DevOps | Infra/Stability | "Works in Prod." | ``` -------------------------------- ### Test Default Settings Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Verifies that the settings manager initializes with the correct default settings, specifically checking the 'dark' theme. ```javascript console.log('Test 1: Default Settings'); const defaultSettings = settingsManager.settings; assert.strictEqual(defaultSettings.visuals.theme, 'dark', 'Default theme should be dark'); console.log('โœ… Default settings verified.'); ``` -------------------------------- ### Run Unit Tests Source: https://github.com/mnemonice/mind-palace-dome/blob/main/dev/guides/getting_started.md Execute the Node.js scripts for unit testing the project's logic, including SRS math and persistence. ```bash # Test the SRS Math node test_srs.mjs # Test Persistence node test_persistence.mjs ``` -------------------------------- ### Project Directory Structure Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Overview of the main directories and files within the dome-icile project. ```bash dome-icile/ โ”œโ”€โ”€ src/ # Source code โ”‚ โ”œโ”€โ”€ app.js # Main controller (UI, Events) โ”‚ โ”œโ”€โ”€ state_manager.js # Data persistence (Rooms, Items) โ”‚ โ”œโ”€โ”€ settings_manager.js # User preferences โ”‚ โ”œโ”€โ”€ srs_engine.js # Spaced Repetition Logic (Math) โ”‚ โ”œโ”€โ”€ audio_engine.js # TTS and STT Logic โ”‚ โ”œโ”€โ”€ prompt_engine.js # AI Prompt Logic (Foundry) โ”‚ โ”œโ”€โ”€ gatekeeper.js # Quiz Logic โ”‚ โ””โ”€โ”€ styles.css # Global Styles โ”œโ”€โ”€ dev/ # Developer Documentation (You are here) โ”œโ”€โ”€ verification/ # Playwright E2E tests โ”œโ”€โ”€ test_*.mjs # Unit tests (Node.js) โ”œโ”€โ”€ index.html # Main entry point โ””โ”€โ”€ AGENTS.md # The "Persona" workflow protocol ``` -------------------------------- ### Launch Quiz Modal Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Displays the quiz modal, populates it with the question, and initiates text-to-speech for the question if the audio engine is enabled. Clears previous interaction elements and feedback. ```javascript // --- QUIZ LOGIC --- function launchQuizModal(quiz) { const modal = document.getElementById('quiz-modal'); const questionEl = document.getElementById('quiz-question'); const area = document.getElementById('quiz-interaction-area'); const submitBtn = document.getElementById('btn-quiz-submit'); const feedback = document.getElementById('quiz-feedback'); // TTS: Speak the question if engine is enabled // Only if not already speaking to avoid chaos const ttsEngine = settingsManager.get('audio', 'engine'); // Simple check: if we are using browser/hybrid, we can try to speak // Note: Chrome requires user interaction first, so this might fail if it's the very first action audioEngine.speak(quiz.question); questionEl.innerText = quiz.question; area.innerHTML = ''; // Clear previous feedback.innerText = ''; ``` -------------------------------- ### Initialize and Load State (State Manager) Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Manages the persistence of the memory palace state using localStorage. Initializes default rooms and items if no saved state is found. ```javascript /** * state_manager.js * Manages the persistence of the memory palace. * Handles room data, item stats (streaks, last reviewed), and localStorage sync. */ import { calculateNextReview, DEFAULT_SRS_STATS } from './srs_engine.js'; const DEFAULT_ROOMS = { 'foyer': { id: 'foyer', name: 'The Foyer', items: [ { id: 1, concept: 'Mitochondria', visualURL: 'https://placehold.co/200x200/222/bb86fc?text=Powerhouse', visual: 'Powerhouse', streak: 0, lastReviewed: 0, ...DEFAULT_SRS_STATS, dueDate: 0 }, { id: 2, concept: 'Nucleus', visualURL: 'https://placehold.co/200x200/222/03dac6?text=Brain', visual: 'Brain', streak: 0, lastReviewed: 0, ...DEFAULT_SRS_STATS, dueDate: 0 }, { id: 3, concept: 'Ribosome', visualURL: 'https://placehold.co/200x200/222/cf6679?text=Chef', visual: 'Chef', streak: 0, lastReviewed: 0, ...DEFAULT_SRS_STATS, dueDate: 0 } ] } }; class StateManager { constructor() { this.storageKey = 'dome_state'; this.state = this.loadState(); } loadState() { if (typeof localStorage === 'undefined') return { rooms: DEFAULT_ROOMS }; const saved = localStorage.getItem(this.storageKey); if (!saved) { // Seed defaults return { rooms: DEFAULT_ROOMS }; } return JSON.parse(saved); } saveState() { if (typeof localStorage !== 'undefined') { localStorage.setItem(this.storageKey, JSON.stringify(this.state)); } } getRoom(roomId) { return this.state.rooms[roomId]; } /** * Updates an item's stats after a review/quiz. * @param {string} roomId * @param {number|string} itemId * @param {number} grade - 0-5 grade or simple boolean (legacy) */ updateItemStats(roomId, itemId, grade) { const room = this.state.rooms[roomId]; if (!room) return; const itemIndex = room.items.findIndex(i => i.id == itemId); if (itemIndex === -1) return; let item = room.items[itemIndex]; // Handle Legacy Boolean Calls (from old Quiz mode) // If success (true) -> Grade 4. If failure (false) -> Grade 1. if (typeof grade === 'boolean') { grade = grade ? 4 : 1; } // Apply SRS logic const currentStats = { interval: item.interval || 0, repetition: item.repetition || 0, easeFactor: item.easeFactor || 2.5 }; const newStats = calculateNextReview(currentStats, grade); // Merge updates item = { ...item, ...newStats, lastReviewed: Date.now() }; // Legacy support: Keep 'streak' roughly aligned with 'repetition' item.streak = item.repetition; // Commit change this.state.rooms[roomId].items[itemIndex] = item; } } ``` -------------------------------- ### Audio Engine Class Initialization Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Initializes the AudioEngine class, setting up priorities for Text-to-Speech (OpenAI, eSpeak, Web Speech API) and Speech-to-Text (OpenAI Whisper, Web Speech Recognition). It checks for browser support for speech synthesis and recognition. ```javascript /** * audio_engine.js * The voice of dome-icile. * * Capabilities: * 1. Text-to-Speech (TTS): * - Priority 1: OpenAI (High quality, paid) * - Priority 2: eSpeak (Retro/Robot, free, via library) * - Priority 3: Web Speech API (Browser native, free, offline) * * 2. Speech-to-Text (STT): * - Priority 1: OpenAI Whisper (High accuracy, paid) * - Priority 2: Web Speech Recognition (Browser native, free) */ class AudioEngine { constructor(config = {}) { this.openAIKey = config.openAIKey || null; this.useESpeak = config.useESpeak || false; // Toggle for retro robot voice // Initialize Speech Synthesis (Check if supported) if (typeof window !== 'undefined' && window.speechSynthesis) { this.synth = window.speechSynthesis; } else { console.warn("AudioEngine: Speech Synthesis not supported in this environment."); this.synth = null; } // Initialize Web Speech Recognition (Chrome/Edge/Safari support varies) if (typeof window !== 'undefined') { this.Recognition = window.SpeechRecognition || window.webkitSpeechRecognition; this.recognizer = this.Recognition ? new this.Recognition() : null; } else { this.recognizer = null; } // MediaRecorder State this.mediaRecorder = null; this.audioChunks = []; } setConfig(config) { if (config.openAIKey !== undefined) this.openAIKey = config.openAIKey; // Update other config if needed } // --- TEXT TO SPEECH (The Palace Speaks to You) --- async speak(text) { console.log(`๐Ÿ”Š Speaking: "${text}"`); // 1. OpenAI TTS (If Key exists) if (this.openAIKey) { try { await this.speakWithOpenAI(text); return; } catch (err) { console.warn("โš ๏ธ OpenAI TTS failed, falling back...", err); } } ``` -------------------------------- ### Mock Local Storage Implementation Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt A mock implementation of localStorage for testing purposes, providing basic getItem, setItem, and clear methods. ```javascript const localStorageMock = (function() { let store = {}; return { getItem: function(key) { return store[key] || null; }, setItem: function(key, value) { store[key] = value.toString(); }, clear: function() { store = {}; } }; })(); // Assign to global global.localStorage = localStorageMock; ``` -------------------------------- ### Handle Quiz Modes (CHOICE and INPUT) Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Configures the quiz interface based on the quiz mode. For 'CHOICE' mode, it displays buttons for each choice. For 'INPUT' mode, it creates a text input field, sets up event listeners for the Enter key, and adds a microphone button for voice input. ```javascript feedback.className = ''; if (quiz.mode === 'CHOICE') { submitBtn.classList.add('hidden'); quiz.choices.forEach(choice => { const btn = document.createElement('button'); btn.className = 'quiz-option-btn'; btn.innerText = choice; btn.onclick = () => handleQuizAttempt(choice); area.appendChild(btn); }); } else { // INPUT Mode const input = document.createElement('input'); input.type = 'text'; input.id = 'quiz-input'; input.placeholder = 'Type the concept...'; input.autocomplete = 'off'; // Allow Enter key to submit input.addEventListener('keyup', (e) => { if (e.key === 'Enter') handleQuizAttempt(input.value); }); area.appendChild(input); // Add Mic Button for Quiz const micContainer = document.createElement('div'); micContainer.style.marginTop = '10px'; const micBtn = document.createElement('button'); micBtn.className = 'btn small-btn'; micBtn.innerText = '๐ŸŽ™๏ธ Answer'; micContainer.appendChild(micBtn); area.appendChild(micContainer); setupMicButton(micBtn, (text) => { input.value = text; // Optional: Auto-submit? Let's wait for user confirmation }); submitBtn.classList.remove('hidden'); submitBtn.onclick = () => handleQuizAttempt(input.value); // Focus input after a short delay for modal transition setTimeout(() => input.focus(), 100); } modal.classList.remove('hidden'); toggleMainContent(true); setModalFocus(modal.id); ``` -------------------------------- ### Initialize Ritual Mode Overlay Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Initializes the ritual mode by filtering room items for those past their due date and rendering a full-screen overlay. If no items are due, it alerts the user. ```javascript import { stateManager } from './state_manager.js'; import { settingsManager } from './settings_manager.js'; export class RitualMode { constructor() { this.overlay = null; this.currentItems = []; this.currentIndex = 0; } start() { const roomId = 'foyer'; // Currently only one room const room = stateManager.getRoom(roomId); if (!room) return; // Filter for Due Items // "Dusty" = Due Date <= Now const now = Date.now(); this.currentItems = room.items.filter(item => { const dueDate = item.dueDate || 0; return now >= dueDate; }); if (this.currentItems.length === 0) { alert("โœจ The Palace is pristine. No rituals required at this time."); return; } this.currentIndex = 0; this.renderOverlay(); this.showItem(this.currentIndex); } renderOverlay() { // Create full screen overlay if not exists if (!this.overlay) { this.overlay = document.createElement('div'); this.overlay.id = 'ritual-overlay'; this.overlay.className = 'ritual-overlay'; document.body.appendChild(this.overlay); } this.overlay.innerHTML = `
``` -------------------------------- ### DOM Content Loaded Initialization Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Initializes the application by calling the `init` function once the DOM is fully loaded. ```javascript // Initialize document.addEventListener('DOMContentLoaded', init); ``` -------------------------------- ### Theme Testing Suite Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Tests the application of dark, light, and matrix themes by mocking the document object and verifying CSS variable changes. Exits on failure. ```javascript import { settingsManager } from './src/settings_manager.js'; console.log("๐Ÿงช Starting Theme Tests..."); // Mock document and documentElement const styleMap = new Map(); global.document = { documentElement: { style: { setProperty: (key, value) => { styleMap.set(key, value); } } }, body: { dataset: {} } }; // Test 1: Apply Dark Theme (Default) console.log("\nTest 1: Apply Dark Theme (Default)"); settingsManager.update('visuals', 'theme', 'dark'); settingsManager.applySettings(); const bgMain = styleMap.get('--bg-main'); if (bgMain === '#121212') { console.log("โœ… Dark theme applied correctly."); } else { console.error(`โŒ Failed to apply dark theme. --bg-main: ${bgMain}`); process.exit(1); } // Test 2: Apply Light Theme console.log("\nTest 2: Apply Light Theme"); settingsManager.update('visuals', 'theme', 'light'); settingsManager.applySettings(); const bgMainLight = styleMap.get('--bg-main'); if (bgMainLight === '#f5f5f5') { console.log("โœ… Light theme applied correctly."); } else { console.error(`โŒ Failed to apply light theme. --bg-main: ${bgMainLight}`); process.exit(1); } // Test 3: Apply Matrix Theme console.log("\nTest 3: Apply Matrix Theme"); settingsManager.update('visuals', 'theme', 'matrix'); settingsManager.applySettings(); const textMainMatrix = styleMap.get('--text-main'); if (textMainMatrix === '#00ff00') { console.log("โœ… Matrix theme applied correctly."); } else { console.error(`โŒ Failed to apply matrix theme. --text-main: ${textMainMatrix}`); process.exit(1); } console.log("\n๐ŸŽ‰ All Theme Tests Passed!"); ``` -------------------------------- ### Default Application Settings Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Defines the default configuration for various application modules, including audio, gatekeeper, AI, and visuals. ```javascript const DEFAULT_SETTINGS = { audio: { engine: 'browser', // 'browser', 'openai' openAIKey: '', recordingMode: 'toggle', // 'toggle', 'hold' voiceId: 'default', volume: 0.8 }, gatekeeper: { mode: 'dynamic', // 'input', 'choice', 'dynamic' scalingThreshold: 3 }, ai: { geminiKey: '', generationMode: 'text' // 'text' or 'image' (future) }, visuals: { decayStyle: 'both', // 'filter', 'texture', 'both', 'none' dualCoding: 'hover', // 'always', 'hover' theme: 'dark' } }; ``` -------------------------------- ### State Management Class Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Manages the application state, including rooms and items. Includes methods for updating, creating, and adding items, as well as resetting the state. ```javascript class StateManager { constructor() { this.state = { rooms: DEFAULT_ROOMS }; this.saveState(); } saveState() { // Placeholder for actual save logic console.log("State saved:", this.state); } /** * Updates item details (Editing). */ updateItemDetails(roomId, itemId, newConcept, newVisualURL) { const room = this.state.rooms[roomId]; if (!room) return; const itemIndex = room.items.findIndex(i => i.id == itemId); if (itemIndex === -1) return; const item = room.items[itemIndex]; if (newConcept) item.concept = newConcept; if (newVisualURL) item.visualURL = newVisualURL; this.state.rooms[roomId].items[itemIndex] = item; this.saveState(); return item; } /** * Creates a new room. */ createRoom(name) { const id = name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/(^-|-$)+/g, ''); if (this.state.rooms[id]) return null; // Already exists const newRoom = { id: id, name: name, items: [] }; this.state.rooms[id] = newRoom; this.saveState(); return newRoom; } /** * Adds a new item to a room. */ addItem(roomId, concept, visualURL) { const room = this.state.rooms[roomId]; if (!room) return null; const newItem = { id: Date.now(), concept: concept, visualURL: visualURL || 'https://placehold.co/200x200?text=?', visual: concept, streak: 0, lastReviewed: 0, ...DEFAULT_SRS_STATS, dueDate: 0 }; room.items.push(newItem); this.saveState(); return newItem; } /** * Reset everything (Debug/Ritual) */ reset() { this.state = { rooms: DEFAULT_ROOMS }; this.saveState(); } } export const stateManager = new StateManager(); ``` -------------------------------- ### Python Script for Codebase Ingestion Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt This script walks through a directory, filters out binary files and excluded directories/extensions, and generates a snapshot of the codebase. It also calculates and saves the differences (delta) between the current and previous snapshots. ```python import os import difflib # Configuration INGEST_DIR = "ingest" SNAPSHOT_FILE = os.path.join(INGEST_DIR, "codebase_snapshot.txt") DELTA_FILE = os.path.join(INGEST_DIR, "delta_snapshot.txt") EXCLUDED_DIRS = {'.git', 'node_modules', '__pycache__', 'dist', 'build', 'ingest', '.github'} EXCLUDED_EXTENSIONS = {'.png', '.jpg', '.jpeg', '.gif', '.ico', '.pyc', '.DS_Store', '.woff', '.woff2', '.ttf', '.eot'} def is_text_file(filepath): """Simple check to avoid reading binary files based on extension.""" _, ext = os.path.splitext(filepath) if ext.lower() in EXCLUDED_EXTENSIONS: return False # Additional check: try reading a small chunk try: with open(filepath, 'r', encoding='utf-8') as f: f.read(1024) return True except UnicodeDecodeError: return False def generate_codebase_string(root_dir): """Walks the directory and generates the snapshot string.""" codebase_content = [] for root, dirs, files in os.walk(root_dir): # Modify dirs in-place to skip ignored directories dirs[:] = [d for d in dirs if d not in EXCLUDED_DIRS] # Sort for deterministic output dirs.sort() files.sort() for file in files: filepath = os.path.join(root, file) # Skip excluded files if any(filepath.endswith(ext) for ext in EXCLUDED_EXTENSIONS): continue # Skip the script itself if it's in the list (optional, but good practice) if filepath.endswith("generate_ingest.py"): pass # We include it usually, unless specified otherwise. if is_text_file(filepath): try: with open(filepath, 'r', encoding='utf-8') as f: content = f.read() codebase_content.append("=" * 64) codebase_content.append(f"File: {filepath}") codebase_content.append("=" * 64) codebase_content.append(content) codebase_content.append("\n") # Add newline between files except Exception as e: print(f"Skipping file {filepath} due to error: {e}") return "\n".join(codebase_content) def main(): # Ensure ingest directory exists if not os.path.exists(INGEST_DIR): os.makedirs(INGEST_DIR) print(f"Created directory: {INGEST_DIR}") # Generate current state current_state = generate_codebase_string(".") # Read previous state if exists previous_state = "" if os.path.exists(SNAPSHOT_FILE): try: with open(SNAPSHOT_FILE, 'r', encoding='utf-8') as f: previous_state = f.read() except Exception as e: print(f"Could not read previous snapshot: {e}") # Calculate Diff if previous_state: print("Calculating diff...") diff = difflib.unified_diff( previous_state.splitlines(), current_state.splitlines(), fromfile='Previous Snapshot', tofile='Current Snapshot', lineterm='' ) delta_content = "\n".join(diff) if not delta_content: delta_content = "No changes detected." else: delta_content = "Initial Snapshot - No previous state to diff against." # Write Snapshot print(f"Writing snapshot to {SNAPSHOT_FILE}...") with open(SNAPSHOT_FILE, 'w', encoding='utf-8') as f: f.write(current_state) # Write Delta print(f"Writing delta to {DELTA_FILE}...") with open(DELTA_FILE, 'w', encoding='utf-8') as f: f.write(delta_content) print("Ingest generation complete.") if __name__ == "__main__": main() ``` -------------------------------- ### Launch Quiz Modal Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/delta_snapshot.txt Generates and displays a quiz in a modal dialog. Closes the modal after a short delay once the quiz is completed. ```javascript function launchQuiz() { const currentRoomId = stateManager.getCurrentRoomId(); const room = stateManager.getRoom(currentRoomId); const quiz = activeGatekeeper.generateQuiz(room.items); launchQuizModal(quiz); } function launchQuizModal(quiz) { // ... (quiz modal setup code) ... modal.classList.remove('hidden'); toggleMainContent(true); setModalFocus(modal.id); } ``` -------------------------------- ### Wire Up Voice Input Button Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Sets up a microphone button to capture voice input and populate a specified input field with the transcribed text. ```javascript // Wire up Mic const micBtn = document.getElementById('btn-mic-concept'); setupMicButton(micBtn, (text) => { document.getElementById('edit-concept').value = text; }); ``` -------------------------------- ### Test Settings Persistence Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Confirms that settings updated via the manager are correctly persisted to localStorage and can be retrieved. ```javascript console.log('Test 3: Persistence'); const saved = JSON.parse(localStorage.getItem('dome_settings')); assert.strictEqual(saved.visuals.theme, 'light', 'LocalStorage should have the updated theme'); console.log('โœ… Persistence verified.'); console.log('๐ŸŽ‰ All Settings Tests Passed!'); ``` -------------------------------- ### Complete Ritual Walk UI Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Displays a completion message and a button to return. ```javascript this.overlay.innerHTML = `

โœจ Ritual Complete

Your mind palace is fortified.

`; document.getElementById('ritual-finish').onclick = () => this.end(); ``` -------------------------------- ### Open HTML File Based on OS Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt This shell script opens an 'index.html' file in the default web browser, adapting the command based on the detected operating system (Linux, macOS, Cygwin, MSYS). If the OS is not recognized, it prompts the user to open the file manually. ```shell # Open based on OS if [[ "$OSTYPE" == "linux-gnu"* ]]; then xdg-open index.html elif [[ "$OSTYPE" == "darwin"* ]]; then open index.html elif [[ "$OSTYPE" == "cygwin" ]]; then start index.html elif [[ "$OSTYPE" == "msys" ]]; then start index.html else echo "Could not detect OS to open browser automatically. Please open index.html manually." fi ``` -------------------------------- ### Handle Card Click for Gatekeeper Initialization Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Initiates the Gatekeeper for a specific item when its card is clicked, provided the card is marked as 'dusty'. It sets up the Gatekeeper with the item's concept and streak, then launches the quiz modal. ```javascript function handleCardClick(item, cardElement) { // If not dusty, do nothing (already clean) if (!cardElement.classList.contains('dusty')) return; const gkMode = settingsManager.get('gatekeeper', 'mode'); // Initialize Gatekeeper for this specific battle activeGatekeeper = new Gatekeeper({ type: gkMode, answer: item.concept, history: { successes: item.streak || 0 } }); currentTargetItem = { item, element: cardElement }; // Generate Quiz const room = stateManager.getRoom(currentRoomId); const quiz = activeGatekeeper.generateQuiz(room.items); launchQuizModal(quiz); } ``` -------------------------------- ### Mock localStorage for Testing Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt A JavaScript mock implementation of the localStorage API, providing `getItem`, `setItem`, and `clear` methods. This is useful for testing JavaScript code that relies on localStorage without depending on the browser environment. ```javascript // Mock localStorage const localStorageMock = (function() { let store = {}; return { getItem: function(key) { return store[key] || null; }, setItem: function(key, value) { store[key] = value.toString(); }, clear: function() { store = {}; } }; })(); // Assign to global global.localStorage = localStorageMock; ``` -------------------------------- ### Microphone Button Event Handling Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Sets up event listeners for microphone buttons to handle 'hold' and 'toggle' recording modes. It manages recording start/stop, UI updates, and calls the onTranscript callback with the recognized text. Ensures existing signal controllers are aborted to prevent duplicate events. ```javascript function setupMicButton(buttonElement, onTranscript) { if (!buttonElement) return; // Remove any existing signal to prevent duplicated events if (buttonElement._abortController) { buttonElement._abortController.abort(); } buttonElement._abortController = new AbortController(); const signal = buttonElement._abortController.signal; let isRecording = false; buttonElement.addEventListener('mousedown', async () => { const mode = settingsManager.get('audio', 'recordingMode'); if (mode === 'hold') { console.log("๐ŸŽค Hold Mode: Start"); const result = await audioEngine.startRecording(); if (result.success) { buttonElement.classList.add('recording'); buttonElement.innerText = '๐Ÿ”ด Listening...'; } else { alert(`Cannot record: ${result.error}`); } } }, { signal }); buttonElement.addEventListener('mouseup', async () => { const mode = settingsManager.get('audio', 'recordingMode'); if (mode === 'hold') { console.log("๐ŸŽค Hold Mode: Stop"); buttonElement.classList.remove('recording'); buttonElement.innerText = 'โณ Processing...'; try { const text = await audioEngine.stopRecording(); buttonElement.innerText = '๐ŸŽ™๏ธ'; onTranscript(text); } catch (err) { console.error(err); buttonElement.innerText = 'โš ๏ธ Error'; } } }, { signal }); buttonElement.addEventListener('click', async () => { const mode = settingsManager.get('audio', 'recordingMode'); if (mode === 'toggle') { if (!isRecording) { console.log("๐ŸŽค Toggle Mode: Start"); const result = await audioEngine.startRecording(); if (result.success) { isRecording = true; buttonElement.classList.add('recording'); buttonElement.innerText = '๐Ÿ”ด Stop'; } else { alert(`Cannot record: ${result.error}`); } } else { console.log("๐ŸŽค Toggle Mode: Stop"); isRecording = false; buttonElement.classList.remove('recording'); buttonElement.innerText = 'โณ Processing...'; try { const text = await audioEngine.stopRecording(); buttonElement.innerText = '๐ŸŽ™๏ธ'; onTranscript(text); } catch (err) { console.error(err); buttonElement.innerText = 'โš ๏ธ Error'; } } } }, { signal }); } ``` -------------------------------- ### Default SRS Statistics Initialization Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Defines the initial state for SuperMemo-2 (SM-2) spaced repetition algorithm statistics. These values are used for new or learning items. ```javascript /** * srs_engine.js * Implements the SuperMemo-2 (SM-2) Spaced Repetition Algorithm. * Adapted for the "Dome" (Mind Palace) context. */ // Default starting values export const DEFAULT_SRS_STATS = { interval: 0, // Days (0 means new/learning) repetition: 0, // Successive successful reviews ``` -------------------------------- ### SettingsManager Class for Application Configuration Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Manages application settings, including loading from localStorage, saving changes, and applying visual themes and decay styles to the DOM. It ensures settings are persisted and updated in real-time. ```javascript class SettingsManager { constructor() { this.settings = this.loadSettings(); // In case we run in an environment without DOM (like tests), we skip applySettings if (typeof document !== 'undefined') { this.applySettings(); // Apply on load } } loadSettings() { if (typeof localStorage === 'undefined') return DEFAULT_SETTINGS; const saved = localStorage.getItem('dome_settings'); return saved ? { ...DEFAULT_SETTINGS, ...JSON.parse(saved) } : DEFAULT_SETTINGS; } saveSettings(newSettings) { this.settings = { ...this.settings, ...newSettings }; if (typeof localStorage !== 'undefined') { localStorage.setItem('dome_settings', JSON.stringify(this.settings)); } if (typeof document !== 'undefined') { this.applySettings(); // Real-time update } console.log("โš™๏ธ Settings saved & applied."); } // Apply logic to the DOM (CSS Variables, Classes, etc.) applySettings() { const root = document.documentElement; const v = this.settings.visuals; // 1. Apply Theme const theme = THEMES[v.theme] || THEMES['dark']; Object.entries(theme).forEach(([key, value]) => { root.style.setProperty(key, value); }); // 2. Apply Decay Styles via CSS Variables if (v.decayStyle === 'filter' || v.decayStyle === 'both') { root.style.setProperty('--decay-filter', '0.4'); root.style.setProperty('--decay-blur', '1px'); } else { root.style.setProperty('--decay-filter', '0'); root.style.setProperty('--decay-blur', '0'); } if (v.decayStyle === 'texture' || v.decayStyle === 'both') { root.style.setProperty('--decay-texture-opacity', '0.3'); } else { root.style.setProperty('--decay-texture-opacity', '0'); } // 3. Dual Coding Mode if (document.body) { document.body.dataset.dualCoding = v.dualCoding; // Used by CSS to toggle visibility } } // Helper to get a specific setting safely get(category, key) { return this.settings[category]?.[key]; } // Update a specific setting and save update(category, key, value) { const currentCat = this.settings[category] || {}; const newCat = { ...currentCat, [key]: value }; this.saveSettings({ [category]: newCat }); } } export const settingsManager = new SettingsManager(); ``` -------------------------------- ### Spaced Repetition System (SRS) Algorithm Formulas Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Mathematical formulas for calculating the next review interval, repetition count, and ease factor based on user feedback. ```latex n = n_{prev} + 1 ``` ```latex I = \begin{cases} 1 & \text{if } n = 1 \\ 6 & \text{if } n = 2 \\ \text{round}(I_{prev} \times EF_{prev}) & \text{if } n > 2 \end{cases} ``` ```latex n = 0 I = 1 ``` ```latex EF' = EF_{prev} + (0.1 - (5 - q) \times (0.08 + (5 - q) \times 0.02)) ``` ```latex \text{DueDate} = \text{Date.now()} + (I \times 24 \times 60 \times 60 \times 1000) ``` -------------------------------- ### State Manager Data Model Source: https://github.com/mnemonice/mind-palace-dome/blob/main/dev/architecture/system_overview.md Illustrates the JSON structure for storing room and item data within the State Manager. ```json { "rooms": { "foyer": { "id": "foyer", "items": [ ... ] } } } ``` -------------------------------- ### Test Add Item Functionality Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Tests the functionality of adding a new item to a room and verifies its properties and presence in the room's item list. ```javascript console.log('Test 5: Add Item'); const newItem = stateManager.addItem('the-library', 'Book of Secrets', 'http://book.img'); assert.ok(newItem, 'Item should be added'); assert.strictEqual(newItem.concept, 'Book of Secrets', 'Concept should match'); assert.strictEqual(newItem.streak, 0, 'Default streak should be 0'); assert.strictEqual(newItem.interval, 0, 'Default interval should be 0'); assert.ok(newItem.id > 0, 'ID should be generated'); const roomItems = stateManager.getRoom('the-library').items; assert.strictEqual(roomItems.length, 1, 'Room should have 1 item'); assert.strictEqual(roomItems[0].id, newItem.id, 'Item should be in the room'); console.log('โœ… Add Item verified.'); console.log('๐ŸŽ‰ All Tests Passed!'); ``` -------------------------------- ### Initialize and Attach Event Listeners for Modal Source: https://github.com/mnemonice/mind-palace-dome/blob/main/ingest/codebase_snapshot.txt Attaches static event listeners to modal elements once. Handles closing the modal and file uploads via FileReader for base64 encoding. ```javascript // Attach static event listeners ONCE modal.querySelector('.close-btn').addEventListener('click', () => { modal.classList.add('hidden'); toggleMainContent(false); }); // File Upload Handler const fileInput = document.getElementById('edit-file-upload'); fileInput.addEventListener('change', (e) => { const file = e.target.files[0]; if (file) { const reader = new FileReader(); reader.onload = (readerEvent) => { document.getElementById('edit-url').value = readerEvent.target.result; // Base64 }; reader.readAsDataURL(file); } }); ```