Frames the next core-panels work for a fresh session, after B.1 (drag spine) + B.2 (toolbar reorder/remove + wire) + toolbar collapse merged this session. Three sub-phases in order: (A) minimal window manager (open/close + I-key toggle; 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 + InventoryController; the drag SOURCE that closes B.2's drag-from-inventory via a SourceKind==Inventory branch in ToolbarController.HandleDropRelease); (C) paperdoll UiViewport (Type 0xD) + Core→App IUiViewportRenderer seam, heaviest, last. Spell bar deferred. Reuse the shipped spine/ShortcutStore/wire. Paste-able new-session prompt + MEMORY index line inside. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
14 KiB
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.
UiRootholds the device-level drag (BeginDrag/UpdateDragHover/FinishDrag, ghost snapshotted at begin, drop delivered only on a real hit).UiElementhasGetDragPayload()/GetDragGhost()/IsDragSourcevirtuals (UiRoot stays item-agnostic).UiItemSlotis drag source + drop target + accept/reject overlay;UiItemListowns a registeredIItemListDragHandler { 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 : IItemListDragHandlerdrives aShortcutStore(AcDream.Core.Items, 18 slots)- the wire:
InventoryActions.BuildAddShortcut(seq,index,objectGuid,spellId,layer)/BuildRemoveShortcut, sent viaWorldSession.SendAddShortcut/SendRemoveShortcut. Green-cross accept sprite0x060011FA. Spec/plan:docs/superpowers/{specs,plans}/2026-06-20-d2b-toolbar-shortcut-drag*.md.
- the wire:
- Toolbar collapse —
UiCollapsibleFramebottom-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 0x21000023nests paperdoll (0x21000024) + backpack (0x21000022) + 3D-items (0x21000021); backpack burden Meter (SetLoadLevel→fill0x69); full inventory wire catalog + acdream parse-status.docs/research/2026-06-16-equipment-paperdoll-deep-dive.md— the doll =UIElement_Viewport(Type0xD) hosting a re-dressed player clone; ~25 equip slots; wield =GetAndWieldItem 0x001A(builder MISSING); needs a Core→AppIUiViewportRendererseam.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.4ClientObjectTable).
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 flipsVisibleand brings it to front viaZOrder). Today windows are alwaysVisible=true; the manager makes a window default-hidden and toggleable. (z-order/persist can be minimal —Visible+ a top-mostZOrderbump on open.) - An
InputAction(e.g.ToggleInventory) + the retail defaultIkeybind (cross-checkdocs/research/named-retail/retail-default.keymap.txt), wired through the existingInputDispatcher(seeclaude-memory/project_input_pipeline.md), gated byWantsKeyboardso 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 isUiItemListcells + anIItemListDragHandleron the controller.DatWidgetFactoryregisters0x10000031 → UiItemList(grep0x10000031insrc/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'sOnDragLiftdoes NOT remove — an inventory→bar drag creates a shortcut REFERENCE; the item stays in the pack), andToolbarController. HandleDropReleasegains theSourceKind == Inventorybranch (place a new shortcut at the target viaSendAddShortcut, no source-bump) — retail'sflags & 0xE == 0CreateShortcutToItempath.
MISSING (the build, in synthesis order — see the 2026-06-18 handoff §C.1-6 for the detailed list):
- Window manager — §3 above (do first).
UiItemListN-cell grid mode — currently single-cell (UiItemList.cs;Flush/AddItemskeleton exists, no column-count/pitch/wrap). LIKELY ~6 cols; confirm fromUIElement_ItemList::ItemList_AddItem.- Sub-window mount in
LayoutImporter—gmInventoryUI 0x21000023nestsgm*UIchildren with their ownBaseLayoutId; the importer only does TEMPLATE inheritance today (src/AcDream.App/UI/Layout/LayoutImporter.cs) — instantiating a nestedgm*UIwindow is new. - Wire gaps (inventory deep-dive §4.3): builders
DropItem 0x001B,GetAndWieldItem 0x001A,NoLongerViewingContents 0x0195; parsersViewContents 0x0196,SetStackSize 0x0197,InventoryRemoveObject; fixParsePutObjInContainer(drops the 4thcontainerType) +ParseInventoryServerSaveFailed(dropsweenieError); registerViewContents/0x019A/0x0052/0x00A0inGameEventWiring. (Grep these symbols — none landed this session.) InventoryController(gm*UI::PostInitfind-by-id pattern): backpack burden Meter (SetLoadLevel→fill0x69), 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(mergeabbd97b) — 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/#143are LIGHTING, not UI). Create a new worktree offmain.
- the collapse, plus the indoor-lighting handoff (
- ISSUES:
#144is the (LOW, latent) empty-item-slot click note from B.1;#141is the toolbar selected-object display (D.5.3a done; mana/stack deferred).#142/#143are 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.mdfirst 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.mdfirst, 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 tomain(abbd97b). Next, in order: (A) a minimal window manager — open/close + anI-key toggle for the inventory window (today every retail-UI window is always-on at a hardcoded position;UiHosthas no open/close API); (B) the inventory window (gmInventoryUInesting paperdoll/backpack/3D-items —UiItemListN-cell grid + sub-window mount inLayoutImporter+ the inventory wire gaps +InventoryController; it's the drag SOURCE that completes B.2's drag-from-inventory via aSourceKind==Inventorybranch inToolbarController.HandleDropRelease); (C) the paperdollUiViewport(Type 0xD) 3D doll, with a Core→AppIUiViewportRendererseam — 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 viaClientObjectTable.Get(guid)/GetContents(containerGuid). Start a new worktree offmain.
MEMORY.md index line:
- Handoff: window manager → inventory → paperdoll (2026-06-20) — 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.