From 50758d479577c6eadcc94a06b82a04eff47bd63c Mon Sep 17 00:00:00 2001 From: Erik Date: Mon, 15 Jun 2026 19:07:05 +0200 Subject: [PATCH] docs(D.2b): chat-window re-drive session handoff (Plan 2 chat piece) Captures: current hand-authored chat window (UiNineSlicePanel + UiChatView, read-only, debug font), the importer toolkit to reuse, the retail gmMainChatUI oracles, the open design questions (scope / behavioral widgets / dat font), and the first research step (find the chat LayoutDesc id). Resume via brainstorming. Co-Authored-By: Claude Opus 4.8 (1M context) --- .../2026-06-15-chat-window-redrive-handoff.md | 135 ++++++++++++++++++ 1 file changed, 135 insertions(+) create mode 100644 docs/research/2026-06-15-chat-window-redrive-handoff.md diff --git a/docs/research/2026-06-15-chat-window-redrive-handoff.md b/docs/research/2026-06-15-chat-window-redrive-handoff.md new file mode 100644 index 00000000..33d12e92 --- /dev/null +++ b/docs/research/2026-06-15-chat-window-redrive-handoff.md @@ -0,0 +1,135 @@ +# Chat-window re-drive — session handoff (2026-06-15) + +**Status:** brainstorm STARTED (context gathered, design questions open) — not yet +designed or implemented. Resume with `superpowers:brainstorming`. + +**Branch:** `claude/hopeful-maxwell-214a12` — **continue UI work HERE** (the user's +call: UI stays on this branch; dungeon lighting / M1.5 goes to a *separate* branch +off `main`, it's unrelated and easy to merge). This branch is already current with +`main` (merged `5ac9d8c`). + +--- + +## Where we are (what shipped this session) + +**D.2b LayoutDesc importer — Plan 1 SHIPPED + flipped to default + post-flip fixes.** +The vitals window is now data-driven from the dat `LayoutDesc 0x2100006C` (no +per-window graphics code). Read **`claude-memory/project_d2b_retail_ui.md`** (the +SSOT crib) FIRST — it has the full architecture + every correction. Key commits: + +- `bf77a23` — flip: importer is the default vitals at `ACDREAM_RETAIL_UI=1`; the + hand-authored `vitals.xml` + the `ACDREAM_RETAIL_UI_IMPORTER` flag were retired. +- `8aa643f` — horizontal resize: edge-anchor mapping corrected to retail + `UIElement::UpdateForParentSizeChange @0x00462640` (`RightEdge==1`=stretch). +- `43064ba` — stamina/mana numbers: `TextRenderer` now draws sprites in + **submission (painter) order** (was per-texture batched → later bars overpainted + their own numbers). +- `34243f2` — number sharpness: `DrawStringDat` **pixel-snaps** each glyph dest. + +**The importer toolkit to REUSE (all in `src/AcDream.App/UI/Layout/`):** +- `ElementReader` — `ElementInfo` POCO + `Merge` (BaseElement/BaseLayoutId + inheritance) + `ToAnchors` (edge-flag → AnchorEdges, decomp-correct). +- `UiDatElement` — generic per-DrawMode sprite renderer (the fallback widget). +- `DatWidgetFactory` — `Type → widget` hybrid: Type 7→`UiMeter`, 12→skip, else + generic; sets rect + anchors + `ZOrder=ReadOrder`. **Behavioral Types map to a + dedicated widget; the widget CONSUMES the element's children (leaf — importer + does not recurse them).** This is the pattern the chat re-drive extends. +- `LayoutImporter` — `Import`/`ImportInfos`/`Build`/`BuildFromInfos` + cycle-guarded + `Resolve`. `ImportedLayout.FindElement(id)` for binding by id. +- `VitalsController` — binds live data to widgets by element id (mirrors retail + `gmVitalsUI::PostInit`). The chat controller will mirror this. +- Format reference: **`docs/research/2026-06-15-layoutdesc-format.md`** (ElementDesc + API, Type table, DrawMode, inheritance). NOTE its §4 edge-flag history: the FIRST + reading was inverted; the CORRECT model (per `@0x00462640`) is now in the doc + + `ToAnchors` — `RightEdge==1`=stretch, `LeftEdge==2`=track-right. + +--- + +## Next task: re-drive the chat window through the importer (Plan 2 chat piece) + +Today the chat window is **hand-authored**, not data-driven. The goal mirrors the +vitals re-drive: read the chat window's dat `LayoutDesc`, build it via +`LayoutImporter`, and bind the live chat through a `ChatController`. + +### Current chat window (what to reproduce / replace) +- Built in `src/AcDream.App/Rendering/GameWindow.cs` in the `if (_options.RetailUi)` + block (~line 1836, "Retail chat window"). +- `UiNineSlicePanel` (hand-authored 8-piece chrome) at `(10,432)`, `440×184`, + `MinWidth 180 / MinHeight 80`, draggable + resizable. +- Hosts a `UiChatView` (`src/AcDream.App/UI/UiChatView.cs`): scrollable transcript, + **bottom-pinned**, mouse-wheel scrollback, **drag-select + Ctrl+C copy + Ctrl+A**, + whole-line vertical clipping. **READ-ONLY** (no input box). Uses the **debug + bitmap font** (`_debugFont`), NOT the dat font. `LinesProvider` polled each frame. +- Data: `ChatVM` (`displayLimit: 200`) → `RecentLinesDetailed()` → per-`ChatKind` + colour via `RetailChatColor(...)` (local static in GameWindow). + +### Chat pipeline (already shipped, Phase I — reuse, don't rebuild) +`ChatLog (Core) → ChatVM (UI.Abstractions) → view`; outbound `input → +ChatInputParser → LiveCommandBus → WorldSession`. See +`claude-memory/project_chat_pipeline.md`. The re-drive is a VIEW/layout change; the +pipeline stays. + +### Retail chat UI classes (decomp oracles — analogous to gmVitalsUI) +`gmMainChatUI`, `gmFloatyMainChatUI`, `gmFloatyChatUI`, `gmChatOptionsUI` +(`docs/research/named-retail/acclient.h` ~line 54923; `symbols.json` has +`gmMainChatUI::Register` etc.). Chat-layout notes: +`docs/research/retail-ui/05-panels.md:120` (chat window layout) + +`06-hud-and-assets.md:651` (every chat window layout is a `LayoutDesc`). + +### FIRST research step (the Task-1 analogue): identify the chat `LayoutDesc` id +The vitals id was `0x2100006C`; the chat window's id is **NOT yet known**. Find it: +- `dump-vitals-layout [0xId]` enumerates LayoutDescs (it already lists all + layouts containing given element ids). Use it to scan for the chat window, or grep + the decomp for the layout id referenced by `gmMainChatUI`/`gmFloatyMainChatUI`. +- Then dump it and enumerate its element Types (expect a scroll/list region + + scrollbar, maybe a text-input/edit element + channel tabs) — this drives the + factory/widget work. + +--- + +## Open design questions (resume the brainstorm here) + +1. **Scope.** Re-drive the EXISTING read-only window (frame from dat + reuse + `UiChatView` for the transcript, parity with today), OR expand to the FULL retail + chat (input box for typing, channel tabs)? Recommendation to discuss: do the + frame re-drive + transcript first (parity), defer input/tabs to a follow-up — + but confirm with the user. +2. **Behavioral widgets.** The chat dat layout introduces the long-tail Types the + vitals didn't have — Type 5 `ListBox`, Type 0xB `Scrollbar`, maybe Type 0xC + `Text`/edit. Two options: + - **(A, recommended) Hybrid reuse** — like Type-7→`UiMeter`: map the transcript + region's Type → the existing `UiChatView` (which already scrolls/selects/copies); + a `ChatController` binds the tail by element id. Minimal new code; fastest parity. + - **(B) Port faithful widgets** — implement `UiScrollbar`/`UiListBox` per the + decomp so the dat's scrollbar element drives scrolling. More faithful, more work; + better as a later step. +3. **Dat font for the transcript.** Switch `UiChatView` from the debug bitmap font + to the dat font (`UiDatFont`, faithful + now pixel-snapped) — OR keep the debug + font for parity first? `UiChatView`'s measure/selection logic is `BitmapFont`-based, + so a dat-font port is non-trivial (a `UiDatFont` measure/advance path + selection + hit-test rework). Likely a follow-up, not the first cut. + +--- + +## Watchouts / lessons (don't regress these) +- **`TextRenderer` draws sprites in submission order** (`_spriteSegs`). Do NOT revert + to per-texture batching — it overpaints later same-atlas text (the stamina/mana bug). +- **`DrawStringDat` pixel-snaps glyphs.** Keep it (sharp text on resize). +- **Edge-flag/anchor model is `@0x00462640`** (`RightEdge==1`=stretch). The format + doc §4's first reading was inverted; trust the corrected `ToAnchors`. +- **Behavioral widgets are leaf** — the factory's widget consumes the element's dat + children; the importer doesn't recurse into them. Apply the same to the chat + transcript widget. +- **Don't fabricate dat reader internals** — `Chorizite.DatReaderWriter` is a NuGet + package (not in `references/`); verify member names via the dump tool / reflection. + +## Process for the next session +1. Read `claude-memory/project_d2b_retail_ui.md`, this handoff, and + `docs/research/2026-06-15-layoutdesc-format.md`. +2. Resume `superpowers:brainstorming` — settle scope + behavioral-widget approach + (the 3 questions above), present a design, write the spec. +3. Then `superpowers:writing-plans` → `superpowers:subagent-driven-development` + (same flow that shipped the vitals importer cleanly). +4. Stay on `claude/hopeful-maxwell-214a12`. Visual checks: launch live (ACE on + `127.0.0.1:9000`) with `ACDREAM_RETAIL_UI=1`; test accounts `testaccount2 / + testpassword2` or `notan / MittSnus81!` (character `+Je`).