Start with the table below. OverworldEditor owns editor lifecycle, shared state, service wiring, save dispatch, and undo integration. Tile16Editor owns the Tile16 definition window and canvases. Its session publishes edits directly to the open overworld document. Both remain at this directory's root so callers and parallel refactors have stable entry points.
| Location | Owns | Start here |
|---|---|---|
| Root | Editor lifecycle and integration | overworld_editor.h, overworld_editor.cc |
| Root | Tile16 window wiring and canvases | tile16_editor.h, tile16_editor.cc |
canvas/ | Hit testing, pan/zoom, map selection, canvas rendering | canvas_navigation_manager.cc, overworld_canvas_renderer.cc |
painting/ | Captured rectangular brushes, painting, fill, and clipboard placement | tile_brush.h, tile_painting_manager.cc |
maps/ | Property edits, metadata, refresh, and texture coordination | map_properties.cc, map_refresh_coordinator.cc, map_texture_coordinator.cc |
entity/ | Entity rendering, editing targets, and mutation services | entity_workbench.cc, entity_mutation_service.cc, entity_operations.cc |
tile16/ | Document edits, metadata transactions, palette/graphics coordination, history | tile16_edit_session.h, tile16_edit_history.cc |
ui/navigation/ | Toolbar and sidebar layout | overworld_toolbar.cc, overworld_sidebar.cc |
ui/tiles/ | Tile selector/window content and scratch workspace | tile16_selector_view.cc, tile16_editor_view.cc, scratch_space.cc |
ui/canvas/ | Canvas window content registration | overworld_canvas_view.cc |
ui/debug/ | Debug and usage-statistics content | debug_window_card.cc, usage_statistics_card.cc |
ui/ and ui/shared/ | Shared editor modes and window context | ui_constants.h, overworld_window_context.h |
core/ | Dispatch from keyboard/toolbar actions to editor callbacks | interaction_coordinator.cc |
panels/ | Existing compatibility wrappers and remaining window content | overworld_panel_access.h |
automation.cc stays beside the editor because it connects multiple features. overworld_undo_actions.h also stays there: it currently contains tile painting, entity, map-property, and project-label history actions. Moving it into one feature would imply an ownership split that has not happened.
The panels/ compatibility directory remains supported. Do not combine a source move with a migration of window IDs, content registration, or shell contracts.
ui/canvas/ calls the editor. canvas/ determines the hovered physical map and provides the canvas interaction state.TileBrush: width, height, and Tile16 IDs in row-major order. Painting and preview use that captured snapshot. Moving the hover or editing the source map must not recapture the selection.src/zelda3/overworld/ owns tile IDs. Bitmap updates and the maps/ coordinators make those changes visible.OverworldEditor::CreateUndoPoint and FinalizePaintOperation record history. OverworldEditor::Save dispatches the model's ROM serializers.Keep the hovered physical map distinct from its parent area. A multi-area room can share graphics while each child map has its own tile positions. Treat Light, Dark, and Special World coordinates explicitly when clipping a stamp or fill.
The painting implementation resolves each destination cell in world coordinates and clips it against that world's bounds. A rectangular stamp may cross physical screen boundaries. Undo records the cells that changed across those screens; each stamp, fill, or paste finalizes its edit. Release also finalizes pending single-tile painting outside the canvas. History retains its existing timed merge window for consecutive paint actions.
Copy and saving to scratch read the captured brush snapshot. Scratch preserves its existing 32-by-32 size limit. Clipboard paste uses the hovered canvas cell as its anchor and the same painting path as a stamp. The fill operation repeats the captured rectangle across the hovered screen. These contracts need focused tests and visual acceptance; their presence in this guide is not a completed runtime qualification claim.
Tile16Editor edits the four Tile8 entries, palette, flips, and priority of a definition. Its pending model and bitmap previews are distinct from the overworld's committed definitions. Trace CommitAllChanges, DiscardAllChanges, and the callbacks installed in OverworldEditor::Load together when changing that workflow.
Do not assume updating a definition updates every cached map or atlas. Check the model, the active map's derived graphics, the selector atlas, and texture refresh separately. A commit/discard cache fix needs both transitions covered.
Palette metadata stores a direct row (0-7). Recolor source pixels with (selected_row * 16) + (pixel & 0x0F). Read the current implementation and Tile16 data flow before changing palette or graphics ownership.
The canvas menu is prepared once when it opens. Its context target captures the physical map, parent area, world position, game state, and Tile16 ID. The context actions resolve that value from the opening click; menu callbacks must capture it by value. Do not rebuild the menu from the hovered or selected map while a popup is open.
Menu layout lives in MapPropertiesSystem::SetupCanvasContextMenu; the current layout is documented in docs/internal/gui/context-menus.md ("Overworld map canvas"): header (map, tile), Tile, Map (select, properties, pin, related maps), map-properties copy/paste, Insert, and a View submenu that mirrors the toolbar toggles.
Where things live (quick appearance fields share the full property edit path):
| Surface | Contents |
|---|---|
Toolbar (ui/navigation/overworld_toolbar.cc) | World LW/DW/SW, map id + pin, labeled Select/Paint/Fill, entity menu, View menu, Windows, Properties, Choose Tile16, Edit Tile16, Graphics groups |
Canvas appearance row (OverworldSidebar::DrawQuickProperties) | Target parent area and follow/pinned status, area graphics, area palette, main palette when supported; immediate refresh and the same undo path as Map Properties |
Map Properties panel (ui/navigation/overworld_sidebar.cc) | Game state, area size, message, area/sprite/animated graphics, custom tile sheets, area/main/sprite palettes, background color, music, visual effects, mosaic; right-click a value to rename its project label |
| Canvas context menu | Quick per-map actions only |
Toolbar groups and appearance fields wrap in narrow docks. Secondary display controls live in the labeled View menu. Tooltips show live ShortcutManager bindings via GetDisplayString. Appearance widget IDs include the target area so an active input cannot carry a typed value to a different area during cursor-follow.
Gestures (Select tool unless noted): left-drag empty map or middle-drag pans; drag an entity to move it (item moves are undoable); double-click a map opens Map Properties; right-click opens the map menu. Brush/Fill: right-click samples the tile16 and makes that map current, right-drag captures a multi-tile brush, [/] cycle the tile16, Shift+right-click opens the map menu. Keys: 1 select, 2/B brush, F fill, 3-6 entity focus (plain keys only; Cmd/Ctrl+digits switch editors), Alt+1/2/3 world, Alt+arrows adjacent map, =/- zoom, 0 fit, Home center, G grid, E entities, arrows nudge the selected item.
Scrolling: CanvasNavigationManager::BeginCanvasViewport owns all canvas scrolling and applies it with SetNextWindowScroll before the child begins (no one-frame lag); the child sets ImGuiWindowFlags_NoScrollWithMouse. Wheel/trackpad pan moves in whole detents of kOverworldPanSnapMapPx scaled by zoom, drops sub-detent residue after kOverworldWheelIdleResetSec, and ignores deltas under kOverworldWheelDeadzone (momentum tails). Cmd/Ctrl+wheel zooms about the cursor. Drag pan pins the grabbed point under the cursor and only starts from a press on the canvas. All tunables are in ui/ui_constants.h.
Unpinned map selection follows the cursor in Mouse, Brush, and Fill modes. Pin through the toolbar, Ctrl+L, or the context menu to hold the property target. Brush/Fill previews and painted pixels use the destination map's own tile16 blockset and palette (TilePaintingManager::DrawBrushPreview, Tile16PixelsForMap), so a pinned map or a stroke that crosses into another area never shows or writes the current map's graphics there. Explicit clicks select the map under the cursor even when pinned, and the pin then holds that map: a Select-tool left click that did not pan (on release; its press must also have landed on the canvas, on the same map, so a press that dismissed a menu does not select) and a Brush/Fill right click. Both go through CanvasNavigationManager::SelectMapUnderCursor (policy: IsMapSelectClick); large areas resolve to the parent area's properties. Middle-drag only pans; it does not pin or open properties. Entity dragging holds the selected map until release. Hover tracking and right-click targeting share the same scaled coordinate validation.
Use maps/overworld_property_edit.h for a property change and maps/overworld_map_metadata.h for metadata resolution. Property UI must route through the editor's edit callbacks so undo and required refresh remain coupled.
For entities, begin with entity/entity_workbench.cc and entity/entity_mutation_service.cc. Keep stable entity identity separate from vector position when changing selection, deletion, or history. ROM storage formats belong under src/zelda3/overworld/, not in ImGui drawing code.
Deferred insertion consumes an entity insertion request containing both the entity type and captured destination. The Entity Workbench must not replace that destination with the next frame's selected map or game state. Item and sprite game coordinates are relative to the parent area's origin, including child-screen offsets; modulo 512 discards those offsets.
src/zelda3/overworld/overworld_sprite_io.* owns pointer/list encoding. Both Overworld::Save and OverworldEditor::Save prepare the sprite plan before mutating ROM data. The editor checks actual sprite write ranges against the project hack manifest. Publication is fenced and rolls back on failure.
Only successfully loaded parent-map lists are replaced. Other lists are read from the ROM and preserved, including expanded entries outside current loader coverage. Exact ordered streams share storage; duplicate sprites and coordinate flag bits are retained. Unchanged lists produce no save plan. Sprite movement uses world coordinates relative to the owning parent, on a 16-pixel grid. This does not implement cross-area sprite reassignment or expand loader coverage.
Do not add a second serializer in UI code, silently truncate positions, advance an original-load baseline after save, or allocate past the reserved region. Unsupported pointer relocation fails closed and needs an explicit layout extension.
| Task | Scope | Acceptance check |
|---|---|---|
| Improve a toolbar group or sidebar label | ui/navigation/ | Existing shortcuts, IDs, focus, and disabled behavior remain correct; inspect narrow and wide windows. |
| Improve tile selector feedback | ui/tiles/ plus an existing helper if needed | Hover, selection, and document edits remain distinct; compare preview and actual paint. |
| Extract one property validation rule | maps/overworld_property_edit.* | Add a focused valid/invalid test; preserve callbacks, undo, and unrelated metadata. |
| Simplify one Tile16 interaction rule | tile16/ helper and its caller | Verify immediate publication, compound Undo/Redo, and document Save; keep layout changes separate. |
| Extract one entity operation | entity/ | Prove identity and history across insert/delete; keep ROM encoding unchanged. |
For a file move, change only location, includes, CMake registration, and affected documentation. Preserve function bodies. For behavior changes, state the trigger and expected outcome first, then add a regression that observes that outcome. Do not combine broad formatting, renaming, and feature work in one patch.
Cursor's session/workbench split is integrated in this repair branch. Change layout in tile16_workbench.cc, edit rules in tile16_edit_session.cc, and transaction/history rules in tile16_edit_history.cc.
Overworld::tiles16() is the authoritative definition data for a bound session. BindDocument gives the session the owning editor's UndoManager. The owner clears history when replacing/loading the document.RunEdit. It publishes metadata immediately, marks the document dirty, and records one complete before/after batch. A four-definition stamp must undo all four definitions. No-op/invalid edits must not clear Redo. Use ReplaceCurrentTile for property UI; do not mutate the pointer returned by GetCurrentTile16Data from new UI code.on_document_changed invalidates shared map/atlas caches after edit, Undo, and Redo. The selected map is rebuilt immediately; other maps use the existing deferred refresh budget. History stores metadata, not graphics tied to the source map's palette. Do not restore stale cached pixels on map changes.OverworldEditor::Save serializes definitions through SaveMap16Tiles or SaveMap16Expanded. Edits/Undo/Redo do not write ROM bytes. Legacy standalone serialization adapters remain for older tooling/tests; bound sessions reject those write/revert paths. Their legacy pending accessors describe local edit caches, not an additional user confirmation step.git status --short, then inspect the touched feature and its tests. Register moved/new .cc files in editor_library.cmake. Use canonical include paths such as app/editor/overworld/maps/map_refresh_coordinator.h..clang-format. For diagnostics, use the existing build's compile_commands.json and run clang-tidy on the touched translation unit. Review fixes individually; do not apply a repository wide -fix pass while debugging behavior.cmake --preset mac-ai && cmake --build --preset mac-ai --parallel 4. Other platforms should select their supported preset with cmake --list-presets; do not copy another machine's absolute build paths.test/unit/editor/tile_painting_manager_test.cc, canvas_navigation_manager_test.cc, map_refresh_coordinator_test.cc, overworld_map_metadata_test.cc, overworld_property_edit_test.cc, tile16_editor_action_state_test.cc, and tile8_source_interaction_test.cc. Tile16 integration coverage lives in test/integration/editor/tile16_editor_test.cc.For visual acceptance, use a disposable ROM copy. Check paint/preview agreement, fill boundaries, rectangular stamp dimensions, map/world transitions, release outside the canvas, and undo/redo. Definition editing additionally needs edit → switch map/palette → Undo/Redo checked against both map and selector pixels, plus Save and independent reopen.