backend-infra-engineer: Post v0.3.9-hotfix7 snapshot (build cleanup)

This commit is contained in:
scawful
2025-12-22 00:20:49 +00:00
parent 2934c82b75
commit 5c4cd57ff8
1259 changed files with 239160 additions and 43801 deletions

View File

@@ -0,0 +1,135 @@
# WASM Planning Documentation Archive
**Date Archived:** November 24, 2025
**Archived By:** Documentation Janitor
This directory contains WASM development planning documents that represent historical design decisions and feature roadmaps. Most content has been superseded by implementation and integration into `docs/internal/wasm_dev_status.md`.
## Archived Documents
### 1. wasm-network-support-plan.md
- **Original Purpose:** Detailed plan for implementing browser-compatible networking (Phases 1-5)
- **Status:** Superseded by implementation
- **Why Archived:** Network abstraction and WASM implementations have been completed; this planning document is no longer needed
- **Current Reference:** See `wasm_dev_status.md` Section 1.1 (ROM Loading & Initialization)
### 2. wasm-web-features-roadmap.md
- **Original Purpose:** Comprehensive feature roadmap (Phases 1-14)
- **Status:** Mostly completed or planning-stage
- **Why Archived:** Long-term planning document that predates actual implementation; many features are now in wasm_dev_status.md
- **Current Reference:** See `wasm_dev_status.md` Sections 1-4 for completed features
### 3. wasm-web-app-enhancements-plan.md
- **Original Purpose:** Detailed Phase 1-8 implementation plan
- **Status:** Most phases completed
- **Why Archived:** Highly structured planning document; actual implementations supersede these plans
- **Current Reference:** See `wasm_dev_status.md` for current status of all phases
### 4. wasm-ai-integration-summary.md
- **Original Purpose:** Summary of Phase 5 AI Service Integration implementation
- **Status:** Consolidated into main status document
- **Why Archived:** Content merged into `wasm_dev_status.md` AI Agent Integration section
- **Current Reference:** See `wasm_dev_status.md` Section 1 (Completed Features → AI Agent Integration)
### 5. wasm-widget-tracking-implementation.md
- **Original Purpose:** Detailed implementation notes for widget bounds tracking
- **Status:** Consolidated into main status document
- **Why Archived:** Implementation details merged into `wasm_dev_status.md` Control APIs section
- **Current Reference:** See `wasm_dev_status.md` Section 1.4 (WASM Control APIs → Widget Tracking Infrastructure)
## Content Consolidation
The following information from archived documents has been consolidated into active documentation:
| Original Document | Content Moved To | Location |
|---|---|---|
| wasm-network-support-plan.md | wasm_dev_status.md | Section 1.1, Section 4 (Key Files) |
| wasm-web-features-roadmap.md | wasm_dev_status.md | Section 1 (Completed Features) |
| wasm-web-app-enhancements-plan.md | wasm_dev_status.md | Section 1 (Completed Features) |
| wasm-ai-integration-summary.md | wasm_dev_status.md | Section 1 (AI Agent Integration) |
| wasm-widget-tracking-implementation.md | wasm_dev_status.md | Section 1.4 (Widget Tracking Infrastructure) |
## Active WASM Documentation
The following documents remain in `docs/internal/` and are actively maintained:
1. **wasm_dev_status.md** - CANONICAL STATUS DOCUMENT
- Current implementation status (updated Nov 24, 2025)
- All completed features with file references
- Technical debt and known issues
- Roadmap for next steps
2. **wasm-debug-infrastructure.md** - HIGH-LEVEL DEBUGGING OVERVIEW
- Debugging architecture and philosophy
- File system fixes with explanations
- Known limitations
- Cross-references to detailed API docs
3. **wasm-yazeDebug-api-reference.md** - DETAILED API REFERENCE
- Complete JavaScript API reference for `window.yazeDebug`
- Authoritative source for all debug functions
- Usage examples for each API section
- Palette, ROM, overworld, arena, emulator debugging
4. **wasm_dungeon_debugging.md** - QUICK REFERENCE GUIDE
- Short, practical debugging tips
- God mode console inspector usage
- Command line bridge reference
- Feature parity notes between WASM and native builds
5. **debugging-wasm-memory-errors.md** - TECHNICAL REFERENCE
- Memory debugging techniques
- SAFE_HEAP usage
- Common pitfalls and fixes
- Function mapping methods
## How to Use This Archive
If you need historical context about WASM development decisions:
1. Start with the relevant archived document
2. Check the "Current Reference" section for where the content moved
3. Consult the active documentation for implementation details
When searching for WASM documentation, use this hierarchy:
1. **wasm_dev_status.md** - Status and overview (start here)
2. **wasm-debug-infrastructure.md** - Debugging overview
3. **wasm-yazeDebug-api-reference.md** - Detailed debug API
4. **wasm_dungeon_debugging.md** - Quick reference for dungeon editor
5. **debugging-wasm-memory-errors.md** - Memory debugging specifics
## Rationale for Archival
The WASM codebase evolved rapidly from November 2024 to November 2025:
- **Planning Phase** (early 2024): Detailed roadmaps and enhancement plans created
- **Implementation Phase** (mid 2024 - Nov 2025): Features implemented incrementally
- **Integration Phase** (current): All systems working, focus on maintenance and refinement
Archived documents represented the planning phase. As implementation completed, their value shifted from prescriptive (what to build) to historical (how we decided to build it). Consolidating information into `wasm_dev_status.md` provides:
- Single source of truth for current status
- Easier maintenance (updates in one place)
- Clearer navigation for developers
- Better signal-to-noise ratio
## Future Archival Guidance
When new WASM documentation is created:
- Keep planning/roadmap docs current or archive them promptly
- Consolidate implementation summaries into main status document
- Use high-level docs (like wasm-debug-infrastructure.md) for architecture overview
- Use detailed reference docs (like wasm-yazeDebug-api-reference.md) for API details
- Maintain clear cross-references between related docs
---
**Archive Directory Structure:**
```
docs/internal/agents/archive/wasm-planning-2025/
├── README.md (this file)
├── wasm-network-support-plan.md
├── wasm-web-features-roadmap.md
├── wasm-web-app-enhancements-plan.md
├── wasm-ai-integration-summary.md
└── wasm-widget-tracking-implementation.md
```

View File

@@ -0,0 +1,199 @@
# WASM AI Service Integration Summary
## Overview
This document summarizes the implementation of Phase 5: AI Service Integration for WASM web build, as specified in the wasm-web-app-enhancements-plan.md.
## Files Created
### 1. Browser AI Service (`src/cli/service/ai/`)
#### `browser_ai_service.h`
- **Purpose**: Browser-based AI service interface for WASM builds
- **Key Features**:
- Implements `AIService` interface for consistency with native builds
- Uses `IHttpClient` from network abstraction layer
- Supports Gemini API for text generation
- Provides vision model support for image analysis
- Manages API keys securely via sessionStorage
- CORS-compliant HTTP requests
- Proper error handling with `absl::Status`
- **Compilation**: Only compiled when `__EMSCRIPTEN__` is defined
#### `browser_ai_service.cc`
- **Purpose**: Implementation of browser AI service
- **Key Features**:
- `GenerateResponse()` for single prompts and conversation history
- `AnalyzeImage()` for vision model support
- JSON request/response handling with nlohmann/json
- Comprehensive error handling and status code mapping
- Debug logging to browser console
- Support for multiple Gemini models (2.0 Flash, 1.5 Pro, etc.)
- Proper handling of API rate limits and quotas
### 2. Browser Storage (`src/app/platform/wasm/`)
#### `wasm_browser_storage.h`
- **Purpose**: Browser storage wrapper for API keys and settings
- **Note**: This is NOT actually secure storage - uses standard localStorage/sessionStorage
- **Key Features**:
- Dual storage modes: sessionStorage (default) and localStorage
- API key management: Store, Retrieve, Clear, Check existence
- Generic secret storage for other sensitive data
- Storage quota tracking
- Bulk operations (list all keys, clear all)
- Browser storage availability checking
#### `wasm_browser_storage.cc`
- **Purpose**: Implementation using Emscripten JavaScript interop
- **Key Features**:
- JavaScript bridge functions using `EM_JS` macros
- SessionStorage access (cleared on tab close)
- LocalStorage access (persistent)
- Prefix-based key namespacing (`yaze_secure_api_`, `yaze_secure_secret_`)
- Error handling for storage exceptions
- Memory management for JS string conversions
## Build System Updates
### 1. CMake Configuration Updates
#### `src/cli/agent.cmake`
- Modified to create a minimal `yaze_agent` library for WASM builds
- Includes browser AI service sources
- Links with network abstraction layer (`yaze_net`)
- Enables JSON support for API communication
#### `src/app/app_core.cmake`
- Added `wasm_browser_storage.cc` to WASM platform sources
- Integrated with existing WASM file system and loading manager
#### `src/CMakeLists.txt`
- Updated to include `net_library.cmake` for all builds (including WASM)
- Network library now provides WASM-compatible HTTP client
#### `CMakePresets.json`
- Added new `wasm-ai` preset for testing AI features in WASM
- Configured with AI runtime enabled and Fetch API flags
## Integration with Existing Systems
### Network Abstraction Layer
- Leverages existing `IHttpClient` interface
- Uses `EmscriptenHttpClient` for browser-based HTTP requests
- Supports CORS-compliant requests to Gemini API
### AI Service Interface
- Implements standard `AIService` interface
- Compatible with existing agent response structures
- Supports tool calls and structured responses
### WASM Platform Support
- Integrates with existing WASM error handler
- Works alongside WASM storage and file dialog systems
- Compatible with progressive loading manager
## API Key Security
### Storage Security Model
1. **SessionStorage (Default)**:
- Keys stored in browser memory
- Automatically cleared when tab closes
- No persistence across sessions
- Recommended for security
2. **LocalStorage (Optional)**:
- Persistent storage
- Survives browser restarts
- Less secure but more convenient
- User choice based on preference
### Security Considerations
- Keys never hardcoded in binary
- Keys prefixed to avoid conflicts
- No encryption currently (future enhancement)
- Browser same-origin policy provides isolation
## Usage Example
```cpp
#ifdef __EMSCRIPTEN__
#include "cli/service/ai/browser_ai_service.h"
#include "app/net/wasm/emscripten_http_client.h"
#include "app/platform/wasm/wasm_browser_storage.h"
// Store API key from user input
WasmBrowserStorage::StoreApiKey("gemini", user_api_key);
}
// Create AI service
BrowserAIConfig config;
config.api_key = WasmBrowserStorage::RetrieveApiKey("gemini").value();
config.model = "gemini-2.5-flash";
auto http_client = std::make_unique<EmscriptenHttpClient>();
BrowserAIService ai_service(config, std::move(http_client));
// Generate response
auto response = ai_service.GenerateResponse("Explain the Zelda 3 ROM format");
#endif
```
## Testing
### Test File: `test/browser_ai_test.cc`
- Verifies secure storage operations
- Tests AI service creation
- Validates model listing
- Checks error handling
### Build and Test Commands
```bash
# Configure with AI support
cmake --preset wasm-ai
# Build
cmake --build build_wasm_ai
# Run in browser
emrun build_wasm_ai/yaze.html
```
## CORS Considerations
### Gemini API
- ✅ Works with browser fetch (Google APIs support CORS)
- ✅ No proxy required
- ✅ Direct browser-to-API communication
### Ollama (Future)
- ⚠️ Requires `--cors` flag on Ollama server
- ⚠️ May need proxy for local instances
- ⚠️ Security implications of CORS relaxation
## Future Enhancements
1. **Encryption**: Add client-side encryption for stored API keys
2. **Multiple Providers**: Support for OpenAI, Anthropic APIs
3. **Streaming Responses**: Implement streaming for better UX
4. **Offline Caching**: Cache AI responses for offline use
5. **Web Worker Integration**: Move AI calls to background thread
## Limitations
1. **Browser Security**: Subject to browser security policies
2. **CORS Restrictions**: Limited to CORS-enabled APIs
3. **Storage Limits**: ~5-10MB for sessionStorage/localStorage
4. **No File System**: Cannot access local models
5. **Network Required**: No offline AI capabilities
## Conclusion
The WASM AI service integration successfully brings browser-based AI capabilities to yaze. The implementation:
- ✅ Provides secure API key management
- ✅ Integrates cleanly with existing architecture
- ✅ Supports both text and vision models
- ✅ Handles errors gracefully
- ✅ Works within browser security constraints
This enables users to leverage AI assistance for ROM hacking directly in their browser without needing to install local AI models or tools.

View File

@@ -0,0 +1,415 @@
# WASM Network Support Plan for yaze
## Executive Summary
This document outlines the architectural changes required to enable AI services (Gemini/Ollama) and WebSocket collaboration features in the browser-based WASM build of yaze. The main challenge is replacing native networking libraries (cpp-httplib, OpenSSL, curl) with browser-compatible APIs provided by Emscripten.
## Current Architecture Analysis
### 1. Gemini AI Service (`src/cli/service/ai/gemini_ai_service.cc`)
**Current Implementation:**
- Uses `httplib` (cpp-httplib) for HTTPS requests with OpenSSL support
- Falls back to `curl` command via `popen()` for API calls
- Depends on OpenSSL for SSL/TLS encryption
- Uses base64 encoding for image uploads
**Key Dependencies:**
- `httplib.h` - C++ HTTP library
- OpenSSL - SSL/TLS support
- `popen()`/`pclose()` - Process execution for curl
- File system access for temporary JSON files
### 2. Ollama AI Service (`src/cli/service/ai/ollama_ai_service.cc`)
**Current Implementation:**
- Uses `httplib::Client` for HTTP requests to local Ollama server
- Communicates over HTTP (not HTTPS) on localhost:11434
- JSON parsing with nlohmann/json
**Key Dependencies:**
- `httplib.h` - C++ HTTP library
- No SSL/TLS requirement (local HTTP only)
### 3. WebSocket Client (`src/app/net/websocket_client.cc`)
**Current Implementation:**
- Uses `httplib::Client` as a placeholder (not true WebSocket)
- Currently implements HTTP POST fallback instead of WebSocket
- Conditional OpenSSL support for secure connections
- Thread-based receive loop
**Key Dependencies:**
- `httplib.h` - C++ HTTP library
- OpenSSL (optional) - For WSS support
- `std::thread` - For receive loop
- Platform-specific socket libraries (ws2_32 on Windows)
### 4. HTTP Server (`src/cli/service/api/http_server.cc`)
**Current Implementation:**
- Uses `httplib::Server` for REST API endpoints
- Runs in separate thread
- Provides health check and model listing endpoints
**Key Dependencies:**
- `httplib.h` - C++ HTTP server
- `std::thread` - For server thread
## Emscripten Capabilities
### Available APIs:
1. **Fetch API** (`emscripten_fetch()`)
- Asynchronous HTTP/HTTPS requests
- Supports GET, POST, PUT, DELETE
- CORS-aware
- Can handle binary data and streams
2. **WebSocket API**
- Native browser WebSocket support
- Accessible via Emscripten's WebSocket wrapper
- Full duplex communication
- Binary and text message support
3. **Web Workers** (via pthread emulation)
- Background processing
- Shared memory support with SharedArrayBuffer
- Can handle async operations
## Required Changes
### Phase 1: Abstract Network Layer
Create platform-agnostic network interfaces:
```cpp
// src/app/net/http_client.h
class IHttpClient {
public:
virtual ~IHttpClient() = default;
virtual absl::StatusOr<HttpResponse> Get(const std::string& url,
const Headers& headers) = 0;
virtual absl::StatusOr<HttpResponse> Post(const std::string& url,
const std::string& body,
const Headers& headers) = 0;
};
// src/app/net/websocket.h
class IWebSocket {
public:
virtual ~IWebSocket() = default;
virtual absl::Status Connect(const std::string& url) = 0;
virtual absl::Status Send(const std::string& message) = 0;
virtual void OnMessage(std::function<void(const std::string&)> callback) = 0;
};
```
### Phase 2: Native Implementation
Keep existing implementations for native builds:
```cpp
// src/app/net/native/httplib_client.cc
class HttpLibClient : public IHttpClient {
// Current httplib implementation
};
// src/app/net/native/httplib_websocket.cc
class HttpLibWebSocket : public IWebSocket {
// Current httplib-based implementation
};
```
### Phase 3: Emscripten Implementation
Create browser-compatible implementations:
```cpp
// src/app/net/wasm/emscripten_http_client.cc
#ifdef __EMSCRIPTEN__
class EmscriptenHttpClient : public IHttpClient {
absl::StatusOr<HttpResponse> Get(const std::string& url,
const Headers& headers) override {
emscripten_fetch_attr_t attr;
emscripten_fetch_attr_init(&attr);
strcpy(attr.requestMethod, "GET");
attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
// Set headers
std::vector<const char*> header_strings;
for (const auto& [key, value] : headers) {
header_strings.push_back(key.c_str());
header_strings.push_back(value.c_str());
}
header_strings.push_back(nullptr);
attr.requestHeaders = header_strings.data();
// Synchronous fetch (blocks until complete)
emscripten_fetch_t* fetch = emscripten_fetch(&attr, url.c_str());
HttpResponse response;
response.status = fetch->status;
response.body = std::string(fetch->data, fetch->numBytes);
emscripten_fetch_close(fetch);
return response;
}
absl::StatusOr<HttpResponse> Post(const std::string& url,
const std::string& body,
const Headers& headers) override {
emscripten_fetch_attr_t attr;
emscripten_fetch_attr_init(&attr);
strcpy(attr.requestMethod, "POST");
attr.attributes = EMSCRIPTEN_FETCH_LOAD_TO_MEMORY;
attr.requestData = body.c_str();
attr.requestDataSize = body.length();
// Similar implementation as Get()
// ...
}
};
#endif
// src/app/net/wasm/emscripten_websocket.cc
#ifdef __EMSCRIPTEN__
#include <emscripten/websocket.h>
class EmscriptenWebSocket : public IWebSocket {
EMSCRIPTEN_WEBSOCKET_T socket_;
absl::Status Connect(const std::string& url) override {
EmscriptenWebSocketCreateAttributes attrs = {
url.c_str(),
nullptr, // protocols
EM_TRUE // createOnMainThread
};
socket_ = emscripten_websocket_new(&attrs);
if (socket_ <= 0) {
return absl::InternalError("Failed to create WebSocket");
}
// Set callbacks
emscripten_websocket_set_onopen_callback(socket_, this, OnOpenCallback);
emscripten_websocket_set_onmessage_callback(socket_, this, OnMessageCallback);
emscripten_websocket_set_onerror_callback(socket_, this, OnErrorCallback);
return absl::OkStatus();
}
absl::Status Send(const std::string& message) override {
EMSCRIPTEN_RESULT result = emscripten_websocket_send_text(
socket_, message.c_str(), message.length());
if (result != EMSCRIPTEN_RESULT_SUCCESS) {
return absl::InternalError("Failed to send WebSocket message");
}
return absl::OkStatus();
}
};
#endif
```
### Phase 4: Factory Pattern
Create factories to instantiate the correct implementation:
```cpp
// src/app/net/network_factory.cc
std::unique_ptr<IHttpClient> CreateHttpClient() {
#ifdef __EMSCRIPTEN__
return std::make_unique<EmscriptenHttpClient>();
#else
return std::make_unique<HttpLibClient>();
#endif
}
std::unique_ptr<IWebSocket> CreateWebSocket() {
#ifdef __EMSCRIPTEN__
return std::make_unique<EmscriptenWebSocket>();
#else
return std::make_unique<HttpLibWebSocket>();
#endif
}
```
### Phase 5: Service Modifications
Update AI services to use the abstraction:
```cpp
// src/cli/service/ai/gemini_ai_service.cc
class GeminiAIService {
std::unique_ptr<IHttpClient> http_client_;
GeminiAIService(const GeminiConfig& config)
: config_(config),
http_client_(CreateHttpClient()) {
// Initialize
}
absl::Status CheckAvailability() {
std::string url = "https://generativelanguage.googleapis.com/v1beta/models/"
+ config_.model;
Headers headers = {{"x-goog-api-key", config_.api_key}};
auto response_or = http_client_->Get(url, headers);
if (!response_or.ok()) {
return response_or.status();
}
auto& response = response_or.value();
if (response.status != 200) {
return absl::UnavailableError("API not available");
}
return absl::OkStatus();
}
};
```
## CMake Configuration
Update CMake to handle WASM builds:
```cmake
# src/app/net/net_library.cmake
if(EMSCRIPTEN)
set(YAZE_NET_SRC
app/net/wasm/emscripten_http_client.cc
app/net/wasm/emscripten_websocket.cc
app/net/network_factory.cc
)
# Add Emscripten fetch and WebSocket flags
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s FETCH=1")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -s WEBSOCKET=1")
else()
set(YAZE_NET_SRC
app/net/native/httplib_client.cc
app/net/native/httplib_websocket.cc
app/net/network_factory.cc
)
# Link native dependencies
target_link_libraries(yaze_net PUBLIC httplib OpenSSL::SSL)
endif()
```
## CORS Considerations
For browser builds, the following CORS requirements apply:
1. **Gemini API**: Google's API servers must include appropriate CORS headers
2. **Ollama**: Local Ollama server needs `--cors` flag or proxy setup
3. **WebSocket Server**: Must handle WebSocket upgrade correctly
### Proxy Server Option
For services without CORS support, implement a proxy:
```javascript
// proxy-server.js (Node.js)
const express = require('express');
const { createProxyMiddleware } = require('http-proxy-middleware');
const app = express();
// Proxy for Ollama
app.use('/ollama', createProxyMiddleware({
target: 'http://localhost:11434',
changeOrigin: true,
pathRewrite: { '^/ollama': '' }
}));
// Proxy for Gemini
app.use('/gemini', createProxyMiddleware({
target: 'https://generativelanguage.googleapis.com',
changeOrigin: true,
pathRewrite: { '^/gemini': '' }
}));
app.listen(3000);
```
## Testing Strategy
1. **Unit Tests**: Mock network interfaces for both native and WASM
2. **Integration Tests**: Test actual API calls in native builds
3. **Browser Tests**: Manual testing of WASM build in browser
4. **E2E Tests**: Selenium/Playwright for automated browser testing
## Implementation Timeline
### Week 1: Foundation
- Create abstract interfaces (IHttpClient, IWebSocket)
- Implement factory pattern
- Update CMake for conditional compilation
### Week 2: Native Refactoring
- Refactor existing code to use interfaces
- Create native implementations
- Ensure no regression in current functionality
### Week 3: WASM Implementation
- Implement EmscriptenHttpClient
- Implement EmscriptenWebSocket
- Test basic functionality
### Week 4: Service Integration
- Update GeminiAIService
- Update OllamaAIService
- Update WebSocketClient
### Week 5: Testing & Refinement
- Comprehensive testing
- CORS handling
- Performance optimization
## Risk Mitigation
### Risk 1: CORS Blocking
**Mitigation**: Implement proxy server as fallback, document CORS requirements
### Risk 2: API Key Security
**Mitigation**:
- Never embed API keys in WASM binary
- Require user to input API key via UI
- Store in browser's localStorage with encryption
### Risk 3: Performance Issues
**Mitigation**:
- Use Web Workers for background processing
- Implement request caching
- Add loading indicators for long operations
### Risk 4: Browser Compatibility
**Mitigation**:
- Test on Chrome, Firefox, Safari, Edge
- Use feature detection
- Provide fallbacks for unsupported features
## Security Considerations
1. **API Keys**: Must be user-provided, never hardcoded
2. **HTTPS Only**: All API calls must use HTTPS in production
3. **Input Validation**: Sanitize all user inputs before API calls
4. **Rate Limiting**: Implement client-side rate limiting
5. **Content Security Policy**: Configure CSP headers properly
## Conclusion
The transition to WASM-compatible networking is achievable through careful abstraction and platform-specific implementations. The key is maintaining a clean separation between platform-agnostic interfaces and platform-specific implementations. This approach allows the codebase to support both native and browser environments without sacrificing functionality or performance.
The proposed architecture provides:
- Clean abstraction layers
- Minimal changes to existing service code
- Easy testing and mocking
- Future extensibility for other platforms
Next steps:
1. Review and approve this plan
2. Create feature branch for implementation
3. Begin with abstract interface definitions
4. Implement incrementally with continuous testing

View File

@@ -0,0 +1,554 @@
# WASM Web App Enhancements Plan
## Executive Summary
This document outlines the comprehensive plan to make yaze's WASM web build fully featured, robust, and user-friendly. The goal is to achieve feature parity with the native desktop application where technically feasible, while leveraging browser-specific capabilities where appropriate.
## Current Status
### Completed
- [x] Basic WASM build configuration
- [x] pthread support for Emscripten
- [x] Network abstraction layer (IHttpClient, IWebSocket)
- [x] Emscripten HTTP client using `emscripten_fetch()`
- [x] Emscripten WebSocket using browser API
- [x] **Phase 1**: File System Layer (WasmStorage, WasmFileDialog)
- [x] **Phase 2**: Error Handling Infrastructure (WasmErrorHandler)
- [x] **Phase 3**: Progressive Loading UI (WasmLoadingManager)
- [x] **Phase 4**: Offline Support (Service Workers, PWA manifest)
- [x] **Phase 5**: AI Service Integration (BrowserAIService, WasmSecureStorage)
- [x] **Phase 6**: Local Storage Persistence (WasmSettings, AutoSaveManager)
- [x] **Phase 7**: Web Workers for heavy processing (WasmWorkerPool)
- [x] **Phase 8**: Emulator Audio (WebAudio, WasmAudioBackend)
### In Progress
- [ ] WASM CI build verification
- [ ] Integration of loading manager with gfx::Arena
- [ ] Integration testing across all phases
---
## Phase 1: File System Layer
### Overview
WASM builds cannot access the local filesystem directly. We need a virtualized file system layer that uses browser storage APIs.
### Implementation
#### 1.1 IndexedDB Storage Backend
```cpp
// src/app/platform/wasm/wasm_storage.h
class WasmStorage {
public:
// Store ROM data
static absl::Status SaveRom(const std::string& name, const std::vector<uint8_t>& data);
static absl::StatusOr<std::vector<uint8_t>> LoadRom(const std::string& name);
static absl::Status DeleteRom(const std::string& name);
static std::vector<std::string> ListRoms();
// Store project files (JSON, palettes, patches)
static absl::Status SaveProject(const std::string& name, const std::string& json);
static absl::StatusOr<std::string> LoadProject(const std::string& name);
// Store user preferences
static absl::Status SavePreferences(const nlohmann::json& prefs);
static absl::StatusOr<nlohmann::json> LoadPreferences();
};
```
#### 1.2 File Upload Handler
```cpp
// JavaScript interop for file input
EM_JS(void, openFileDialog, (const char* accept, int callback_id), {
const input = document.createElement('input');
input.type = 'file';
input.accept = UTF8ToString(accept);
input.onchange = (e) => {
const file = e.target.files[0];
const reader = new FileReader();
reader.onload = () => {
const data = new Uint8Array(reader.result);
Module._handleFileLoaded(callback_id, data);
};
reader.readAsArrayBuffer(file);
};
input.click();
});
```
#### 1.3 File Download Handler
```cpp
// Download ROM or project file
EM_JS(void, downloadFile, (const char* filename, const uint8_t* data, size_t size), {
const blob = new Blob([HEAPU8.subarray(data, data + size)], {type: 'application/octet-stream'});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = UTF8ToString(filename);
a.click();
URL.revokeObjectURL(url);
});
```
### Files to Create
- `src/app/platform/wasm/wasm_storage.h`
- `src/app/platform/wasm/wasm_storage.cc`
- `src/app/platform/wasm/wasm_file_dialog.h`
- `src/app/platform/wasm/wasm_file_dialog.cc`
---
## Phase 2: Error Handling Infrastructure
### Overview
Browser-based applications need specialized error handling that integrates with the web UI and provides user-friendly feedback.
### Implementation
#### 2.1 Browser Error Handler
```cpp
// src/app/platform/wasm/wasm_error_handler.h
class WasmErrorHandler {
public:
// Display error in browser UI
static void ShowError(const std::string& title, const std::string& message);
static void ShowWarning(const std::string& title, const std::string& message);
static void ShowInfo(const std::string& title, const std::string& message);
// Toast notifications (non-blocking)
static void Toast(const std::string& message, ToastType type, int duration_ms = 3000);
// Progress indicators
static void ShowProgress(const std::string& task, float progress);
static void HideProgress();
// Confirmation dialogs
static void Confirm(const std::string& message, std::function<void(bool)> callback);
};
```
#### 2.2 JavaScript Integration
```javascript
// src/web/error_handler.js
window.showYazeError = function(title, message) {
// Create styled error modal
const modal = document.createElement('div');
modal.className = 'yaze-error-modal';
modal.innerHTML = `
<div class="yaze-error-content">
<h2>${escapeHtml(title)}</h2>
<p>${escapeHtml(message)}</p>
<button onclick="this.parentElement.parentElement.remove()">OK</button>
</div>
`;
document.body.appendChild(modal);
};
window.showYazeToast = function(message, type, duration) {
const toast = document.createElement('div');
toast.className = `yaze-toast yaze-toast-${type}`;
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => toast.remove(), duration);
};
```
### Files to Create
- `src/app/platform/wasm/wasm_error_handler.h`
- `src/app/platform/wasm/wasm_error_handler.cc`
- `src/web/error_handler.js`
- `src/web/error_handler.css`
---
## Phase 3: Progressive Loading UI
### Overview
ROM loading and graphics processing can take significant time. Users need visual feedback and the ability to cancel long operations.
### Implementation
#### 3.1 Loading Manager
```cpp
// src/app/platform/wasm/wasm_loading_manager.h
class WasmLoadingManager {
public:
// Start a loading operation with progress tracking
static LoadingHandle BeginLoading(const std::string& task_name);
// Update progress (0.0 to 1.0)
static void UpdateProgress(LoadingHandle handle, float progress);
static void UpdateMessage(LoadingHandle handle, const std::string& message);
// Check if user requested cancel
static bool IsCancelled(LoadingHandle handle);
// Complete the loading operation
static void EndLoading(LoadingHandle handle);
};
```
#### 3.2 Integration with gfx::Arena
```cpp
// Modified graphics loading to report progress
void Arena::LoadGraphicsWithProgress(Rom* rom) {
auto handle = WasmLoadingManager::BeginLoading("Loading Graphics");
for (int i = 0; i < kNumGraphicsSheets; i++) {
if (WasmLoadingManager::IsCancelled(handle)) {
WasmLoadingManager::EndLoading(handle);
return; // User cancelled
}
LoadGraphicsSheet(rom, i);
WasmLoadingManager::UpdateProgress(handle, static_cast<float>(i) / kNumGraphicsSheets);
WasmLoadingManager::UpdateMessage(handle, absl::StrFormat("Sheet %d/%d", i, kNumGraphicsSheets));
// Yield to browser event loop periodically
emscripten_sleep(0);
}
WasmLoadingManager::EndLoading(handle);
}
```
### Files to Create
- `src/app/platform/wasm/wasm_loading_manager.h`
- `src/app/platform/wasm/wasm_loading_manager.cc`
- `src/web/loading_indicator.js`
- `src/web/loading_indicator.css`
---
## Phase 4: Offline Support (Service Workers)
### Overview
Cache the WASM binary and assets for offline use, enabling users to work without an internet connection.
### Implementation
#### 4.1 Service Worker
```javascript
// src/web/service-worker.js
const CACHE_NAME = 'yaze-cache-v1';
const ASSETS = [
'/yaze.wasm',
'/yaze.js',
'/yaze.data',
'/index.html',
'/style.css',
'/fonts/',
];
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHE_NAME).then((cache) => cache.addAll(ASSETS))
);
});
self.addEventListener('fetch', (event) => {
event.respondWith(
caches.match(event.request).then((response) => {
return response || fetch(event.request);
})
);
});
```
#### 4.2 Registration
```javascript
// src/web/index.html
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/service-worker.js')
.then((registration) => {
console.log('Service Worker registered:', registration.scope);
});
}
```
### Files to Create
- `src/web/service-worker.js`
- `src/web/manifest.json` (PWA manifest)
---
## Phase 5: AI Service Integration
### Overview
Integrate the network abstraction layer with AI services (Gemini, Ollama) for browser-based AI assistance.
### Implementation
#### 5.1 Browser AI Client
```cpp
// src/cli/service/ai/browser_ai_service.h
#ifdef __EMSCRIPTEN__
class BrowserAIService : public IAIService {
public:
explicit BrowserAIService(const AIConfig& config);
absl::StatusOr<std::string> GenerateResponse(const std::string& prompt) override;
absl::StatusOr<std::string> AnalyzeImage(const gfx::Bitmap& image,
const std::string& prompt) override;
private:
std::unique_ptr<IHttpClient> http_client_;
std::string api_key_; // User-provided, never stored in binary
std::string model_;
};
#endif
```
#### 5.2 API Key Management
```cpp
// Secure API key storage in browser
EM_JS(void, storeApiKey, (const char* service, const char* key), {
// Use sessionStorage for temporary storage (cleared on tab close)
// or encrypted localStorage for persistent storage
sessionStorage.setItem('yaze_' + UTF8ToString(service) + '_key', UTF8ToString(key));
});
EM_JS(char*, retrieveApiKey, (const char* service), {
const key = sessionStorage.getItem('yaze_' + UTF8ToString(service) + '_key');
if (!key) return null;
const len = lengthBytesUTF8(key) + 1;
const ptr = _malloc(len);
stringToUTF8(key, ptr, len);
return ptr;
});
```
### CORS Considerations
- Gemini API: Should work with browser fetch (Google APIs support CORS)
- Ollama: Requires `--cors` flag or proxy server for local instances
### Files to Create
- `src/cli/service/ai/browser_ai_service.h`
- `src/cli/service/ai/browser_ai_service.cc`
- `src/app/platform/wasm/wasm_browser_storage.h`
- `src/app/platform/wasm/wasm_browser_storage.cc`
---
## Phase 6: Local Storage Persistence
### Overview
Persist user settings, recent files, undo history, and workspace layouts in browser storage.
### Implementation
#### 6.1 Settings Persistence
```cpp
// src/app/platform/wasm/wasm_settings.h
class WasmSettings {
public:
// User preferences
static void SaveTheme(const std::string& theme);
static std::string LoadTheme();
// Recent files (stored as IndexedDB references)
static void AddRecentFile(const std::string& name);
static std::vector<std::string> GetRecentFiles();
// Workspace layouts
static void SaveWorkspace(const std::string& name, const std::string& layout_json);
static std::string LoadWorkspace(const std::string& name);
// Undo history (for crash recovery)
static void SaveUndoHistory(const std::string& editor, const std::vector<uint8_t>& history);
static std::vector<uint8_t> LoadUndoHistory(const std::string& editor);
};
```
#### 6.2 Auto-Save & Recovery
```cpp
// Periodic auto-save
class AutoSaveManager {
public:
void Start(int interval_seconds = 60);
void Stop();
// Called on page unload
void EmergencySave();
// Called on startup
bool HasRecoveryData();
void RecoverLastSession();
void ClearRecoveryData();
};
```
### Files to Create
- `src/app/platform/wasm/wasm_settings.h`
- `src/app/platform/wasm/wasm_settings.cc`
- `src/app/platform/wasm/wasm_autosave.h`
- `src/app/platform/wasm/wasm_autosave.cc`
---
## Phase 7: Web Workers for Heavy Processing
### Overview
Offload CPU-intensive operations to Web Workers to prevent UI freezing.
### Implementation
#### 7.1 Background Processing Worker
```cpp
// Operations to run in Web Worker:
// - ROM decompression (LC-LZ2)
// - Graphics sheet decoding
// - Palette calculations
// - Asar assembly compilation
class WasmWorkerPool {
public:
using TaskCallback = std::function<void(const std::vector<uint8_t>&)>;
// Submit work to background thread
void SubmitTask(const std::string& type,
const std::vector<uint8_t>& input,
TaskCallback callback);
// Wait for all tasks
void WaitAll();
};
```
### Files to Create
- `src/app/platform/wasm/wasm_worker_pool.h`
- `src/app/platform/wasm/wasm_worker_pool.cc`
- `src/web/worker.js`
---
## Phase 8: Emulator Integration
### Overview
The SNES emulator can run in WASM with WebAudio for sound output.
### Implementation
#### 8.1 WebAudio Backend
```cpp
// src/app/emu/platform/wasm/wasm_audio.h
class WasmAudioBackend : public IAudioBackend {
public:
void Initialize(int sample_rate, int buffer_size) override;
void QueueSamples(const int16_t* samples, size_t count) override;
void Shutdown() override;
};
```
#### 8.2 Canvas Rendering
```cpp
// Use EM_ASM for direct canvas manipulation if needed
// Or use existing ImGui/SDL2 rendering (already WASM compatible)
```
### Files to Create
- `src/app/emu/platform/wasm/wasm_audio.h`
- `src/app/emu/platform/wasm/wasm_audio.cc`
---
## Feature Availability Matrix
| Feature | Native | WASM | Notes |
|---------|--------|------|-------|
| ROM Loading | File dialog | File input + IndexedDB | Full support |
| ROM Saving | Direct write | Blob download | Full support |
| Overworld Editor | Full | Full | No limitations |
| Dungeon Editor | Full | Full | No limitations |
| Graphics Editor | Full | Full | No limitations |
| Palette Editor | Full | Full | No limitations |
| Emulator | Full | Full | WebAudio for sound |
| AI (Gemini) | HTTP | Browser Fetch | Requires API key |
| AI (Ollama) | HTTP | Requires proxy | CORS limitation |
| Asar Assembly | Full | In-memory | No file I/O |
| Collaboration | gRPC | WebSocket | Different protocol |
| Crash Recovery | File-based | IndexedDB | Equivalent |
| Offline Mode | N/A | Service Worker | WASM-only feature |
---
## CMake Configuration
### Recommended Preset Updates
```json
{
"name": "wasm-release",
"cacheVariables": {
"YAZE_BUILD_WASM_PLATFORM": "ON",
"YAZE_WASM_ENABLE_WORKERS": "ON",
"YAZE_WASM_ENABLE_OFFLINE": "ON",
"YAZE_WASM_ENABLE_AI": "ON"
}
}
```
### Source Organization
```
src/app/platform/
wasm/
wasm_storage.h/.cc
wasm_file_dialog.h/.cc
wasm_error_handler.h/.cc
wasm_loading_manager.h/.cc
wasm_settings.h/.cc
wasm_autosave.h/.cc
wasm_worker_pool.h/.cc
wasm_browser_storage.h/.cc
src/web/
index.html
shell.html
style.css
error_handler.js
loading_indicator.js
service-worker.js
worker.js
manifest.json
```
---
## Implementation Priority
### High Priority (Required for MVP)
1. File System Layer (Phase 1)
2. Error Handling (Phase 2)
3. Progressive Loading (Phase 3)
### Medium Priority (Enhanced Experience)
4. Local Storage Persistence (Phase 6)
5. Offline Support (Phase 4)
### Lower Priority (Advanced Features)
6. AI Integration (Phase 5)
7. Web Workers (Phase 7)
8. Emulator Audio (Phase 8)
---
## Success Criteria
- [ ] User can load ROM from local file system
- [ ] User can save modified ROM to downloads
- [ ] Loading progress is visible with cancel option
- [ ] Errors display user-friendly messages
- [ ] Settings persist across sessions
- [ ] App works offline after first load
- [ ] AI assistance available via Gemini API
- [ ] No UI freezes during heavy operations
- [ ] Emulator runs with sound
---
## References
- [Emscripten Documentation](https://emscripten.org/docs/)
- [IndexedDB API](https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API)
- [Service Workers](https://developer.mozilla.org/en-US/docs/Web/API/Service_Worker_API)
- [Web Workers](https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API)
- [WebAudio API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Audio_API)

View File

@@ -0,0 +1,224 @@
# WASM Web Features Roadmap
This document captures planned features for the browser-based yaze editor.
## Foundation (Completed)
The following infrastructure is in place:
- **Phase 1**: File System Layer (WasmStorage, WasmFileDialog) - IndexedDB storage
- **Phase 2**: Error Handling (WasmErrorHandler) - Browser UI integration
- **Phase 3**: Progressive Loading (WasmLoadingManager) - Cancellable loading with progress
- **Phase 4**: Offline Support (service-worker.js, manifest.json) - PWA capabilities
- **Phase 5**: AI Integration (BrowserAIService, WasmSecureStorage) - WebLLM ready
- **Phase 6**: Local Storage (WasmSettings, WasmAutosave) - Preferences persistence
- **Phase 7**: Web Workers (WasmWorkerPool) - Background task processing
- **Phase 8**: WebAudio (WasmAudio) - SPC700 audio playback
---
## Phase 9: Enhanced File Handling
### 9.1 Drag & Drop ROM Loading
- **Status**: Planned
- **Priority**: High
- **Description**: Enhanced drag-and-drop interface for ROM files
- **Features**:
- Visual drop zone with hover effects
- File type validation before processing
- Preview panel showing ROM metadata
- Multiple file support (load ROM + patch together)
- **Files**: `src/app/platform/wasm/wasm_drop_handler.{h,cc}`, `src/web/drop_zone.{js,css}`
### 9.2 Export Options
- **Status**: Planned
- **Priority**: High
- **Description**: Export modifications as patches instead of full ROMs
- **Features**:
- BPS patch generation (standard format)
- IPS patch generation (legacy support)
- UPS patch generation (alternative)
- Patch preview showing changed bytes
- Direct download or save to IndexedDB
- **Files**: `src/app/platform/wasm/wasm_patch_export.{h,cc}`
---
## Phase 10: Collaboration
### 10.1 Real-time Collaboration
- **Status**: Planned
- **Priority**: High
- **Description**: Multi-user editing via WebSocket
- **Features**:
- Session creation/joining with room codes
- User presence indicators (cursors, selections)
- Change synchronization via operational transforms
- Chat/comments sidebar
- Permission levels (owner, editor, viewer)
- **Files**: `src/app/platform/wasm/wasm_collaboration.{h,cc}`, `src/web/collaboration_ui.{js,css}`
- **Dependencies**: EmscriptenWebSocket (completed)
### 10.2 ShareLink Generation
- **Status**: Future
- **Priority**: Medium
- **Description**: Create shareable URLs with embedded patches
- **Features**:
- Base64-encoded diff in URL hash
- Short URL generation via service
- QR code generation for mobile sharing
### 10.3 Comment Annotations
- **Status**: Future
- **Priority**: Low
- **Description**: Add notes to map locations/rooms
- **Features**:
- Pin comments to coordinates
- Threaded discussions
- Export annotations as JSON
---
## Phase 11: Browser-Specific Enhancements
### 11.1 Keyboard Shortcut Overlay
- **Status**: Future
- **Priority**: Medium
- **Description**: Help panel showing all keyboard shortcuts
- **Features**:
- Toggle with `?` key
- Context-aware (shows relevant shortcuts for current editor)
- Searchable
### 11.2 Touch Support
- **Status**: Future
- **Priority**: Medium
- **Description**: Touch gestures for tablet/mobile browsers
- **Features**:
- Pinch to zoom
- Two-finger pan
- Long press for context menu
- Touch-friendly toolbar
### 11.3 Fullscreen Mode
- **Status**: Future
- **Priority**: Low
- **Description**: Dedicated fullscreen API integration
- **Features**:
- F11 toggle
- Auto-hide toolbar in fullscreen
- Escape to exit
### 11.4 Browser Notifications
- **Status**: Future
- **Priority**: Low
- **Description**: Alert when long operations complete
- **Features**:
- Permission request flow
- Build/export completion notifications
- Background tab awareness
---
## Phase 12: AI Integration Enhancements
### 12.1 Browser-Local LLM
- **Status**: Future
- **Priority**: Medium
- **Description**: In-browser AI using WebLLM
- **Features**:
- Model download/caching
- Chat interface for ROM hacking questions
- Code generation for ASM patches
- **Dependencies**: BrowserAIService (completed)
### 12.2 ROM Analysis Reports
- **Status**: Future
- **Priority**: Low
- **Description**: Generate sharable HTML reports
- **Features**:
- Modification summary
- Changed areas visualization
- Exportable as standalone HTML
---
## Phase 13: Performance Optimizations
### 13.1 WebGPU Rendering
- **Status**: Future
- **Priority**: Medium
- **Description**: Modern GPU acceleration
- **Features**:
- Feature detection with fallback to WebGL
- Hardware-accelerated tile rendering
- Shader-based effects
### 13.2 Lazy Tile Loading
- **Status**: Future
- **Priority**: Medium
- **Description**: Load only visible map sections
- **Features**:
- Virtual scrolling for large maps
- Tile cache management
- Preload adjacent areas
---
## Phase 14: Cloud Features
### 14.1 Cloud ROM Storage
- **Status**: Future
- **Priority**: Low
- **Description**: Optional cloud sync for projects
- **Features**:
- User accounts
- Project backup/restore
- Cross-device sync
### 14.2 Screenshot Gallery
- **Status**: Future
- **Priority**: Low
- **Description**: Save emulator screenshots
- **Features**:
- Auto-capture on emulator test
- Gallery view in IndexedDB
- Share to social media
---
## Implementation Notes
### Technology Stack
- **Storage**: IndexedDB via WasmStorage
- **Networking**: EmscriptenWebSocket for real-time features
- **Background Processing**: WasmWorkerPool (pthread-based)
- **Audio**: WebAudio API via WasmAudio
- **UI**: ImGui with HTML overlays for browser-specific elements
### File Organization
```
src/app/platform/wasm/
├── wasm_storage.{h,cc} # Phase 1 ✓
├── wasm_file_dialog.{h,cc} # Phase 1 ✓
├── wasm_error_handler.{h,cc} # Phase 2 ✓
├── wasm_loading_manager.{h,cc} # Phase 3 ✓
├── wasm_settings.{h,cc} # Phase 6 ✓
├── wasm_autosave.{h,cc} # Phase 6 ✓
├── wasm_browser_storage.{h,cc} # Phase 5 ✓
├── wasm_worker_pool.{h,cc} # Phase 7 ✓
├── wasm_drop_handler.{h,cc} # Phase 9 (planned)
├── wasm_patch_export.{h,cc} # Phase 9 (planned)
└── wasm_collaboration.{h,cc} # Phase 10 (planned)
src/app/emu/platform/wasm/
└── wasm_audio.{h,cc} # Phase 8 ✓
src/web/
├── error_handler.{js,css} # Phase 2 ✓
├── loading_indicator.{js,css} # Phase 3 ✓
├── service-worker.js # Phase 4 ✓
├── manifest.json # Phase 4 ✓
├── drop_zone.{js,css} # Phase 9 (planned)
└── collaboration_ui.{js,css} # Phase 10 (planned)
```

View File

@@ -0,0 +1,344 @@
# WASM Widget Tracking Implementation
**Date**: 2025-11-24
**Author**: Claude (AI Agent)
**Status**: Implemented
**Related Files**:
- `/Users/scawful/Code/yaze/src/app/platform/wasm/wasm_control_api.cc`
- `/Users/scawful/Code/yaze/src/app/gui/automation/widget_id_registry.h`
- `/Users/scawful/Code/yaze/src/app/gui/automation/widget_measurement.h`
- `/Users/scawful/Code/yaze/src/app/controller.cc`
## Overview
This document describes the implementation of actual ImGui widget bounds tracking for GUI automation in the YAZE WASM build. The system replaces placeholder hardcoded bounds with real-time widget position data from the `WidgetIdRegistry`.
## Problem Statement
The original `WasmControlApi::GetUIElementTree()` and `GetUIElementBounds()` implementations returned hardcoded placeholder bounds:
```cpp
// OLD: Hardcoded placeholder
elem["bounds"] = {{"x", 0}, {"y", 0}, {"width", 100}, {"height", 30}};
```
This prevented accurate GUI automation, as agents and test frameworks couldn't reliably click on or query widget positions.
## Solution Architecture
### 1. Existing Infrastructure (Already in Place)
YAZE already had a comprehensive widget tracking system:
- **`WidgetIdRegistry`** (`src/app/gui/automation/widget_id_registry.h`): Centralized registry that tracks all ImGui widgets with their bounds, visibility, and state
- **`WidgetMeasurement`** (`src/app/gui/automation/widget_measurement.h`): Measures widget dimensions using `ImGui::GetItemRectMin()` and `ImGui::GetItemRectMax()`
- **Frame lifecycle hooks**: `BeginFrame()` and `EndFrame()` calls already integrated in `Controller::OnLoad()` (lines 96-98)
### 2. Integration with WASM Control API
The implementation connects the WASM API to the existing widget registry:
#### Updated `GetUIElementTree()`
**File**: `/Users/scawful/Code/yaze/src/app/platform/wasm/wasm_control_api.cc` (lines 1386-1433)
```cpp
std::string WasmControlApi::GetUIElementTree() {
nlohmann::json result;
if (!IsReady()) {
result["error"] = "Control API not initialized";
result["elements"] = nlohmann::json::array();
return result.dump();
}
// Query the WidgetIdRegistry for all registered widgets
auto& registry = gui::WidgetIdRegistry::Instance();
const auto& all_widgets = registry.GetAllWidgets();
nlohmann::json elements = nlohmann::json::array();
// Convert WidgetInfo to JSON elements
for (const auto& [path, info] : all_widgets) {
nlohmann::json elem;
elem["id"] = info.full_path;
elem["type"] = info.type;
elem["label"] = info.label;
elem["enabled"] = info.enabled;
elem["visible"] = info.visible;
elem["window"] = info.window_name;
// Add bounds if available
if (info.bounds.valid) {
elem["bounds"] = {
{"x", info.bounds.min_x},
{"y", info.bounds.min_y},
{"width", info.bounds.max_x - info.bounds.min_x},
{"height", info.bounds.max_y - info.bounds.min_y}
};
} else {
elem["bounds"] = {
{"x", 0}, {"y", 0}, {"width", 0}, {"height", 0}
};
}
// Add metadata
if (!info.description.empty()) {
elem["description"] = info.description;
}
elem["imgui_id"] = static_cast<uint32_t>(info.imgui_id);
elem["last_seen_frame"] = info.last_seen_frame;
elements.push_back(elem);
}
result["elements"] = elements;
result["count"] = elements.size();
result["source"] = "WidgetIdRegistry";
return result.dump();
}
```
**Changes**:
- Removed hardcoded editor-specific element generation
- Queries `WidgetIdRegistry::GetAllWidgets()` for real widget data
- Returns actual bounds from `info.bounds` if valid
- Includes metadata: `imgui_id`, `last_seen_frame`, `description`
- Adds `source: "WidgetIdRegistry"` to JSON for debugging
#### Updated `GetUIElementBounds()`
**File**: `/Users/scawful/Code/yaze/src/app/platform/wasm/wasm_control_api.cc` (lines ~1435+)
```cpp
std::string WasmControlApi::GetUIElementBounds(const std::string& element_id) {
nlohmann::json result;
if (!IsReady()) {
result["error"] = "Control API not initialized";
return result.dump();
}
// Query the WidgetIdRegistry for the specific widget
auto& registry = gui::WidgetIdRegistry::Instance();
const auto* widget_info = registry.GetWidgetInfo(element_id);
result["id"] = element_id;
if (widget_info == nullptr) {
result["found"] = false;
result["error"] = "Element not found: " + element_id;
return result.dump();
}
result["found"] = true;
result["visible"] = widget_info->visible;
result["enabled"] = widget_info->enabled;
result["type"] = widget_info->type;
result["label"] = widget_info->label;
result["window"] = widget_info->window_name;
// Add bounds if available
if (widget_info->bounds.valid) {
result["x"] = widget_info->bounds.min_x;
result["y"] = widget_info->bounds.min_y;
result["width"] = widget_info->bounds.max_x - widget_info->bounds.min_x;
result["height"] = widget_info->bounds.max_y - widget_info->bounds.min_y;
result["bounds_valid"] = true;
} else {
result["x"] = 0;
result["y"] = 0;
result["width"] = 0;
result["height"] = 0;
result["bounds_valid"] = false;
}
// Add metadata
result["imgui_id"] = static_cast<uint32_t>(widget_info->imgui_id);
result["last_seen_frame"] = widget_info->last_seen_frame;
if (!widget_info->description.empty()) {
result["description"] = widget_info->description;
}
return result.dump();
}
```
**Changes**:
- Removed hardcoded element ID pattern matching
- Queries `WidgetIdRegistry::GetWidgetInfo(element_id)` for specific widget
- Returns `found: false` if widget doesn't exist
- Returns actual bounds with `bounds_valid` flag
- Includes full widget metadata
### 3. Frame Lifecycle Integration
**File**: `/Users/scawful/Code/yaze/src/app/controller.cc` (lines 96-98)
The widget registry is already integrated into the main render loop:
```cpp
absl::Status Controller::OnLoad() {
// ... ImGui::NewFrame() setup ...
gui::WidgetIdRegistry::Instance().BeginFrame();
absl::Status update_status = editor_manager_.Update();
gui::WidgetIdRegistry::Instance().EndFrame();
RETURN_IF_ERROR(update_status);
return absl::OkStatus();
}
```
**Frame Lifecycle**:
1. `BeginFrame()`: Resets `seen_in_current_frame` flag for all widgets
2. Widget rendering: Editors register widgets during `editor_manager_.Update()`
3. `EndFrame()`: Marks unseen widgets as invisible, prunes stale entries
### 4. Widget Registration (Future Work)
**Current State**: Widget registration infrastructure exists but **editors are not yet registering widgets**.
**Registration Pattern** (to be implemented in editors):
```cpp
// Example: Dungeon Editor registering a card
{
gui::WidgetIdScope scope("DungeonEditor");
if (ImGui::Begin("Room Selector##dungeon")) {
// Widget now has full path: "DungeonEditor/Room Selector"
if (ImGui::Button("Load Room")) {
// After rendering button, register it
gui::WidgetIdRegistry::Instance().RegisterWidget(
scope.GetWidgetPath("button", "Load Room"),
"button",
ImGui::GetItemID(),
"Loads the selected room into the editor"
);
}
}
ImGui::End();
}
```
**Macros Available**:
- `YAZE_WIDGET_SCOPE(name)`: RAII scope for hierarchical widget paths
- `YAZE_REGISTER_WIDGET(type, name)`: Register widget after rendering
- `YAZE_REGISTER_CURRENT_WIDGET(type)`: Auto-extract widget name from ImGui
## API Usage
### JavaScript API
**Get All UI Elements**:
```javascript
const elements = window.yaze.control.getUIElementTree();
console.log(elements);
// Output:
// {
// "elements": [
// {
// "id": "DungeonEditor/RoomSelector/button:LoadRoom",
// "type": "button",
// "label": "Load Room",
// "visible": true,
// "enabled": true,
// "window": "DungeonEditor",
// "bounds": {"x": 150, "y": 200, "width": 100, "height": 30},
// "imgui_id": 12345,
// "last_seen_frame": 4567
// }
// ],
// "count": 1,
// "source": "WidgetIdRegistry"
// }
```
**Get Specific Widget Bounds**:
```javascript
const bounds = window.yaze.control.getUIElementBounds("DungeonEditor/RoomSelector/button:LoadRoom");
console.log(bounds);
// Output:
// {
// "id": "DungeonEditor/RoomSelector/button:LoadRoom",
// "found": true,
// "visible": true,
// "enabled": true,
// "type": "button",
// "label": "Load Room",
// "window": "DungeonEditor",
// "x": 150,
// "y": 200,
// "width": 100,
// "height": 30,
// "bounds_valid": true,
// "imgui_id": 12345,
// "last_seen_frame": 4567
// }
```
## Performance Considerations
1. **Memory**: `WidgetIdRegistry` stores widget metadata in `std::unordered_map`, which grows with UI complexity. Stale widgets are pruned after 600 frames of inactivity.
2. **CPU Overhead**:
- `BeginFrame()`: O(n) iteration to reset flags (n = number of widgets)
- Widget registration: O(1) hash map lookup/insert
- `EndFrame()`: O(n) iteration for pruning stale entries
3. **Optimization**: Widget measurement can be disabled globally:
```cpp
gui::WidgetMeasurement::Instance().SetEnabled(false);
```
## Testing
**Manual Test (WASM Build)**:
```bash
# Build WASM
./scripts/build-wasm.sh
# Serve locally
cd build-wasm
python3 -m http.server 8080
# Open browser console
window.yaze.control.getUIElementTree();
```
**Expected Behavior**:
- Initially, `elements` array will be empty (no widgets registered yet)
- After editors implement registration, widgets will appear with real bounds
- `bounds_valid: false` for widgets not yet rendered in current frame
## Next Steps
1. **Add widget registration to editors**:
- `DungeonEditorV2`: Register room tabs, cards, buttons
- `OverworldEditor`: Register canvas, tile selectors, property panels
- `GraphicsEditor`: Register graphics sheets, palette pickers
2. **Add registration helpers**:
- Create `AgentUI::RegisterButton()`, `AgentUI::RegisterCard()` wrappers
- Auto-register common widget patterns (cards with visibility flags)
3. **Extend API**:
- `FindWidgetsByPattern(pattern)`: Search widgets by regex
- `ClickWidget(element_id)`: Simulate click via automation API
## References
- Widget ID Registry: `/Users/scawful/Code/yaze/src/app/gui/automation/widget_id_registry.h`
- Widget Measurement: `/Users/scawful/Code/yaze/src/app/gui/automation/widget_measurement.h`
- WASM Control API: `/Users/scawful/Code/yaze/src/app/platform/wasm/wasm_control_api.h`
- Controller Integration: `/Users/scawful/Code/yaze/src/app/controller.cc` (lines 96-98)
## Revision History
| Date | Author | Changes |
|------------|--------|--------------------------------------------|
| 2025-11-24 | Claude | Initial implementation and documentation |