UI Studio (preview panels through the production renderer), importer dat-fidelity (Fix A/B/C/4/5: the importer carries dat font/justification/color; boundary look=importer / state=runtime), and the Character window Attributes tab (reads as retail). 3062 tests green. Handoff: docs/research/2026-06-26-mockup-stage-handoff.md. # Conflicts: # docs/ISSUES.md # docs/architecture/retail-divergence-register.md
8.9 KiB
Handoff — the full multi-window UI mockup stage (2026-06-26)
For the next agent. This session built the UI Studio, made the importer faithful to the dat, and brought the Character window to a retail look. The next stage is the user's actual goal: turn the studio into a full interactive multi-window mockup — several windows live in one host, draggable + resizable + clickable — so the whole UI can be tested without the game.
Read this, then claude-memory/project_ui_studio.md (the SSOT) and project_d2b_retail_ui.md.
1. Where we are (what's done + works)
The UI Studio (the dev tool)
AcDream.App ui-studio <datDir> [--layout 0xNNNN | --dump <slug>] [--screenshot <png>] — a standalone
Silk.NET/GL window that renders a UI panel through the production renderer (RenderBootstrap.Create
builds a RenderStack = the subset of GameWindow's render stack the UI needs; GameWindow is untouched).
- Code:
src/AcDream.App/Studio/(StudioWindow,FixtureProvider,PanelFbo,DumpLayout,UiDumpModel,StudioInspector,LayoutSource) +src/AcDream.App/Rendering/RenderBootstrap.cs. - Two panel sources:
--layout 0xNNNN(real dat-import viaLayoutImporter, populated by the PRODUCTION controllers throughFixtureProvider+SampleData), and--dump <slug>(the 26-window retail dump,docs/research/2026-06-25-retail-ui-layout-dump.json, static structure only). - Headless
--screenshot <png>(PanelFbo → glReadPixels → PNG) — the autonomous self-verify primitive. Use it constantly. - Interactive: click-routing forwards the ImGui canvas mouse → the panel's
UiHost(coord map =mouse − ImGui.GetItemRectMin(), no extra Y-flip). An Interact / Inspect toggle in the toolbar: Interact = clicks drive the panel; Inspect = clicks select elements in the tree.
The faithful importer (the "better way" — the load-bearing win)
The treadmill of hand-tuning each panel's look in C# was because the importer DROPPED the dat's per-element
visual properties. The dat carries them in StateDesc.Properties: 0x1A FontDID, 0x14/0x15 H/V
justification, 0x1B FontColor. We wired them in, backward-compatibly (the importer applies them as
DEFAULTS at build time; a controller that still sets them explicitly overrides → existing panels
byte-unchanged):
- Fix A
4143042— justification →UiText.Centered/RightAligned/VerticalJustify. - Fix B
6e0be4b— FontColor →UiText.DefaultColor(character colors proved RUNTIME — the dat carries none). - Fix C
a0d3395— FontDid → per-element font via a resolver (Func<uint,UiDatFont?>) threaded throughLayoutImporter.Import→DatWidgetFactory. STUDIO path only; GameWindow passes null (issue #157). - Fix 5
ad4ed51—LayoutImporter.BuildWidgetbuilds aUiMeter's non-slice (text) children while still consuming the Type-3 slice containers (vitals unaffected). - Fix 4
1b9dd6c— investigated, NO fix: the dat has no Visible flag; runtime gm*UI is correct.
THE BOUNDARY (the key conceptual result): the importer faithfully carries an element's LOOK (font, justification, color, position) from the dat. STATE + BEHAVIOR (which group is visible, tab / selection activation) is genuinely runtime gm*UI logic and belongs in the controller — it is NOT an importer gap (Fix 4 + the Fix-5 tab-skip proved this). Don't try to push state/visibility into the importer.
The Character window (the proof panel)
LayoutDesc 0x2100002E, src/AcDream.App/UI/Layout/CharacterStatController.cs — Attributes tab reads as
retail: 3 tabs, header, big-gold level number (its own dat FontDid), captions, 9-row attribute list
(icons + right-aligned values + Health/Stamina/Mana), click-to-select (top/bottom selection bars + footer
State-B "{Attr}: {value}" / "Experience To Raise: Infinity!" + affordability-gated raise triangles), centered
footer. User accepted it as good-enough 2026-06-26 ("still needs some polish for later" — see issue for
deferred items). Specs: docs/research/2026-06-25-character-window-faithful-spec.md,
2026-06-25-attributes-tab-interactive-spec.md, 2026-06-26-character-window-retail-reference.md.
Other panels with production controllers (the mockup's building blocks)
VitalsController (0x2100006C), ToolbarController (0x21000016), InventoryController + PaperdollController
(0x21000023), ChatWindowController, CharacterStatController (0x2100002E). All bind real importer-mounted
elements via FindElement(datId) (note: UiElement.EventId ≠ the dat id — the dat id lives in
ImportedLayout._byId).
2. The mockup goal + proposed approach
Goal: the studio shows ONE panel today. The mockup shows the whole UI at once — multiple windows in one host, each draggable + resizable + clickable — a real testbed.
Why it's reachable: the production UiHost (src/AcDream.App/UI/UiHost.cs) ALREADY manages multiple
draggable/resizable windows and routes input — it's the game's actual UI layer. The studio just needs to host
the full thing instead of one FBO'd panel. (Per memory, the game has a window manager: F12 toggles inventory;
there's a UiHost.ToggleWindow / WindowNames API.)
Proposed path (a new studio "mockup mode"):
- Load SEVERAL panels into ONE
UiHost(not one panel per FBO) — a "desktop." Render the host directly to the studio GL window (not the inspector FBO), so native input/drag/resize work. - Window open/close/arrange (reuse the game's window-manager API).
- Drag + resize via the production frame chrome (already exists for the retail windows).
- Keep the inspector available as an optional overlay.
The studio is the THIN host; the production
UiHostis the engine. Don't reimplement window management — drive the production one.
3. Must-fix before / during the mockup
- #156 — the inventory window (0x21000023) renders all-black in the studio (pre-existing fixture gap, NOT a regression). It's a key window for the mockup; fix the studio's inventory fixture/sub-window setup.
- #157 — GameWindow doesn't thread the Fix-C font resolver (live game = global font). Turnkey (the issue
has the exact 4 import sites + the
ConcurrentDictionary+GetOrAddpattern). - Character deferred polish — the user flagged "some polish for later" (level-glyph fidelity, icon crispness, sample strings). Filed as an issue; the user will enumerate.
4. Patterns + lessons (don't relearn these the hard way)
- Faithful-panel pattern: bind real importer-mounted elements via
FindElement(datId);EventId≠ dat id. - Importer dat-fidelity: the look comes from the dat; when re-deriving fonts/colors/positions in code feels like a treadmill, AUDIT what the dat element carries vs what the importer drops, then wire it backward-compat.
- The boundary: look = importer; state/behavior = runtime controller. Don't fight it.
- Apparatus over speculation: for a layout bug, DUMP the actual rendered rects (walk the tree, print abs rect + visible + text). One position dump cracked the footer-overlap after FIVE speculative passes. cdb is for runtime behavior questions, not "why is our element at the wrong place."
- Watch subagents for scope creep: the Fix-C subagent finished its task then committed unrequested AP-58 +
#148 (reverted per the user, kept in reflog
46c5cd2/82e13b7). Constrain dispatches to the assigned task. - Shared-infra changes: regression-screenshot vitals + toolbar (the canaries) every time; chat is the one
panel the studio can't easily preview, so never change global
UiText/UiMeterbehavior without verifying it.
5. Reference map
- Studio spec/plan:
docs/superpowers/specs/2026-06-25-ui-studio-previewer-design.md,docs/superpowers/plans/2026-06-25-ui-studio-previewer.md; dump sweepdocs/research/2026-06-25-studio-dump-panel-sweep.md. - Character window: the three specs above + the retail reference.
- Importer audit (this session): the dat carries FontDID/justification/FontColor in
StateDesc.Properties;ElementReader/LayoutImporter/DatWidgetFactory/UiTextare the wiring path. - The retail UI dump:
docs/research/2026-06-25-retail-ui-layout-dump.json(26 windows, real ids + rects + sprites). - Deferred studio features (the original plan, Tasks 5-8): live 3-D doll, markup hot-reload, editable props, camera sliders — secondary to the mockup.
- Memory SSOT:
claude-memory/project_ui_studio.md,project_d2b_retail_ui.md.
6. First concrete steps for the next agent
- Fix #156 (inventory studio render) — you need the inventory window for any real mockup.
- Prototype mockup mode: render the production
UiHost(with vitals + toolbar + character + chat opened) directly to the studio GL window, native input. Confirm drag + resize + click work through the production host. - Then iterate window open/close/arrange + the desktop layout.