16 KiB
WASM / Web Agent Integration Status
Last Updated: November 25, 2025 Status: Functional MVP with Agent APIs (ROM loading fixed, loading progress added, control APIs implemented, performance optimizations applied)
Overview
This document tracks the development state of the yaze WASM web application, specifically focusing on the AI Agent integration (z3ed console) and the modern UI overhaul.
1. Completed Features
ROM Loading & Initialization (November 2025 Fixes)
- ROM File Validation (
rom_file_manager.cc):- Fixed minimum ROM size check from 1MB to 512KB (was rejecting valid 1MB Zelda 3 ROMs)
- CMake WASM Configuration (
app.cmake):- Added
MODULARIZE=1andEXPORT_NAME='createYazeModule'to matchapp.jsexpectations - Added missing exports:
_yazeHandleDroppedFile,_yazeHandleDropError,_yazeHandleDragEnter,_yazeHandleDragLeave,_malloc,_free - Added missing runtime methods:
lengthBytesUTF8,IDBFS,allocateUTF8
- Added
- JavaScript Fixes (
filesystem_manager.js):- Fixed
Module.ccallreturn type from'null'(string) tonull - Fixed direct function fallback to properly allocate/free memory for string parameters
- Fixed
- Drop Zone (
drop_zone.js):- Disabled duplicate auto-initialization (conflicted with C++ handler)
- Now delegates to
FilesystemManager.handleRomUploadinstead of calling non-existent function
- Loading Progress (
editor_manager.cc):- Added
WasmLoadingManagerintegration toLoadAssets() - Shows progress for each editor: "Loading overworld...", "Loading dungeons...", etc.
- Added
- UI Streamlining (
shell.html,app.js):- Removed HTML welcome screen - canvas is always visible
- Loading overlay shows during initialization with status messages
AI Agent Integration
- Core Bridge (
wasm_terminal_bridge.cc):- Exposes
Z3edProcessCommandto JavaScript. - Exposes
GetGlobalBrowserAIService()andGetGlobalRom()to C++ handlers.
- Exposes
- Browser Agent (
browser_agent.cc):agent chat: Fully functional with conversation history. Usesstd::threadfor non-blocking AI calls.agent plan: Generates text-based implementation plans (asynchronous).agent diff: Shows the "pending plan" (conceptual diff).agent list/describe: Introspects ROM resources viaResourceCatalog.agent todo: Fully implemented with persistent storage.
- Browser AI Service (
src/cli/service/ai/browser_ai_service.cc):- Implements
AIServiceinterface for browser-based AI calls - Uses
IHttpClientfrom network abstraction layer (CORS-compatible) - Supports Gemini API (text and vision models)
- Secures API keys via sessionStorage (cleared on tab close)
- Comprehensive error handling with
absl::Status
- Implements
- Browser Storage (
src/app/platform/wasm/wasm_browser_storage.cc):- Non-hardcoded API key management via sessionStorage/localStorage
- User-provided keys, never embedded in binary
- Namespaced storage to avoid conflicts
- Persistence (
todo_manager.cc):- Updated to use
WasmStorage(IndexedDB) when compiled for Emscripten. TODOs persist across reloads.
- Updated to use
UI & UX
- Drag & Drop (
wasm_drop_handler.cc):- Supports
.sfc,.smc,.zip. - Automatically writes to
/roms/in MEMFS and loads the ROM. - Stubbed support for
.pal/.tpl.
- Supports
- Modern Interface:
main.css: Unified design system (VS Code dark theme variables).app.js: Extracted logic fromshell.html. Handles terminal resize, zoom, and PWA updates.- Components:
terminal.css,collab_console.css, etc., updated to use CSS variables.
WASM Control APIs (November 2025)
The WASM build now exposes comprehensive JavaScript APIs for programmatic control, enabling LLM agents with DOM access to interact with the editor.
Editor State APIs (window.yaze.editor)
getSnapshot(): Get current editor state (type, ROM status, active data)getCurrentRoom(): Get dungeon room info (room_id, active_rooms, visible_cards)getCurrentMap(): Get overworld map info (map_id, world, world_name)getSelection(): Get current selection in active editor
Read-only Data APIs (window.yaze.data)
- Dungeon Data:
getRoomTiles(roomId)- Get room tile data (layer1, layer2)getRoomObjects(roomId)- Get objects in a roomgetRoomProperties(roomId)- Get room properties (music, palette, tileset)
- Overworld Data:
getMapTiles(mapId)- Get map tile datagetMapEntities(mapId)- Get entities (entrances, exits, items, sprites)getMapProperties(mapId)- Get map properties (gfx_group, palette, area_size)
- Palette Data:
getPalette(group, id)- Get palette colorsgetPaletteGroups()- List available palette groups
GUI Automation APIs (window.yaze.gui)
- Element Discovery:
discover()- List all interactive UI elements with metadatagetElementBounds(id)- Get element position and dimensions (backed byWidgetIdRegistry)waitForElement(id, timeout)- Async wait for element to appear
- Interaction:
click(target)- Click by element ID or {x, y} coordinatesdoubleClick(target)- Double-clickdrag(from, to, steps)- Drag operationpressKey(key, modifiers)- Send keyboard inputtype(text, delay)- Type text stringscroll(dx, dy)- Scroll canvas
- Utility:
takeScreenshot(format)- Capture canvas as base64getCanvasInfo()- Get canvas dimensionsisReady()- Check if GUI API is ready
Widget Tracking Infrastructure (November 2025):
The WidgetIdRegistry system tracks all ImGui widget bounds in real-time:
- Real-time Bounds:
GetUIElementTree()andGetUIElementBounds()query live widget positions viaWidgetIdRegistry - Frame Lifecycle: Integrated into
Controller::OnLoad()withBeginFrame()andEndFrame()hooks - Bounds Data: Includes
min_x,min_y,max_x,max_yfor accurate GUI automation - Metadata: Returns
imgui_id,last_seen_frame, widget type, visibility, enabled state - Key Files:
src/app/gui/automation/widget_id_registry.h,src/app/gui/automation/widget_measurement.h
Control APIs (window.yaze.control)
- Editor Control:
switchEditor(),getCurrentEditor(),getAvailableEditors() - Card Control:
openCard(),closeCard(),toggleCard(),getVisibleCards() - Layout Control:
setCardLayout(),getAvailableLayouts(),saveCurrentLayout() - Menu Actions:
triggerMenuAction(),getAvailableMenuActions() - Session Control:
getSessionInfo(),createSession(),switchSession() - ROM Control:
getRomStatus(),readRomBytes(),writeRomBytes(),saveRom()
Extended UI Control APIs (November 2025)
Async Editor Switching (yazeDebug.switchToEditorAsync):
Promise-based editor switching with operation tracking for reliable LLM automation.
- Returns
Promise<{success, editor, session_id, error}>after editor transition completes - Supports all 14 editor types: Assembly, Dungeon, Graphics, Music, Overworld, Palette, Screen, Sprite, Message, Hex, Agent, Settings, World, Map
- 5-second timeout with proper error reporting
Card Control API (yazeDebug.cards):
show(cardId)- Show a specific card by ID (e.g., "dungeon.room_selector")hide(cardId)- Hide a specific cardtoggle(cardId)- Toggle card visibilitygetState()- Get visibility state of all cardsgetInCategory(category)- List cards in a category (dungeon, overworld, etc.)showGroup(groupName)- Show predefined card groups (dungeon_editing, overworld_editing, etc.)hideGroup(groupName)- Hide predefined card groupsgetGroups()- List available card groups
Sidebar Control API (yazeDebug.sidebar):
isTreeView()- Check if tree view mode is activesetTreeView(enabled)- Switch between tree view (200px) and icon mode (48px)toggle()- Toggle between view modesgetState()- Get sidebar state (mode, width, collapsed)
Right Panel Control API (yazeDebug.rightPanel):
open(panelName)- Open specific panel: properties, agent, proposals, settings, helpclose()- Close current paneltoggle(panelName)- Toggle panel visibilitygetState()- Get panel state (active, expanded, width)openProperties()- Convenience method for properties panelopenAgent()- Convenience method for agent chat panel
Tree View Sidebar: New hierarchical sidebar mode (200px wide) with:
- Category icons and expandable tree nodes
- Checkboxes for each card with visibility toggles
- Visible count badges per category
- "Show All" / "Hide All" buttons per category
- Toggle button to switch to icon mode
Selection Properties Panel: New right-side panel for editing selected entities:
- Context-aware property display based on selection type
- Supports dungeon rooms, objects, sprites, entrances
- Supports overworld maps, tiles, sprites, entrances, exits, items
- Supports graphics sheets and palettes
- Position/size editors with clamping
- Byte/word property editors with hex display
- Flag property editors with checkboxes
- Advanced and raw data toggles
Key Files:
src/app/platform/wasm/wasm_control_api.cc- C++ implementationsrc/app/platform/wasm/wasm_control_api.h- API declarationssrc/web/core/agent_automation.js- GUI automation layersrc/web/debug/yaze_debug_inspector.cc- Extended WASM bindingssrc/app/editor/system/editor_card_registry.cc- Tree view sidebar implementationsrc/app/editor/ui/right_panel_manager.cc- Right panel managementsrc/app/editor/ui/selection_properties_panel.cc- Properties panel implementation
Performance Optimizations & Bug Fixes (November 2025)
A comprehensive audit and fix of the WASM web layer was performed to address performance issues, memory leaks, and race conditions.
JavaScript Performance Fixes (app.js)
- Event Sanitization Optimization:
- Removed redundant document-level event listeners (canvas-only now)
- Added WeakMap caching to avoid re-sanitizing the same event objects
- Optimized to check only relevant properties per event type category
- ~50% reduction in sanitization overhead
- Console Log Buffer:
- Replaced O(n)
Array.shift()with O(1) circular buffer implementation - Uses modulo arithmetic for constant-time log rotation
- Replaced O(n)
- Polling Cleanup:
- Added timeout tracking and max retry limits for module initialization
- Proper interval cleanup when components are destroyed
- Added
window.YAZE_MODULE_READYflag for reliable initialization detection
Memory Leak Fixes
- Service Worker Cache (
service-worker.js):- Added
MAX_RUNTIME_CACHE_SIZE(50 entries) with LRU eviction - New
trimRuntimeCache()function enforces size limits addToRuntimeCacheWithEviction()wrapper for cache operations
- Added
- Confirmation Callbacks (
wasm_error_handler.cc):- Added
CallbackEntrystruct with timestamps for timeout tracking - Auto-cleanup of callbacks older than 5 minutes
- Page unload handler via
js_register_cleanup_handler()
- Added
- Loading Indicators (
loading_indicator.js):- Added try-catch error handling to ensure cleanup on errors
- Stale indicator cleanup (5-minute timeout)
- Periodic cleanup interval with proper lifecycle management
Race Condition Fixes
- Module Initialization (
app.js):- Added
window.YAZE_MODULE_READYflag set AFTER promise resolves - Updated
waitForModule()to check both Module existence AND ready flag - Prevents code from seeing incomplete Module state
- Added
- FS Ready State (
filesystem_manager.js):- Restructured
initPersistentFS()with synchronous lock pattern - Promise created immediately before async operations
- Eliminates race where two calls could create duplicate promises
- Restructured
- Redundant FS Exposure:
- Added
fsExposedflag to prevent wasteful redundant calls - Reduced from 3 setTimeout calls to 1 conditional retry
- Added
C++ WASM Fixes
- Memory Safety (
wasm_storage.cc):- Added
free(data_ptr)in error paths ofLoadRom()to prevent memory leaks - Ensures allocated memory is freed even when operations fail
- Added
- Cleanup Handlers (
wasm_error_handler.cc):- Added
cleanupConfirmCallbacks()function for page unload - Registered via
js_register_cleanup_handler()inInitialize()
- Added
Drop Zone Optimization (drop_zone.js, filesystem_manager.js)
- Eliminated Double File Reading:
- Added new
FilesystemManager.handleRomData(filename, data)method - Accepts pre-read
Uint8Arrayinstead ofFileobject - Drop zone now passes already-read data instead of re-reading
- Reduces CPU and memory usage for ROM uploads
- Added new
Key Files Modified:
src/web/app.js- Event sanitization, console buffer, module initsrc/web/core/filesystem_manager.js- FS init race fix, handleRomDatasrc/web/core/loading_indicator.js- Stale cleanup, error handlingsrc/web/components/drop_zone.js- Use handleRomDatasrc/web/pwa/service-worker.js- Cache evictionsrc/app/platform/wasm/wasm_storage.cc- Memory free on errorsrc/app/platform/wasm/wasm_error_handler.cc- Callback cleanup
2. Technical Debt & Known Issues
SimpleChatSession: This C++ class relies onVimModeand raw TTY input, which is incompatible with WASM. We bypassed this by implementing a customHandleChatCommandinbrowser_agent.cc. The originalSimpleChatSessionremains unused in the browser build.- Emscripten Fetch Blocking: The
EmscriptenHttpClientimplementation contains acv.wait()which blocks the main thread. We worked around this by spawningstd::threadin the command handlers, but the HTTP client itself remains synchronous-blocking if called directly on the main thread. - Single-Threaded Rendering: Dungeon graphics loading happens on the main thread (
DungeonEditorV2::DrawRoomTab), causing UI freezes on large ROMs.
3. Next Steps / Roadmap
Short Term
- Palette Import: Implement the logic in
wasm_drop_handler.cc(ormain.cccallback) to parse.palfiles and apply them toPaletteManager. - Deep Linking: Add logic to
app.jsandmain.ccto parse URL query parameters (e.g.,?rom=url) for easy sharing.
Medium Term
- In-Memory Proposal Registry:
- Implement a
WasmProposalRegistrythat mimics the file-basedProposalRegistry. - Store "sandboxes" as
Romcopies in memory (or IndexedDB blobs). - Enable
agent applyto execute the plans generated byagent plan.
- Implement a
- Multithreaded Graphics:
- Refactor
DungeonEditorV2to useWasmWorkerPoolforLoadRoomGraphics. - Requires decoupling
Roomdata structures from the loading logic to pass data across threads safely.
- Refactor
4. Key Files
-
C++ Logic:
src/cli/handlers/agent/browser_agent.cc(Agent commands)src/cli/wasm_terminal_bridge.cc(JS <-> C++ Bridge)src/app/platform/wasm/wasm_drop_handler.cc(File drag & drop)src/app/platform/wasm/wasm_control_api.cc(Control API implementation)src/app/platform/wasm/wasm_control_api.h(Control API declarations)src/cli/service/agent/todo_manager.cc(Persistence logic)
-
Web Frontend:
src/web/shell.html(Entry point)src/web/app.js(Main UI logic)src/web/core/agent_automation.js(GUI Automation layer)src/web/styles/main.css(Theme definitions)src/web/components/terminal.js(Console UI component)src/web/components/collaboration_ui.js(Collaboration UI)