# WASM Antigravity Playbook **Status:** ACTIVE **Owner:** docs-janitor **Created:** 2025-11-24 **Last Reviewed:** 2025-11-24 **Next Review:** 2025-12-08 **Coordination:** [coordination-board entry](./coordination-board.md#2025-11-24-docs-janitor--wasm-docs-consolidation-for-antigravity-gemini) --- ## Purpose Canonical entry point for Antigravity/Gemini when operating the yaze WASM build. This document consolidates build instructions, AI integration notes, filesystem setup, and debug workflows so agents can: 1. Build and serve the WASM app reliably 2. Load ROMs safely with visual progress feedback 3. Debug editor rendering using the yazeDebug API 4. Troubleshoot WASM-specific issues quickly For detailed build troubleshooting, API reference, and roadmap updates, see the reference docs listed at the end. --- ## Quick Start ### Prerequisites Emscripten SDK must be installed and activated: ```bash source /path/to/emsdk/emsdk_env.sh which emcmake # verify it's available ``` ### Build Commands ```bash # Full debug build (SAFE_HEAP + ASSERTIONS for debugging) ./scripts/build-wasm.sh debug # Clean rebuild (after CMakePresets.json changes) ./scripts/build-wasm.sh debug --clean # Incremental debug build (skips CMake cache, 30-60s faster after first build) ./scripts/build-wasm.sh debug --incremental # Release build (optimized for production) ./scripts/build-wasm.sh release # Serve locally (uses custom server with COOP/COEP headers) ./scripts/serve-wasm.sh --force 8080 # Release (default) ./scripts/serve-wasm.sh --debug --force 8080 # Debug build # Manual CMake (alternative) cmake --preset wasm-debug cmake --build build-wasm --parallel ``` **Important:** - Always serve from `dist/`, not `bin/`. The serve script handles this automatically. - The serve script now uses a custom Python server that sets COOP/COEP headers for SharedArrayBuffer support. - Use `--clean` flag after modifying CMakePresets.json to ensure changes take effect. --- ## Attach Antigravity + Initial Setup 1. **Build and serve** (see Quick Start above). 2. **Whitelist in Antigravity:** Allow `http://127.0.0.1:8080` (or your chosen port). 3. **Open the app** in Antigravity's browser and verify initialization: ```javascript // In DevTools console, run these checks: window.Module?.calledRun // should be true window.z3edTerminal?.executeCommand('help') // prints command list toggleCollabConsole() // opens collab pane if needed ``` 4. **Focus terminal:** Press backtick or click the bottom terminal pane. 5. **Clean session (if needed):** `localStorage.clear(); sessionStorage.clear(); location.reload();` --- ## Step-by-Step Workflow for AI Agents (Gemini) This section provides explicit, ordered steps for AI agents to navigate the application. ### Phase 1: Verify Module Ready Before doing anything, verify the WASM module is initialized: ```javascript // Step 1: Check module ready const moduleReady = window.Module?.calledRun === true; const apiReady = window.yaze?.control?.isReady?.() === true; console.log('Module ready:', moduleReady, 'API ready:', apiReady); // If not ready, wait and retry (poll every 500ms) // Expected: both should be true within 5 seconds of page load ``` ### Phase 2: ROM Loading (User-Initiated) **CRITICAL:** ROM loading MUST be initiated by the user through the UI. Do NOT attempt programmatic ROM loading. **Step-by-step for guiding user:** 1. **Locate the Open ROM button**: Look for the folder icon (πŸ“) in the top navigation bar 2. **Click "Open ROM"** or drag a `.sfc`/`.smc` file onto the canvas 3. **Wait for loading overlay**: A progress indicator shows loading stages 4. **Verify ROM loaded**: ```javascript // Check ROM status after user loads ROM const status = window.yaze.control.getRomStatus(); console.log('ROM loaded:', status.loaded, 'Title:', status.title); // Expected: { loaded: true, filename: "zelda3.sfc", title: "THE LEGEND OF ZELDA", ... } ``` **If ROM not loading:** - Check `FilesystemManager.ready === true` - Check browser console for errors - Try: `FS.stat('/roms')` - should not throw ### Phase 3: Dismiss Welcome Screen / Initial View After ROM loads, the app may show a **Welcome screen** or **Settings editor** by default. **Switch to a working editor:** ```javascript // Step 1: Check current editor const current = window.yaze.control.getCurrentEditor(); console.log('Current editor:', current.name); // Step 2: Switch to Dungeon or Overworld editor window.yaze.control.switchEditor('Dungeon'); // OR for async with confirmation: const result = await window.yazeDebug.switchToEditorAsync('Dungeon'); console.log('Switch result:', result); // Expected: { success: true, editor: "Dungeon", session_id: 1 } // Step 3: Verify switch const newEditor = window.yaze.control.getCurrentEditor(); console.log('Now in:', newEditor.name); ``` **Available editors:** `Overworld`, `Dungeon`, `Graphics`, `Palette`, `Sprite`, `Music`, `Message`, `Screen`, `Assembly`, `Hex`, `Agent`, `Settings` ### Phase 4: Make Cards Visible After switching editors, the canvas may appear empty if no cards are visible. **Show essential cards for Dungeon editor:** ```javascript // Option A: Show a predefined card group window.yazeDebug.cards.showGroup('dungeon_editing'); // Shows: room_selector, object_editor, canvas // Option B: Show cards individually window.yazeDebug.cards.show('dungeon.room_selector'); window.yazeDebug.cards.show('dungeon.object_editor'); // Option C: Apply a layout preset window.yaze.control.setCardLayout('dungeon_default'); ``` **Show essential cards for Overworld editor:** ```javascript window.yazeDebug.cards.showGroup('overworld_editing'); // OR window.yaze.control.setCardLayout('overworld_default'); ``` **Query visible cards:** ```javascript const visible = window.yaze.control.getVisibleCards(); console.log('Visible cards:', visible); // Get all available cards for current editor const available = window.yaze.control.getAvailableCards(); console.log('Available cards:', available); ``` ### Phase 5: Verify Working State After completing setup, verify the editor is functional: ```javascript // Full state check const state = window.aiTools.getAppState(); // Logs: ROM Status, Current Editor, Visible Cards, Available Editors // Or get structured data: const snapshot = { rom: window.yaze.control.getRomStatus(), editor: window.yaze.control.getCurrentEditor(), cards: window.yaze.control.getVisibleCards(), session: window.yaze.control.getSessionInfo() }; console.log(JSON.stringify(snapshot, null, 2)); ``` **Expected successful state:** ```json { "rom": { "loaded": true, "title": "THE LEGEND OF ZELDA" }, "editor": { "name": "Dungeon", "active": true }, "cards": ["Room Selector", "Object Editor", ...], "session": { "rom_loaded": true, "current_editor": "Dungeon" } } ``` --- ## Quick Command Reference for AI Agents Copy-paste ready commands for common operations: ```javascript // ========== INITIAL SETUP ========== // 1. Verify ready state window.Module?.calledRun && window.yaze.control.isReady() // 2. Check ROM (after user loads it) window.yaze.control.getRomStatus() // 3. Switch editor (away from welcome/settings) await window.yazeDebug.switchToEditorAsync('Dungeon') // 4. Show cards window.yazeDebug.cards.showGroup('dungeon_editing') // 5. Full state dump window.aiTools.getAppState() // ========== NAVIGATION ========== // Switch editors window.yaze.control.switchEditor('Overworld') window.yaze.control.switchEditor('Dungeon') window.yaze.control.switchEditor('Graphics') // Jump to specific room/map window.aiTools.jumpToRoom(0) // Dungeon room 0 window.aiTools.jumpToMap(0) // Overworld map 0 // ========== CARD CONTROL ========== // Show/hide cards window.yazeDebug.cards.show('dungeon.room_selector') window.yazeDebug.cards.hide('dungeon.object_editor') window.yazeDebug.cards.toggle('dungeon.room_selector') // Card groups window.yazeDebug.cards.showGroup('dungeon_editing') window.yazeDebug.cards.showGroup('overworld_editing') window.yazeDebug.cards.showGroup('minimal') // Layout presets window.yaze.control.setCardLayout('dungeon_default') window.yaze.control.setCardLayout('overworld_default') window.yaze.control.getAvailableLayouts() // ========== DATA ACCESS ========== // Dungeon data window.yaze.data.getRoomTiles(0) window.yaze.data.getRoomObjects(0) window.yaze.data.getRoomProperties(0) // Overworld data window.yaze.data.getMapTiles(0) window.yaze.data.getMapEntities(0) window.yaze.data.getMapProperties(0) // ========== SIDEBAR/PANEL CONTROL ========== window.yazeDebug.sidebar.setTreeView(true) // Expand sidebar window.yazeDebug.sidebar.setTreeView(false) // Collapse to icons window.yazeDebug.rightPanel.open('properties') window.yazeDebug.rightPanel.close() // ========== SCREENSHOTS ========== window.yaze.gui.takeScreenshot() // Returns { dataUrl: "data:image/png;base64,..." } ``` --- ## Build and Serve Strategy ### Minimize Rebuild Time **Use `--incremental` flag** after the first full build: ```bash ./scripts/build-wasm.sh debug --incremental ``` Saves 30-60 seconds by preserving the CMake cache. **Batch your changes:** Make all planned C++ changes first, then rebuild once. JS/CSS changes don't require rebuildingβ€”they're copied from source on startup. **JS/CSS-only changes:** No rebuild needed. Just copy files and refresh: ```bash cp src/web/app.js build-wasm/dist/ cp -r src/web/styles/* build-wasm/dist/styles/ cp -r src/web/components/* build-wasm/dist/components/ cp -r src/web/core/* build-wasm/dist/core/ # Then refresh browser ``` **When to do a full rebuild:** - After modifying CMakeLists.txt or CMakePresets.json - After changing compiler flags or linker options - After adding/removing source files - When incremental build produces unexpected behavior ### Typical Development Workflow 1. **First session:** Full debug build ```bash ./scripts/build-wasm.sh debug ./scripts/serve-wasm.sh --debug --force 8080 ``` 2. **Subsequent C++ changes:** Incremental rebuild ```bash ./scripts/build-wasm.sh debug --incremental # Server auto-detects new files, just refresh browser ``` 3. **JS/CSS only changes:** No rebuild needed ```bash # Copy changed files directly cp src/web/core/filesystem_manager.js build-wasm/dist/core/ # Refresh browser ``` 4. **Verify before lengthy debug session:** ```javascript // In browser console console.log(Module?.calledRun); // should be true console.log(FilesystemManager.ready); // should be true after FS init ``` --- ## Loading ROMs Safely ### ROM Input Methods Supported input formats: - **Drag/drop:** `.sfc`, `.smc`, or `.zip` files (writes to `/roms` in MEMFS) - **File dialog:** Via UI "Open ROM" button (Folder icon in Nav Bar) > [!IMPORTANT] > **Do not use `window.yazeApp.loadRom()` or other JS calls to programmatically open ROMs.** > These methods are unreliable because they bypass the browser's security model for file access or expect files to already exist in the virtual filesystem. > **Always ask the user to load the ROM using the UI.** ### Async JavaScript Calls When using `execute_browser_javascript` or similar tools: - **Async Syntax:** If you need to use `await` (e.g., for `navigator.clipboard.writeText`), wrap your code in an async IIFE: ```javascript (async () => { await someAsyncFunction(); return "done"; })(); ``` - **Top-level await:** Direct top-level `await` is often not supported by runner contexts (like Playwright's `evaluate`). ### Filesystem Readiness WASM mounts IDBFS in C++ during initialization. JS no longer remounts. To verify: ```javascript // Check if filesystem is ready FS && FS.stat('/roms') // should not throw // Or use the debug API window.yazeDebug?.rom.getStatus() // should show { loaded: true, ... } ``` If "Open ROM" appears dead: - Check console for `FS already initialized by C++ runtime` message - Verify `FilesystemManager.ready === true` - If `fsReady` is false, wait for header status to show "Ready" or refresh after build/serve ### After Loading Verify ROM loaded successfully: ```javascript window.yazeDebug?.rom.getStatus() // Expected: { loaded: true, size: 1048576, title: "THE LEGEND OF ZELDA", version: 0 } ``` Loading progress should show overlay UI with messages like "Loading graphics...", "Loading dungeons...", etc. --- ## Directory Reorganization (November 2025) The `src/web/` directory is now organized into logical subdirectories: ``` src/web/ β”œβ”€β”€ app.js # Main application logic β”œβ”€β”€ shell.html # HTML shell template β”œβ”€β”€ components/ # UI Component JS files β”‚ β”œβ”€β”€ collab_console.js β”‚ β”œβ”€β”€ collaboration_ui.js β”‚ β”œβ”€β”€ drop_zone.js # Drag/drop (C++ handler takes precedence) β”‚ β”œβ”€β”€ shortcuts_overlay.js β”‚ β”œβ”€β”€ terminal.js β”‚ └── touch_gestures.js β”œβ”€β”€ core/ # Core infrastructure β”‚ β”œβ”€β”€ config.js # YAZE_CONFIG settings β”‚ β”œβ”€β”€ error_handler.js β”‚ β”œβ”€β”€ filesystem_manager.js # ROM file handling (VFS) β”‚ └── loading_indicator.js # Loading progress UI β”œβ”€β”€ debug/ # Debug utilities β”‚ └── yaze_debug_inspector.cc β”œβ”€β”€ icons/ # PWA icons β”œβ”€β”€ pwa/ # Progressive Web App files β”‚ β”œβ”€β”€ coi-serviceworker.js # SharedArrayBuffer support β”‚ β”œβ”€β”€ manifest.json β”‚ └── service-worker.js └── styles/ # CSS stylesheets β”œβ”€β”€ main.css └── terminal.css ``` **Update all file paths in your prompts and code accordingly.** --- ## Key Systems Reference ### FilesystemManager (`src/web/core/filesystem_manager.js`) Global object for ROM file operations: ```javascript FilesystemManager.ready // Boolean: true when /roms is accessible FilesystemManager.ensureReady() // Check + show status if not ready FilesystemManager.handleRomUpload(file) // Write to VFS + call LoadRomFromWeb FilesystemManager.onFileSystemReady() // Called by C++ when FS is mounted ``` ### WasmLoadingManager (`src/app/platform/wasm/wasm_loading_manager.cc`) C++ system that creates browser UI overlays during asset loading: ```cpp auto handle = WasmLoadingManager::BeginLoading("Task Name"); WasmLoadingManager::UpdateProgress(handle, 0.5f); WasmLoadingManager::UpdateMessage(handle, "Loading dungeons..."); WasmLoadingManager::EndLoading(handle); ``` Corresponding JS functions: `createLoadingIndicator()`, `updateLoadingProgress()`, `removeLoadingIndicator()` ### WasmDropHandler (`src/app/platform/wasm/wasm_drop_handler.cc`) C++ drag/drop handler that: - Registered in `wasm_bootstrap.cc::InitializeWasmPlatform()` - Writes dropped files to `/roms/` and calls `LoadRomFromWeb()` - JS `drop_zone.js` is disabled to avoid conflicts --- ## Debug API: yazeDebug **For detailed API documentation, see** `docs/internal/wasm-yazeDebug-api-reference.md`. The `window.yazeDebug` API provides unified access to WASM debug infrastructure. Key functions: ### Quick Checks ```javascript // Is module ready? window.yazeDebug.isReady() // Complete state dump as JSON window.yazeDebug.dumpAll() // Human-readable summary for AI window.yazeDebug.formatForAI() ``` ### ROM + Emulator ```javascript // ROM load status window.yazeDebug.rom.getStatus() // β†’ { loaded: true, size: 1048576, title: "...", version: 0 } // Read ROM bytes (up to 256) window.yazeDebug.rom.readBytes(0x10000, 32) // β†’ { address: 65536, count: 32, bytes: [...] } // ROM palette lookup window.yazeDebug.rom.getPaletteGroup("dungeon_main", 0) // Emulator CPU/memory state window.yazeDebug.emulator.getStatus() window.yazeDebug.emulator.readMemory(0x7E0000, 16) window.yazeDebug.emulator.getVideoState() ``` ### Graphics + Palettes ```javascript // Graphics sheet diagnostics window.yazeDebug.graphics.getDiagnostics() window.yazeDebug.graphics.detect0xFFPattern() // Regression check // Palette events (DungeonEditor debug) window.yazeDebug.palette.getEvents() window.yazeDebug.palette.getFullState() window.yazeDebug.palette.samplePixel(x, y) // Arena (graphics queue) status window.yazeDebug.arena.getStatus() window.yazeDebug.arena.getSheetInfo(index) ``` ### Editor + Overworld ```javascript // Current editor state window.yazeDebug.editor.getState() window.yazeDebug.editor.executeCommand("cmd") // Overworld data window.yazeDebug.overworld.getMapInfo(mapId) window.yazeDebug.overworld.getTileInfo(mapId, x, y) ``` ### DOM Hooks (for Antigravity) ```javascript document.getElementById('loading-overlay') // Progress UI document.getElementById('status') // Status text document.getElementById('header-status') // Header status document.getElementById('canvas') // Main canvas document.getElementById('rom-input') // File input ``` ### Quick Reference for Antigravity: ```javascript // 1. Capture screenshot for visual analysis const result = window.yaze.gui.takeScreenshot(); const dataUrl = result.dataUrl; // 2. Get room data for comparison const roomData = window.aiTools.getRoomData(); const tiles = window.yaze.data.getRoomTiles(roomData.id || 0); // 3. Check graphics loading status const arena = window.yazeDebug.arena.getStatus(); // 4. Full diagnostic dump async function getDiagnostic(roomId) { const data = { room_id: roomId, objects: window.yaze.data.getRoomObjects(roomId), properties: window.yaze.data.getRoomProperties(roomId), arena: window.yazeDebug?.arena?.getStatus(), visible_cards: window.aiTools.getVisibleCards() }; await navigator.clipboard.writeText(JSON.stringify(data, null, 2)); return data; } ``` --- ## Current Issues and Priorities ### ROM Loading Reliability Past issue: duplicate IDBFS initialization (`app.js:initPersistentFS` vs C++ mount). **FIXED** in November 2025. **Current best practice:** UI paths should only gate on `fsReady` flag or `/roms` existence. Surface helpful status text if initialization is in progress. ### DungeonEditor Object Rendering (WASM) Rendering runs on the main thread (`DungeonEditorV2::DrawRoomTab`), causing UI freezes on large rooms with many objects. **Debug approach:** 1. Use `window.yazeDebug.palette.getEvents()` to capture palette application 2. Use `window.yazeDebug.arena.getStatus()` to check texture queue depth 3. Consider offloading `LoadRoomGraphics` to `WasmWorkerPool` or batching uploads ### Network Blocking Direct `EmscriptenHttpClient` calls on the main thread can stall the UI. Keep AI/HTTP calls inside worker threads or async handlers (current `browser_agent.cc` uses `std::thread`). ### Short-term Tasks (Phase 9 Roadmap) - Palette import for `.pal` files in `wasm_drop_handler` - Deep-linking (`?rom=url`) parsing in `app.js/main.cc` - Improve drag/drop UI feedback --- ## SNES Dungeon Context (for Object Rendering Debugging) ### Load Path **Vanilla (usdasm reference):** ``` Underworld_LoadRoom $01:873A β†’ RoomDraw_DrawFloors β†’ RoomDraw_LayoutPointers (layout pass) β†’ RoomDraw_DrawAllObjects (three object passes): 1. Layout stream 2. Object stream 3. BG2/BG1 pointer swaps β†’ Doors start after sentinel $FFF0 β†’ Stream terminates with $FFFF ``` ### Object Byte Format 3-byte entries: `(x|layer, y|layer, id/size)` **Object Types:** - **Type1:** `$00–$FF` (standard tiles) - **Type2:** `$100–$1FF` (extended tiles) - **Type3:** `$F00–$FFF` (chests, pipes) Position = upper 6 bits of first two bytes Size = lower 2 bits of each byte Layer = lowest bits See usdasm `RoomDraw_RoomObject $01:893C` and `RoomDraw_DoorObject $01:8916` for details. ### Key ROM Pointers (Vanilla) ``` RoomData_ObjectDataPointers = $1F8000 (LoROM) kRoomObjectLayoutPointer = $882D kRoomObjectPointer = $874C kRoomHeaderPointer = $B5DD Palette group table = $0DEC4B Dungeon tile data = $091B52 ``` ### yaze Rendering Pipeline 1. `Room::RenderRoomGraphics()` sets the dungeon palette on BG1/BG2 **before** `ObjectDrawer` writes indexed pixels 2. `ObjectDrawer` chooses BG by layer bits 3. Known issue: BothBG stubs (e.g., `DrawRightwards2x4spaced4_1to16_BothBG`) currently single-buffer (incomplete) 4. Texture uploads deferred via `gfx::Arena::QueueTextureCommand` ### Reference Materials - **ZScream parity:** `DungeonObjectData` defines tiles/routines; `Room_Object.LayerType` supports BG1/BG2/BG3. yaze `ObjectDrawer` mirrors ZScream tables. - **Oracle-of-Secrets:** Custom Object Handler in `Docs/World/Dungeons/Dungeons.md` replaces reserved object IDs with data-driven multi-tile draws (hooks `$31/$32/$54`). Useful precedent for custom draw hooks. --- ## Ready-to-send Prompt for Gemini Copy and customize this prompt when attaching Gemini to the WASM build: ```text You are operating the yaze WASM build at http://127.0.0.1:8080 in Antigravity browser. Stay in-browser; all fixes must be WASM-compatible. === STARTUP SEQUENCE (Follow in order) === NOTE: All commands are SYNCHRONOUS unless marked (async). For async, wrap in: (async () => { await ...; })() STEP 1: Verify module ready window.Module?.calledRun === true window.yaze?.control?.isReady?.() === true β†’ If false, wait and retry STEP 2: ROM Loading (USER MUST DO THIS) - Tell user: "Please click the folder icon (πŸ“) in the nav bar to load a ROM" - Or tell user: "Drag your .sfc/.smc file onto the canvas" - DO NOT attempt programmatic ROM loading - it won't work STEP 3: Verify ROM loaded window.yaze.control.getRomStatus() β†’ Expected: { loaded: true, title: "THE LEGEND OF ZELDA" } STEP 4: Switch away from welcome/settings screen window.yaze.control.switchEditor('Dungeon') // ← SYNC, use this one β†’ Verify: window.yaze.control.getCurrentEditor() STEP 5: Make cards visible (editor may appear empty without this!) window.yaze.control.setCardLayout('dungeon_default') // ← SYNC β†’ Verify: window.yaze.control.getVisibleCards() STEP 6: Confirm working state window.aiTools.getAppState() // Logs full state to console === QUICK REFERENCE === Check state: window.yaze.control.getRomStatus() window.yaze.control.getCurrentEditor() window.yaze.control.getVisibleCards() window.aiTools.getAppState() Switch editors: window.yaze.control.switchEditor('Dungeon') window.yaze.control.switchEditor('Overworld') window.yaze.control.switchEditor('Graphics') Show cards: window.yazeDebug.cards.showGroup('dungeon_editing') window.yazeDebug.cards.showGroup('overworld_editing') window.yaze.control.setCardLayout('dungeon_default') Navigate: window.aiTools.jumpToRoom(0) // Go to dungeon room 0 window.aiTools.jumpToMap(0) // Go to overworld map 0 Screenshot: window.yaze.gui.takeScreenshot() === COMMON ISSUES === "Canvas is blank / no content visible" β†’ Run: window.yazeDebug.cards.showGroup('dungeon_editing') β†’ Or: window.yaze.control.setCardLayout('dungeon_default') "ROM not loading" β†’ User must load via UI (folder icon or drag-drop) β†’ Check: FilesystemManager.ready === true β†’ Check: FS.stat('/roms') should not throw "Still on welcome/settings screen" β†’ Run: window.yaze.control.switchEditor('Dungeon') "API calls return { error: ... }" β†’ Check: window.yaze.control.isReady() β†’ Check: window.yaze.control.getRomStatus().loaded === CURRENT FOCUS === [Describe the specific debugging goal, e.g., "DungeonEditor rendering"] === SUCCESS CRITERIA === 1. ROM loaded (verified via getRomStatus) 2. Editor switched (not on welcome/settings) 3. Cards visible (content displaying) 4. Specific goal achieved ``` --- ## Reference Documentation For detailed information, consult these **three primary WASM docs**: - **Build & Debug:** `docs/internal/agents/wasm-development-guide.md` - Build instructions, CMake config, debugging tips, performance best practices, ImGui ID conflict prevention - **API Reference:** `docs/internal/wasm-yazeDebug-api-reference.md` - Full JavaScript API documentation, including Agent Discoverability Infrastructure (widget overlay, canvas data attributes, card registry APIs) - **This Playbook:** AI agent workflows, Gemini integration, quick start guides **Archived docs** (for historical reference only): `docs/internal/agents/archive/wasm-docs-2025/` --- ## Common Time Wasters to Avoid - Don't rebuild for JS/CSS changes β€” just copy files and refresh - Don't clean CMake cache unless necessary β€” use `--incremental` - Don't restart server after rebuild β€” it serves from `dist/` which gets updated automatically - Don't rebuild to test yazeDebug API β€” it's already in the running module - Batch multiple C++ fixes before rebuilding instead of rebuild-per-fix --- ## Troubleshooting Common Issues ### SharedArrayBuffer / COI Reload Loop If the app is stuck in a reload loop or shows "SharedArrayBuffer unavailable": 1. **Reset COI state:** Add `?reset-coi=1` to the URL (e.g., `http://localhost:8080?reset-coi=1`) 2. **Clear browser state:** - DevTools β†’ Application β†’ Service Workers β†’ Unregister all - DevTools β†’ Application β†’ Storage β†’ Clear site data 3. **Verify server headers:** The serve script should show "Server running with COOP/COEP headers enabled" ### FS Not Available / ROM Loading Fails The `FS` object must be exported from the WASM module. If `window.FS` is undefined: 1. Verify CMakePresets.json includes `EXPORTED_RUNTIME_METHODS=['FS','ccall','cwrap',...]` 2. Check console for `[FilesystemManager] Aliasing Module.FS to window.FS` 3. If missing, rebuild with `--clean` flag ### Thread Pool Exhausted If you see "Tried to spawn a new thread, but the thread pool is exhausted": - Current setting: `PTHREAD_POOL_SIZE=8` in CMakePresets.json - If still insufficient, increase the value and rebuild with `--clean` ### Canvas Resize Crash If resizing the terminal panel causes WASM abort with "attempt to write non-integer": - This was fixed by ensuring `Math.floor()` is used for canvas dimensions - Verify `src/web/app.js` and `src/web/shell.html` both use integer values for resize ### Missing CSS Files (404) CSS files are in `src/web/styles/`. Component JS files that dynamically load CSS must use the correct path: ```javascript // Correct: link.href = 'styles/shortcuts_overlay.css'; // Wrong: link.href = 'shortcuts_overlay.css'; ``` --- ## Key C++ Files for Debugging - `src/app/editor/dungeon/dungeon_editor_v2.cc` β€” Main dungeon editor - `src/app/editor/dungeon/dungeon_room_loader.cc` β€” Room loading logic - `src/zelda3/dungeon/room.cc` β€” Room data and rendering - `src/zelda3/dungeon/room_object.cc` β€” Object drawing - `src/app/gfx/resource/arena.cc` β€” Graphics sheet management - `src/app/platform/wasm/wasm_loading_manager.cc` β€” Loading progress UI - `src/app/platform/wasm/wasm_drop_handler.cc` β€” Drag/drop file handling - `src/app/platform/wasm/wasm_control_api.cc` β€” JS API implementation