# Handoff — window manager → inventory window → paperdoll (the next D.2b core-panels arc) **Date:** 2026-06-20 **From:** the D.5.3 session that shipped the drag-drop spine (B.1), toolbar shortcut reorder/remove + wire (B.2), and toolbar collapse-to-one-row — all **merged to `main`** (merge commit `abbd97b`; branch `claude/hopeful-maxwell-214a12`, tip `14443e5`). Full suite green (2752) at merge. **Line numbers below WILL drift — grep the symbol, don't trust the line.** --- ## 0. What just shipped (the foundation the next work builds on) This is now on `main` and visually confirmed: - **Drag-drop spine (B.1)** — the shared widget-level drag machine. `UiRoot` holds the device-level drag (BeginDrag/UpdateDragHover/FinishDrag, ghost snapshotted at begin, drop delivered only on a real hit). `UiElement` has `GetDragPayload()`/`GetDragGhost()`/`IsDragSource` virtuals (UiRoot stays item-agnostic). `UiItemSlot` is drag source + drop target + accept/reject overlay; `UiItemList` owns a registered `IItemListDragHandler { OnDragLift; OnDragOver; HandleDropRelease }`. Payload = `ItemDragPayload(ObjId, SourceKind, SourceSlot, SourceCell)`. Spec/plan: `docs/superpowers/{specs,plans}/2026-06-20-d2b-drag-drop-spine*.md`. - **Toolbar shortcut drag (B.2)** — retail **remove-on-lift / place-on-drop / no-restore**. `ToolbarController : IItemListDragHandler` drives a `ShortcutStore` (`AcDream.Core.Items`, 18 slots) + the wire: `InventoryActions.BuildAddShortcut(seq,index,objectGuid,spellId,layer)` / `BuildRemoveShortcut`, sent via `WorldSession.SendAddShortcut`/`SendRemoveShortcut`. Green-cross accept sprite `0x060011FA`. Spec/plan: `docs/superpowers/{specs,plans}/2026-06-20-d2b-toolbar-shortcut-drag*.md`. - **Toolbar collapse** — `UiCollapsibleFrame` bottom-edge snap resize (1-row ↔ 2-row). Spec: `docs/superpowers/specs/2026-06-20-d2b-toolbar-collapse-design.md`. **The one B-stream piece still owed:** *drag FROM inventory onto the bar.* B.2 did the within-bar reorder/remove (`SourceKind == ShortcutBar`). The fresh-from-inventory branch (retail `flags & 0xE == 0` → `CreateShortcutToItem`) needs the inventory window as a drag SOURCE — so it lands WITH Stream C below, not before it. --- ## 1. Read first - This doc. - `docs/research/2026-06-16-ui-panels-synthesis.md` §4 — **the build plan** for the core panels (build order, widget list, cross-panel wire table). Stream C follows it. - `docs/research/2026-06-16-inventory-deep-dive.md` — `gmInventoryUI 0x21000023` nests paperdoll (`0x21000024`) + backpack (`0x21000022`) + 3D-items (`0x21000021`); backpack burden Meter (`SetLoadLevel`→fill `0x69`); full inventory wire catalog + acdream parse-status. - `docs/research/2026-06-16-equipment-paperdoll-deep-dive.md` — the doll = `UIElement_Viewport` (Type `0xD`) hosting a re-dressed player clone; ~25 equip slots; wield = `GetAndWieldItem 0x001A` (builder MISSING); needs a Core→App `IUiViewportRenderer` seam. - `docs/research/2026-06-18-d53-bar-finish-and-inventory-handoff.md` §C — the predecessor's current-code readiness for the inventory window (most still accurate; deltas noted below). - `claude-memory/project_d2b_retail_ui.md` (the toolkit, now incl. B.1/B.2/collapse) + `claude-memory/project_object_item_model.md` (the D.5.4 `ClientObjectTable`). **Mandatory workflow** (CLAUDE.md): grep `docs/research/named-retail/acclient_2013_pseudo_c.txt` by `class::method` → cross-ref ACE/holtburger → pseudocode → port; conformance tests throughout. Each sub-phase gets its own brainstorm → spec → plan → subagent-driven (the flow this arc has been using). --- ## 2. Scope — three sub-phases, in order | # | Sub-phase | One-liner | |---|---|---| | **A** | **Window manager (open/close + `I`-key)** | The small shared infra to open/close a window + a keybind to toggle the inventory window. Prerequisite for everything below. | | **B** | **Inventory window (Stream C / D.5.5)** | `gmInventoryUI` nesting paperdoll + backpack + 3D-items. The drag SOURCE that completes "drag-from-inventory." | | **C** | **Paperdoll** (`UiViewport`) | The 3D dressed-doll viewport — the heaviest piece, its own Core→App seam. Last. | **Out of scope:** the spell bar (a separate spell-casting feature, NOT the action-bar spell shortcuts); the faithful dragbar/resizebar window resize (IA-12 whole-window-drag + the new bottom-edge collapse are the accepted approximations). --- ## 3. Sub-phase A — window manager (open/close + `I`-key) — DO THIS FIRST **Current state:** every retail-UI window is **always-on at a hardcoded position** — the vitals, chat, and toolbar are added to `_uiHost.Root` unconditionally at startup under `ACDREAM_RETAIL_UI=1` (grep `_uiHost.Root.AddChild` in `GameWindow.cs`; the mounts are in the `_options.RetailUi` block). `UiHost` has **no open/close API** (`src/AcDream.App/UI/UiHost.cs` — it exposes `Root`, `Tick`, `Draw`, `WireMouse/Keyboard` only). Input flows through `InputDispatcher` (Phase K) — `keybinds.json` + `KeyBindings`; the `I` key needs an action + binding. **Minimum deliverable:** open/close a top-level window + toggle the inventory window with `I`. Concretely: - A small open/close concept on `UiHost`/`UiRoot` (e.g. register a named window + Show/Hide/Toggle that flips `Visible` and brings it to front via `ZOrder`). Today windows are always `Visible=true`; the manager makes a window default-hidden and toggleable. (z-order/persist can be minimal — `Visible` + a top-most `ZOrder` bump on open.) - An `InputAction` (e.g. `ToggleInventory`) + the retail default `I` keybind (cross-check `docs/research/named-retail/retail-default.keymap.txt`), wired through the existing `InputDispatcher` (see `claude-memory/project_input_pipeline.md`), gated by `WantsKeyboard` so it doesn't fire while a chat input is focused. **Brainstorm questions (A):** Is the window manager a property on `UiRoot` (a registry of named top-level windows + Show/Hide/Toggle), or a thin `UiHost` API? How is z-order handled on open (bring to front)? Does `I` toggle (open↔close) or only open (Esc/close-button to close)? Persist open-state across the session (in-memory) only — no disk persistence yet (that's the deferred Plan-2)? Faithful retail note: retail windows are opened by the radar/menu buttons + hotkeys and managed by keystone.dll (no decomp) — so this is a toolkit-defined manager (IA-12/IA-15 umbrella), kept minimal. --- ## 4. Sub-phase B — inventory window (Stream C / D.5.5) — follow the synthesis §4 **The design is already written — follow `2026-06-16-ui-panels-synthesis.md` §4.** This section is the **current-code readiness** + what's missing. Don't re-derive the design. **READY (and stronger now, post-B.1/B.2):** - `UiItemSlot` + `UiItemList` + `IconComposer` (`src/AcDream.App/UI/`) — the shared item-cell spine, now with the full drag-drop machine (B.1). An inventory grid is `UiItemList` cells + an `IItemListDragHandler` on the controller. - `DatWidgetFactory` registers `0x10000031 → UiItemList` (grep `0x10000031` in `src/AcDream.App/UI/Layout/DatWidgetFactory.cs`). - The data path: `ClientObjectTable.GetContents(containerGuid)` → ordered guids → `Get(guid)` → full icon fields (`src/AcDream.Core/Items/ClientObjectTable.cs`). The object/container model shipped in D.5.4 (`project_object_item_model.md`). - The shortcut wire + `ShortcutStore` (B.2) — so **drag-from-inventory-onto-the-bar** is now a small addition: the inventory cell is a drag SOURCE (its handler's `OnDragLift` does NOT remove — an inventory→bar drag creates a shortcut REFERENCE; the item stays in the pack), and `ToolbarController. HandleDropRelease` gains the `SourceKind == Inventory` branch (place a new shortcut at the target via `SendAddShortcut`, no source-bump) — retail's `flags & 0xE == 0` `CreateShortcutToItem` path. **MISSING (the build, in synthesis order — see the 2026-06-18 handoff §C.1-6 for the detailed list):** 1. **Window manager** — §3 above (do first). 2. **`UiItemList` N-cell grid mode** — currently single-cell (`UiItemList.cs`; `Flush`/`AddItem` skeleton exists, no column-count/pitch/wrap). LIKELY ~6 cols; confirm from `UIElement_ItemList::ItemList_AddItem`. 3. **Sub-window mount in `LayoutImporter`** — `gmInventoryUI 0x21000023` nests `gm*UI` children with their own `BaseLayoutId`; the importer only does TEMPLATE inheritance today (`src/AcDream.App/UI/Layout/LayoutImporter.cs`) — instantiating a nested `gm*UI` window is new. 4. **Wire gaps** (inventory deep-dive §4.3): builders `DropItem 0x001B`, `GetAndWieldItem 0x001A`, `NoLongerViewingContents 0x0195`; parsers `ViewContents 0x0196`, `SetStackSize 0x0197`, `InventoryRemoveObject`; fix `ParsePutObjInContainer` (drops the 4th `containerType`) + `ParseInventoryServerSaveFailed` (drops `weenieError`); register `ViewContents`/`0x019A`/`0x0052`/ `0x00A0` in `GameEventWiring`. (Grep these symbols — none landed this session.) 5. **`InventoryController`** (`gm*UI::PostInit` find-by-id pattern): backpack burden Meter (`SetLoadLevel`→fill `0x69`), own-pack list + side-pack list, `ObjDescEvent 0xF625` → re-dress. **Brainstorm questions (B):** Sub-window mount — recursive `Import()` in `LayoutImporter`, or an external stitch by the controller? Grid column count (confirm 6 from decomp)? Open by `I`-key only, or also a toolbar inventory button? The inventory cell's drag semantics (lift does NOT remove from the pack — it's a copy-to-shortcut for the bar, but a real MOVE between packs DOES relocate via `PutItemInContainer 0x0019`) — pin the SourceKind→action matrix (deep-dive §5.7 opcode table). --- ## 5. Sub-phase C — paperdoll (`UiViewport`, Type 0xD) — heaviest, last **The single biggest new piece.** No widget, no factory registration, no renderer today. Needs an `IUiViewportRenderer` **Core→App seam** (structure Rule 2) for a scissored single-entity GL pass — the doll is the local player's ObjDesc-dressed entity in a fixed viewport. ~25 equip slots; the element-id→`EquipMask` map; wield = `GetAndWieldItem 0x001A` (builder still MISSING). Brainstorm separately (it's substantial). Follow `2026-06-16-equipment-paperdoll-deep-dive.md`. **Brainstorm questions (C):** Does the doll clone the player `WorldEntity` or build a fresh ObjDesc-dressed `AnimatedEntityState` (the player is the camera, so there's no player-as-renderable today)? `IUiViewportRenderer` timing (post-world pass vs pre-pass)? Scissor infra (the toolkit has no GL scissor yet — see the collapse spec's clipping discussion). --- ## 6. Build order + dependency graph ``` A. window manager (open/close + I-key) ← do first; unblocks B │ ▼ B. inventory window (grid + sub-window mount + wire gaps + InventoryController) │ └─ also completes B.2's drag-from-inventory (inventory cell = drag source; │ ToolbarController.HandleDropRelease gains the SourceKind==Inventory branch) ▼ C. paperdoll (UiViewport + IUiViewportRenderer seam + PaperDollController) ← heaviest, last ``` Critical-path note: A is small and unblocks B; the inventory window (B) is the big one and the drag *source* that closes the B-stream; the paperdoll (C) is the heaviest and independent enough to come last. --- ## 7. ⚠ State notes for the fresh session - **Start from `main`** (merge `abbd97b`) — it has all of D.5.2 + D.5.4 + this session's D.5.3 B.1/B.2 + the collapse, plus the indoor-lighting handoff (`f7f3e08`, issues `#142`/`#143` are LIGHTING, not UI). Create a new worktree off `main`. - **ISSUES:** `#144` is the (LOW, latent) empty-item-slot click note from B.1; `#141` is the toolbar selected-object display (D.5.3a done; mana/stack deferred). `#142`/`#143` are the lighting issues. - The drag spine's `IItemListDragHandler` + `ItemDragPayload` + `ShortcutStore` + the shortcut wire all exist now — reuse them; don't rebuild. - Don't re-port what WorldBuilder/the toolkit already gives (read `worldbuilder-inventory.md` first for any dat/render work). --- ## 8. New-session prompt (paste into a fresh session) > Continue acdream's D.2b retail-UI track. **Read `docs/research/2026-06-20-window-manager-inventory-handoff.md` first**, then the 2026-06-16 UI deep-dives + synthesis §4 it references. The drag-drop spine (B.1), toolbar shortcut reorder/remove + wire (B.2), and toolbar collapse all shipped + merged to `main` (`abbd97b`). Next, in order: **(A)** a minimal **window manager** — open/close + an `I`-key toggle for the inventory window (today every retail-UI window is always-on at a hardcoded position; `UiHost` has no open/close API); **(B)** the **inventory window** (`gmInventoryUI` nesting paperdoll/backpack/3D-items — `UiItemList` N-cell grid + sub-window mount in `LayoutImporter` + the inventory wire gaps + `InventoryController`; it's the drag SOURCE that completes B.2's drag-from-inventory via a `SourceKind==Inventory` branch in `ToolbarController.HandleDropRelease`); **(C)** the **paperdoll** `UiViewport` (Type 0xD) 3D doll, with a Core→App `IUiViewportRenderer` seam — heaviest, last. Spell bar DEFERRED. Use the full brainstorm → spec → plan → subagent-driven flow per sub-phase; mandatory grep-named→cross-ref→pseudocode→port for any wire format; conformance tests throughout. Reuse the shipped spine (`IItemListDragHandler`/`ItemDragPayload`/`ShortcutStore`/the AddShortcut/RemoveShortcut wire) — don't rebuild it. Resolve objects via `ClientObjectTable.Get(guid)` / `GetContents(containerGuid)`. Start a new worktree off `main`. **MEMORY.md index line:** - [Handoff: window manager → inventory → paperdoll (2026-06-20)](research/2026-06-20-window-manager-inventory-handoff.md) — next D.2b-UI arc after B.1/B.2/collapse (all merged to main `abbd97b`). 3 sub-phases: (A) window manager (open/close + I-key — UiHost has no open/close API today; windows are always-on); (B) inventory window (Stream C, synthesis §4 — UiItemList grid + sub-window mount + wire gaps DropItem 0x1B/GetAndWieldItem 0x1A/ViewContents 0x196 + InventoryController; the drag SOURCE that closes B.2's drag-from-inventory via a SourceKind==Inventory branch); (C) paperdoll UiViewport Type 0xD + Core→App IUiViewportRenderer seam (heaviest). Reuse the shipped drag spine + ShortcutStore + shortcut wire. Spell bar DEFERRED.