# Message Editor Implementation Roadmap **Status**: Active Development **Last Updated**: 2025-11-21 **Owner**: Frontend/UI Team **Related Docs**: - `docs/internal/architecture/message_system.md` (Gemini's architecture vision) - `docs/internal/plans/message_system_improvement_plan.md` (Gemini's feature proposals) --- ## Executive Summary This roadmap bridges Gemini's architectural vision with practical implementation steps for completing the Message Editor. The current implementation has the **core foundation** in place (message parsing, dictionary system, preview rendering) but lacks several key features proposed in Gemini's plan, particularly around **JSON import/export**, **translation workflows**, and **theme integration**. --- ## Current State Analysis ### What's Working (Completed Features) #### Core Data Layer ✅ - **MessageData**: Full implementation with raw/parsed representations - **DictionaryEntry**: Compression system with dictionary optimization - **TextElement**: Command and special character parsing - **Character Encoding**: Complete CharEncoder table (0x00-0x66) - **ROM Reading**: `ReadAllTextData()` successfully loads all 396 messages - **ROM Writing**: `Save()` handles two-bank text data with overflow detection #### Message Preview System ✅ - **MessagePreview**: Live rendering of messages as they appear in-game - **Font Graphics**: 2BPP font tiles loaded and displayed at 0x70000 - **Character Widths**: Proportional font support via width table at 0x74ADF - **Preview Bitmap**: Real-time message rendering with proper palette support #### Editor UI ✅ - **Card System**: Four dockable cards (Message List, Editor, Font Atlas, Dictionary) - **Message List**: Table view with ID, contents, and address columns - **Text Editor**: Multiline input with live preview updates - **Command Insertion**: Buttons to insert text commands and special characters - **Dictionary Display**: Read-only view of all 97 dictionary entries - **Expanded Messages**: Basic support for loading external message bins #### Testing Coverage ✅ - **Unit Tests**: 20+ tests covering parsing, encoding, dictionary optimization - **Integration Tests**: ROM-dependent tests verify actual game data - **Command Parsing**: Regression tests for argument handling bugs #### CLI Integration ✅ - **Message List**: `z3ed message list --format json --range 0-100` - **Message Read**: `z3ed message read --id 5 --format json` - **Message Search**: `z3ed message search --query "Link"` - **Message Stats**: `z3ed message stats --format json` ### What's Missing (Gaps vs. Gemini's Vision) #### 1. JSON Import/Export ❌ (HIGH PRIORITY) **Status**: Not implemented **Gemini's Vision**: ```json [ { "id": 0, "address": 917504, "text": "[W:00][SPD:00]Welcome to [D:05]...", "context": "Uncle dying in sewers" } ] ``` **Current Gap**: - No `SerializeMessages()` or `DeserializeMessages()` in `MessageData` - No UI for export/import operations - No context field for translator notes - CLI has JSON output but not JSON input **Impact**: Cannot version control text, cannot use external editors, cannot collaborate with translators --- #### 2. Translation Workspace ❌ (MEDIUM PRIORITY) **Status**: Not implemented **Gemini's Vision**: Side-by-side view with reference ROM/JSON and editable translation **Current Gap**: - No reference text display - No side-by-side layout - No translation progress tracking - No language-specific dictionary optimization **Impact**: Manual translation workflows are tedious and error-prone --- #### 3. Search & Replace ⚠️ (PARTIAL) **Status**: Stub implementation exists **Gemini's Vision**: Regex support, batch replace across all messages **Current Implementation**: - `Find()` method exists in `MessageEditor` (lines 574-600) - Basic UI skeleton present (search input, case sensitivity toggle) - **Missing**: Replace functionality, regex support, "Find All", multi-message operations **Impact**: Global text edits require manual per-message changes --- #### 4. Theme Integration ❌ (LOW PRIORITY - UI POLISH) **Status**: Not implemented **Current Issues**: - No hardcoded `ImVec4` colors found (GOOD!) - Not using `AgentUITheme` system for consistency - Missing semantic color names for message editor components **Impact**: Message Editor UI may not match rest of application theme --- #### 5. Expanded ROM Support ⚠️ (PARTIAL) **Status**: Basic implementation exists **Gemini's Vision**: Repointing text blocks to expanded ROM space (Banks 10+), automatic bank switching **Current Implementation**: - Can load expanded message bins (lines 322-334) - Can save expanded messages (lines 497-508) - **Missing**: Repointing logic, bank management, automatic overflow handling **Impact**: Cannot support large translation projects that exceed vanilla space --- #### 6. Scripting Integration ❌ (FUTURE) **Status**: Not planned **Gemini's Vision**: Lua/Python API for procedural text generation **Current Gap**: No scripting hooks in message system **Impact**: Low - nice-to-have for advanced users --- ## Architectural Decisions Required ### Decision 1: JSON Schema Design **Question**: What fields should the JSON export include? **Proposal**: ```json { "version": "1.0", "rom_name": "zelda3.sfc", "messages": [ { "id": 0, "address": 917504, "address_hex": "0xE0000", "text": "[W:00][SPD:00]Welcome...", "context": "Optional translator note", "dictionary_optimized": true, "expanded": false } ], "dictionary": [ { "id": 0, "token": "[D:00]", "contents": "the" } ] } ``` **Trade-offs**: - Verbose but human-readable - Includes metadata for validation - Context field for translator workflow **Status**: ✅ RECOMMENDED --- ### Decision 2: Translation Workspace Layout **Question**: How should reference vs. translation be displayed? **Option A**: Side-by-side split pane ``` ┌────────────────┬────────────────┐ │ Reference │ Translation │ │ (English) │ (Spanish) │ │ [Read-only] │ [Editable] │ │ │ │ │ Message 0: │ Message 0: │ │ "Welcome to │ "Bienvenido a │ │ Hyrule" │ Hyrule" │ └────────────────┴────────────────┘ ``` **Option B**: Top-bottom with context panel ``` ┌────────────────────────────────┐ │ Reference: "Welcome to Hyrule" │ │ Context: Uncle's dying words │ ├────────────────────────────────┤ │ Translation: │ │ [Editable text box] │ └────────────────────────────────┘ ``` **Recommendation**: Option A for large screens, Option B for narrow windows **Status**: ⚠️ NEEDS USER FEEDBACK --- ### Decision 3: Dictionary Auto-Optimization **Question**: Should we auto-generate optimal dictionary for new languages? **Challenges**: - Dictionary optimization is NP-hard (longest common substring problem) - Need to preserve ROM space constraints (97 entries max) - Different languages have different common phrases **Proposal**: 1. Provide "Analyze Translation" button that suggests optimal dictionary 2. Let user accept/reject suggestions 3. Preserve manual dictionary entries **Status**: ⚠️ NEEDS RESEARCH --- ## Implementation Priority Matrix ### Phase 1: Foundation (Sprint 1-2 weeks) **Goal**: JSON import/export with UI integration #### Task 1.1: Implement JSON Serialization **Location**: `src/app/editor/message/message_data.h`, `message_data.cc` **Priority**: P0 (Blocker for translation workflow) **Estimated Effort**: 3 days **Implementation**: ```cpp // In MessageData nlohmann::json SerializeToJson() const; static absl::StatusOr DeserializeFromJson(const nlohmann::json& j); // Free functions absl::Status ExportMessagesToJson( const std::vector& messages, const std::vector& dictionary, const std::string& output_path); absl::StatusOr> ImportMessagesFromJson( const std::string& input_path); ``` **Dependencies**: nlohmann/json (already in project via CPM) **Acceptance Criteria**: - [ ] Export all 396 messages to valid JSON - [ ] Import JSON and verify byte-for-byte ROM equivalence - [ ] Handle malformed JSON with clear error messages - [ ] Preserve dictionary optimization - [ ] Include context field in schema --- #### Task 1.2: Add Export/Import UI **Location**: `src/app/editor/message/message_editor.cc` **Priority**: P0 **Estimated Effort**: 2 days **UI Additions**: ```cpp void MessageEditor::DrawExportImportPanel() { if (ImGui::Button("Export to JSON")) { std::string path = util::FileDialogWrapper::ShowSaveFileDialog("json"); PRINT_IF_ERROR(ExportMessagesToJson(list_of_texts_, message_preview_.all_dictionaries_, path)); } if (ImGui::Button("Import from JSON")) { std::string path = util::FileDialogWrapper::ShowOpenFileDialog(); auto result = ImportMessagesFromJson(path); if (result.ok()) { list_of_texts_ = result.value(); RefreshMessageList(); } } } ``` **Acceptance Criteria**: - [ ] File dialogs open with correct filters - [ ] Progress indicator for large exports - [ ] Confirmation dialog on import (warns about overwriting) - [ ] Error popup on import failure with details --- #### Task 1.3: CLI JSON Import Support **Location**: `src/cli/handlers/game/message.cc` **Priority**: P1 **Estimated Effort**: 1 day **Implementation**: ```bash z3ed message import --json messages.json --rom zelda3.sfc --output zelda3_translated.sfc ``` **Acceptance Criteria**: - [ ] Import JSON and write to ROM - [ ] Validate JSON schema before import - [ ] Verify ROM size constraints - [ ] Dry-run mode (validate without writing) --- ### Phase 2: Translation Workflow (Sprint 2-3 weeks) **Goal**: Side-by-side translation UI #### Task 2.1: Add Translation Mode Card **Location**: `src/app/editor/message/message_editor.h`, `message_editor.cc` **Priority**: P1 **Estimated Effort**: 5 days **New Components**: ```cpp class TranslationWorkspace { public: void Initialize(Rom* reference_rom, Rom* translation_rom); void DrawUI(); void LoadReferenceFromJson(const std::string& path); private: void DrawSideBySideView(); void DrawProgressTracker(); void UpdateTranslationProgress(); std::vector reference_messages_; std::vector translation_messages_; std::map translation_complete_flags_; Rom* reference_rom_ = nullptr; Rom* translation_rom_ = nullptr; }; ``` **UI Mockup**: ``` ┌─────────────────────────────────────────────────┐ │ Translation Progress: 42/396 (10.6%) │ ├──────────────────────┬──────────────────────────┤ │ Reference (EN) │ Translation (ES) │ ├──────────────────────┼──────────────────────────┤ │ Message 0: │ Message 0: │ │ "Welcome to Hyrule" │ [Editable input box] │ │ │ │ │ Dictionary: [D:05] │ Dictionary: [D:05] │ ├──────────────────────┴──────────────────────────┤ │ [Previous] [Next] [Mark Complete] [Skip] │ └─────────────────────────────────────────────────┘ ``` **Acceptance Criteria**: - [ ] Load reference ROM or JSON - [ ] Display messages side-by-side - [ ] Track translation progress (per-message completion) - [ ] Keyboard shortcuts for navigation (Ctrl+N, Ctrl+P) - [ ] Auto-save translated ROM on completion --- #### Task 2.2: Context/Notes System **Location**: `src/app/editor/message/message_data.h` **Priority**: P2 **Estimated Effort**: 2 days **Schema Addition**: ```cpp struct MessageData { // ... existing fields ... std::string context; // Translator notes, scene context std::string screenshot_path; // Optional screenshot reference nlohmann::json SerializeToJson() const { return { {"id", ID}, {"address", Address}, {"text", RawString}, {"context", context}, {"screenshot", screenshot_path} }; } }; ``` **UI Addition**: ```cpp void MessageEditor::DrawContextPanel() { ImGui::InputTextMultiline("Context Notes", ¤t_message_.context); if (!current_message_.screenshot_path.empty()) { ImGui::Image(LoadScreenshot(current_message_.screenshot_path)); } } ``` **Acceptance Criteria**: - [ ] Context field persists in JSON export/import - [ ] Context displayed in translation workspace - [ ] Optional screenshot attachment (stored as relative path) --- ### Phase 3: Search & Replace (Sprint 3-1 week) **Goal**: Complete Find/Replace implementation #### Task 3.1: Implement Replace Functionality **Location**: `src/app/editor/message/message_editor.cc` **Priority**: P2 **Estimated Effort**: 2 days **Implementation**: ```cpp absl::Status MessageEditor::Replace(const std::string& find_text, const std::string& replace_text, bool case_sensitive, bool whole_word, bool all_messages) { int replaced_count = 0; if (all_messages) { for (auto& message : list_of_texts_) { replaced_count += ReplaceInMessage(message, find_text, replace_text, case_sensitive, whole_word); } } else { replaced_count += ReplaceInMessage(current_message_, find_text, replace_text, case_sensitive, whole_word); } return absl::OkStatus(); } ``` **UI Updates**: ```cpp void MessageEditor::DrawFindReplacePanel() { static char find_text[256] = ""; static char replace_text[256] = ""; ImGui::InputText("Find", find_text, IM_ARRAYSIZE(find_text)); ImGui::InputText("Replace", replace_text, IM_ARRAYSIZE(replace_text)); ImGui::Checkbox("Case Sensitive", &case_sensitive_); ImGui::Checkbox("Whole Word", &match_whole_word_); ImGui::Checkbox("All Messages", &replace_all_messages_); if (ImGui::Button("Replace")) { PRINT_IF_ERROR(Replace(find_text, replace_text, case_sensitive_, match_whole_word_, false)); } if (ImGui::Button("Replace All")) { PRINT_IF_ERROR(Replace(find_text, replace_text, case_sensitive_, match_whole_word_, true)); } } ``` **Acceptance Criteria**: - [ ] Replace in current message - [ ] Replace in all messages - [ ] Case-sensitive/insensitive options - [ ] Whole word matching - [ ] Undo support (requires history stack) --- #### Task 3.2: Add Regex Support **Location**: `src/app/editor/message/message_editor.cc` **Priority**: P3 (Nice-to-have) **Estimated Effort**: 2 days **Implementation**: ```cpp absl::Status MessageEditor::ReplaceRegex(const std::string& pattern, const std::string& replacement, bool all_messages) { std::regex regex_pattern; try { regex_pattern = std::regex(pattern); } catch (const std::regex_error& e) { return absl::InvalidArgumentError( absl::StrFormat("Invalid regex: %s", e.what())); } // Perform replacement... } ``` **Acceptance Criteria**: - [ ] Regex validation with error messages - [ ] Capture group support ($1, $2, etc.) - [ ] Preview matches before replacement --- ### Phase 4: UI Polish (Sprint 4-1 week) **Goal**: Theme integration and UX improvements #### Task 4.1: Integrate AgentUITheme **Location**: `src/app/editor/message/message_editor.cc` **Priority**: P3 **Estimated Effort**: 1 day **Implementation**: ```cpp void MessageEditor::DrawMessageList() { const auto& theme = AgentUI::GetTheme(); AgentUI::PushPanelStyle(); ImGui::PushStyleColor(ImGuiCol_Header, theme.panel_bg_darker); ImGui::PushStyleColor(ImGuiCol_HeaderHovered, theme.accent_color); // ... table rendering ... ImGui::PopStyleColor(2); AgentUI::PopPanelStyle(); } ``` **Acceptance Criteria**: - [ ] All panels use `AgentUI::PushPanelStyle()` - [ ] Section headers use `AgentUI::RenderSectionHeader()` - [ ] Buttons use `AgentUI::StyledButton()` where appropriate - [ ] Color scheme matches rest of editor --- #### Task 4.2: Add Keyboard Shortcuts **Location**: `src/app/editor/message/message_editor.cc` **Priority**: P3 **Estimated Effort**: 1 day **Shortcuts**: - `Ctrl+F`: Open Find/Replace - `Ctrl+E`: Export to JSON - `Ctrl+I`: Import from JSON - `Ctrl+S`: Save ROM - `Ctrl+N`: Next message (in translation mode) - `Ctrl+P`: Previous message (in translation mode) **Implementation**: ```cpp void MessageEditor::HandleKeyboardShortcuts() { if (ImGui::IsKeyPressed(ImGuiKey_F) && ImGui::GetIO().KeyCtrl) { show_find_replace_ = true; } // ... other shortcuts ... } ``` **Acceptance Criteria**: - [ ] Shortcuts don't conflict with global shortcuts - [ ] Shortcuts displayed in tooltips - [ ] Configurable shortcuts (future enhancement) --- ## Test Strategy ### Unit Tests **Location**: `test/unit/message_data_test.cc` (new file) ```cpp TEST(MessageDataTest, SerializeToJson_BasicMessage) { MessageData msg; msg.ID = 0; msg.Address = 0xE0000; msg.RawString = "Hello World"; msg.context = "Test message"; auto json = msg.SerializeToJson(); EXPECT_EQ(json["id"], 0); EXPECT_EQ(json["text"], "Hello World"); EXPECT_EQ(json["context"], "Test message"); } TEST(MessageDataTest, DeserializeFromJson_RoundTrip) { MessageData original; original.ID = 5; original.RawString = "[W:00][K]Test"; auto json = original.SerializeToJson(); auto result = MessageData::DeserializeFromJson(json); ASSERT_TRUE(result.ok()); EXPECT_EQ(result.value().ID, 5); EXPECT_EQ(result.value().RawString, "[W:00][K]Test"); } ``` ### Integration Tests **Location**: `test/integration/message_export_test.cc` (new file) ```cpp TEST_F(MessageRomTest, ExportImport_RoundTrip) { // Export all messages to JSON std::string json_path = "/tmp/messages.json"; EXPECT_OK(ExportMessagesToJson(list_of_texts_, dictionary_, json_path)); // Import back auto imported = ImportMessagesFromJson(json_path); ASSERT_TRUE(imported.ok()); // Verify identical EXPECT_EQ(imported.value().size(), list_of_texts_.size()); for (size_t i = 0; i < list_of_texts_.size(); ++i) { EXPECT_EQ(imported.value()[i].RawString, list_of_texts_[i].RawString); } } ``` ### E2E Tests **Location**: `test/e2e/message_editor_workflow_test.cc` (new file) ```cpp TEST_F(MessageEditorE2ETest, TranslationWorkflow) { // Open translation workspace EXPECT_OK(ClickButton("Translation Mode")); // Load reference ROM EXPECT_OK(OpenFileDialog("reference_rom.sfc")); // Navigate to message 0 EXPECT_EQ(GetCurrentMessageID(), 0); // Edit translation EXPECT_OK(SetTextBoxValue("Bienvenido a Hyrule")); // Mark complete EXPECT_OK(ClickButton("Mark Complete")); // Verify progress EXPECT_EQ(GetTranslationProgress(), "1/396"); } ``` --- ## Dependencies & Risks ### External Dependencies 1. **nlohmann/json**: Already integrated via CPM ✅ 2. **ImGui Test Engine**: Available for E2E tests ✅ 3. **File Dialog**: `util::FileDialogWrapper` already exists ✅ ### Technical Risks #### Risk 1: JSON Schema Evolution **Impact**: Breaking changes to JSON format **Mitigation**: - Include version number in schema - Implement forward/backward compatibility - Provide migration tool for old exports #### Risk 2: Dictionary Auto-Optimization Complexity **Impact**: Algorithm may be too slow for real-time use **Mitigation**: - Run optimization in background thread - Provide progress indicator - Allow cancellation #### Risk 3: Large ROM Size with Expanded Messages **Impact**: May exceed bank boundaries **Mitigation**: - Implement repointing logic early (Phase 5) - Warn user when approaching limits - Suggest dictionary optimization --- ## Success Metrics ### Quantitative Metrics - [ ] 100% message export/import success rate (no data loss) - [ ] JSON schema supports all 396 vanilla messages - [ ] Translation workspace reduces edit time by 50% vs. current workflow - [ ] Search/Replace handles batch operations in <1 second - [ ] 90%+ test coverage for new code ### Qualitative Metrics - [ ] Translator feedback: "Translation workflow is intuitive" - [ ] No hardcoded colors in Message Editor - [ ] UI matches yaze style guide - [ ] Documentation complete for all new features --- ## Timeline Estimate | Phase | Duration | Effort (Dev Days) | |-------|----------|-------------------| | Phase 1: JSON Export/Import | 2 weeks | 6 days | | Phase 2: Translation Workspace | 3 weeks | 9 days | | Phase 3: Search & Replace | 1 week | 4 days | | Phase 4: UI Polish | 1 week | 2 days | | **Total** | **7 weeks** | **21 dev days** | **Note**: Timeline assumes single developer working full-time. Adjust for part-time work or team collaboration. --- ## Future Enhancements (Post-MVP) 1. **Scripting API** (Gemini's vision) - Expose MessageData to Lua/Python - Allow procedural text generation - Useful for randomizers 2. **Cloud Translation Integration** - Google Translate API for quick drafts - DeepL API for quality translations - Requires API key management 3. **Collaborative Editing** - Real-time multi-user translation - Conflict resolution for concurrent edits - Requires network infrastructure 4. **ROM Patch Generation** - Export as `.ips` or `.bps` patch files - Useful for distribution without ROM sharing - Requires patch generation library 5. **Message Validation** - Check for overlong messages (exceeds textbox width) - Verify all messages have terminators - Flag unused dictionary entries --- ## Open Questions 1. **Q**: Should we support multiple translation languages simultaneously? **A**: TBD - May require multi-ROM workspace UI 2. **Q**: How should we handle custom dictionary entries for expanded ROMs? **A**: TBD - Need research into ROM space allocation 3. **Q**: Should translation progress be persisted? **A**: TBD - Could store in `.yaze` project file 4. **Q**: Do we need undo/redo for message editing? **A**: TBD - ImGui InputTextMultiline has built-in undo, may be sufficient --- ## Conclusion The Message Editor has a **solid foundation** with core parsing, preview, and UI systems in place. The main gaps are **JSON export/import** (P0), **translation workspace** (P1), and **search/replace** (P2). **Recommended Next Steps**: 1. Start with Phase 1 (JSON export/import) - this unblocks all translation workflows 2. Get user feedback on translation workspace mockups before Phase 2 3. Defer theme integration to Phase 4 - not blocking functionality **Estimated Effort**: ~7 weeks to MVP, ~21 dev days total. **Success Criteria**: Translator can export messages to JSON, edit in external tool, and re-import without data loss. Side-by-side translation workspace reduces manual comparison time by 50%.