acdream/docs/research/2026-06-26-mockup-stage-handoff.md
Erik ca94b479bf Merge claude/hopeful-maxwell-214a12 — D.2b UI Studio + faithful importer + Character window
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
2026-06-26 12:33:51 +02:00

8.9 KiB
Raw Blame History

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 via LayoutImporter, populated by the PRODUCTION controllers through FixtureProvider + 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 through LayoutImporter.ImportDatWidgetFactory. STUDIO path only; GameWindow passes null (issue #157).
  • Fix 5 ad4ed51LayoutImporter.BuildWidget builds a UiMeter'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"):

  1. 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.
  2. Window open/close/arrange (reuse the game's window-manager API).
  3. Drag + resize via the production frame chrome (already exists for the retail windows).
  4. Keep the inspector available as an optional overlay. The studio is the THIN host; the production UiHost is 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 + GetOrAdd pattern).
  • 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/UiMeter behavior 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 sweep docs/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/UiText are 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

  1. Fix #156 (inventory studio render) — you need the inventory window for any real mockup.
  2. 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.
  3. Then iterate window open/close/arrange + the desktop layout.