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
This commit is contained in:
commit
ca94b479bf
61 changed files with 125881 additions and 44 deletions
82
docs/research/2026-06-25-character-window-faithful-spec.md
Normal file
82
docs/research/2026-06-25-character-window-faithful-spec.md
Normal file
|
|
@ -0,0 +1,82 @@
|
|||
# Character window — faithful element spec (LayoutDesc 0x2100002E)
|
||||
|
||||
Decomp-grounded blueprint for the Character window, produced to REDO the guessed pilot.
|
||||
Sources: `docs/research/named-retail/acclient_2013_pseudo_c.txt` (PDB-named decomp),
|
||||
the user UI dump `docs/research/2026-06-25-retail-ui-layout-dump.json`, and acdream's own
|
||||
importer-resolved tree (dumped live in the studio). **No guessing** — every row cites a
|
||||
decomp address or a confirmed `FindElement` result.
|
||||
|
||||
## What 0x2100002E actually is
|
||||
|
||||
A **tabbed Attributes / Skills / Titles window** (300×600, root panel `0x10000227`, Type 8).
|
||||
It is NOT a text report. Three tab-content slots each mount a sub-layout via
|
||||
`BaseElement`+`BaseLayoutId` inheritance (acdream's `ShouldMountBaseChildren` path):
|
||||
|
||||
| Slot | BaseElement | BaseLayoutId | gm*UI | Register |
|
||||
|---|---|---|---|---|
|
||||
| `0x1000022B` (Attributes) | `0x10000225` | `0x2100002C` | gmAttributeUI (root type 0x1000002A) | 0x0049db50 |
|
||||
| `0x1000022C` (Skills) | `0x1000022E` | `0x2100002D` | gmSkillUI | 0x0049adb0 |
|
||||
| `0x10000539` (Titles) | `0x1000052D` | `0x2100005E` | gmCharacterTitleUI | 0x0049aba0 |
|
||||
|
||||
Both Attributes + Skills tabs share a header strip filled by the base class
|
||||
**`gmStatManagementUI`** (`UpdateCharacterInfo` 0x004f0770, `UpdateExperience` 0x004f0a70,
|
||||
`UpdatePKStatus` 0x004f00a0). The tab sub-layout `0x2100002C` nests `0x10000226` which
|
||||
chains again into `0x21000045` — the real header/list content is **two inheritance levels deep**.
|
||||
|
||||
The **text report** ("birth/age/deaths, innate attributes, augmentations, load") the earlier
|
||||
pilot guessed is a SEPARATE sub-panel: LayoutDesc `0x2100001A`, `gmCharacterInfoUI`
|
||||
(register 0x004b8c90), whose `m_pMainText` (`0x1000011d`) is created at RUNTIME. That window
|
||||
is handled by `CharacterController` (create-the-element path) — different layout, not 0x2100002E.
|
||||
|
||||
## Importer reality (dumped live, 2026-06-25)
|
||||
|
||||
- `dump-vitals-layout` prints the RAW dat (no inheritance) → the content elements look "missing".
|
||||
That is an artifact. The **importer** mounts them. Confirmed by `FindElement` on the studio's
|
||||
imported 0x2100002E:
|
||||
- `0x10000231` name → `UiText` ✓
|
||||
- `0x10000232` heritage → `UiText` ✓
|
||||
- `0x10000233` PK status → `UiText` ✓
|
||||
- `0x1000023B` level → `UiText` ✓
|
||||
- `0x10000235` total XP → `UiText` ✓
|
||||
- `0x10000236` XP-to-level meter → `UiMeter` ✓
|
||||
- `0x1000023D` list box → `UiDatElement` (generic container) ✓
|
||||
- `0x10000238` XP-to-level text → **NULL** (nested variant; not mounted — minor)
|
||||
- `UiElement.EventId` is NOT the dat element id (root shows 0x10000001, not 0x10000227). The dat
|
||||
id lives in `ImportedLayout._byId` (set from `info.Id`, LayoutImporter:107). Bind via
|
||||
`FindElement(id)`, never by reading `widget.EventId`.
|
||||
|
||||
## Header element map (gmStatManagementUI)
|
||||
|
||||
| Id | Field | Content source | Rect (in sub-layout) |
|
||||
|---|---|---|---|
|
||||
| `0x10000231` | m_pNameText | player full name | 0,25,230,20 |
|
||||
| `0x10000232` | m_pHeritageText | gender+heritage display (InqGenderHeritageDisplay) | 0,45,230,15 |
|
||||
| `0x10000233` | m_pPKStatusText | PK status string (StringTable) | 0,60,230,15 |
|
||||
| `0x1000023B` | m_pLevelText | PropertyInt 0x19 (level) | 235,60,65,50 |
|
||||
| `0x10000234` | (label) | "Total Experience:" | 0,95,130,18 |
|
||||
| `0x10000235` | m_pTotalXPText | PropertyInt64 1 | 130,95,100,18 |
|
||||
| `0x10000236` | m_pXPToLevelMeter | (cur−base)/(cap−base), img 0x060011A6 | 0,113,230,17 |
|
||||
| `0x10000237` | (label) | "To next level:" | 0,113,130,17 |
|
||||
| `0x10000238` | m_pXPToLevelText | XP remaining | 130,113,100,17 |
|
||||
| `0x1000023D` | m_pListBox | attribute/skill rows (runtime) | 0,137,300,398 |
|
||||
|
||||
Attribute list rows: gmAttributeUI builds `AttributeInfoRegion` rows (name + base/buffed value +
|
||||
raise button). Canonical AC display order: Strength, Endurance, Coordination, Quickness, Focus, Self.
|
||||
|
||||
## StringTable
|
||||
|
||||
`DatReaderWriter` CAN read `StringTable` (CLI uses `GetAllIdsOfType<StringTable>`). The decomp's
|
||||
`StringInfo::SetStringIDandTableEnum(…, stringId, 0x10000001)` references table enum 0x10000001 →
|
||||
the game string table dat (0x31000001 family); StringId=0 is a placeholder overwritten by a
|
||||
preceding `compute_str_hash("ID_…")`. To reproduce exact wording: read the StringTable + a
|
||||
StringInfo helper (hash → id → lookup, `AddVariable_*` substitution). NOT yet ported — current
|
||||
controller uses canonical AC labels.
|
||||
|
||||
## Status (this session)
|
||||
|
||||
- **DONE (commit 0e644b5):** `CharacterStatController` binds name/heritage/PK/level/total-XP +
|
||||
the XP meter fill + the six attributes into the real elements. Studio renders the real panel.
|
||||
- **Follow-ups (known sources):** (1) tab-button active/inactive STATE so the 3 tabs draw their
|
||||
sprites (0x06005D92-97); (2) StringTable wording; (3) full AttributeInfoRegion row template
|
||||
(column-aligned values + per-attribute raise buttons); (4) wire `0x10000234`/`0x10000237`
|
||||
labels + the right-side level area placement.
|
||||
116869
docs/research/2026-06-25-retail-ui-layout-dump.json
Normal file
116869
docs/research/2026-06-25-retail-ui-layout-dump.json
Normal file
File diff suppressed because it is too large
Load diff
45
docs/research/2026-06-25-studio-dump-panel-sweep.md
Normal file
45
docs/research/2026-06-25-studio-dump-panel-sweep.md
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
# UI Studio — dump-panel render sweep (2026-06-25)
|
||||
|
||||
All 26 windows in `docs/research/2026-06-25-retail-ui-layout-dump.json` were rendered through
|
||||
the studio's dump LayoutSource (`AcDream.App ui-studio --dump <slug> --screenshot <png>`) and
|
||||
read back as PNGs. Goal: verify the generic dump source renders every retail window, identify
|
||||
real render bugs vs. expected gaps.
|
||||
|
||||
## Verdict
|
||||
|
||||
The generic dump source renders **all 26 panels with no crash**. Render fidelity tracks how
|
||||
much of a panel is **static chrome** (present in the dump, so it draws) vs **runtime content**
|
||||
(slots, stats, appraise text — NOT in the dump, so it can't draw). This is the dump's nature,
|
||||
not a studio bug.
|
||||
|
||||
## Per-panel status (read from `studio-shots/`)
|
||||
|
||||
| Status | Panels |
|
||||
|--------|--------|
|
||||
| **Full / great** (frame + static content) | `vitals` (3 bars + heart/glyphs), `side_by_side_vitals`, `toolbar` (all spell/combat icons + backpack), `map`, `main_panel`, `radar` (disc), `character` (frame+tabs+scrollbar), `combat`, `powerbar`, `negative_effects`, `positive_effects`, `options`, `quests`, `social`, `spellbook`, `main_chat`, `floating_chat_1..4`, `environment_panel` |
|
||||
| **Partial** (chrome only; runtime content absent) | `inventory` — stone backdrop + top frame render, but the nested sub-window chrome (gm3DItemsUI / gmBackpackUI / paperdoll) is sparse and the slots are runtime-filled. **The one panel with a possible real gap — investigate the nested-layout / sub-window node handling.** |
|
||||
| **Blank** (runtime-only window, no static sprites in dump) | `examine` (appraise window — populated only on examine), and likely the small `indicators` / `smartbox` / `vitae` |
|
||||
|
||||
## Tooling shipped this sweep
|
||||
|
||||
- `--screenshot <path>` headless mode (`f778b10`): render the loaded panel (dump or dat) to a
|
||||
PNG off-screen via `PanelFbo` + `glReadPixels` + ImageSharp, then exit. Hidden window
|
||||
(`IsVisible=false`). The autonomous self-verification primitive.
|
||||
- Contact-sheet montage via PowerShell `System.Drawing` (one image, 26 thumbs) — the cheap
|
||||
per-sweep overview (`studio-shots/_contact.png`, gitignored).
|
||||
|
||||
## Rect basis (confirmed in `DumpLayout`)
|
||||
|
||||
Dump `rect` fields are ABSOLUTE retail screen coords; `DumpLayout` converts to parent-relative
|
||||
(`child.Left = child.Rect.X - parent.Rect.X`) and places the root at (0,0). Verified correct by
|
||||
the toolbar (icons land precisely) + vitals.
|
||||
|
||||
## Next (loop)
|
||||
|
||||
1. **Inventory nested chrome** — determine whether the sub-window content is missing because
|
||||
(a) those nodes are `Group`s with no static sprite (expected), or (b) `DumpLayout` isn't
|
||||
expanding a `base_layout_id` sub-layout reference (a fixable bug). Dump the inventory panel's
|
||||
node `widget_kind`/`rect`/`image_id` list and compare to what rendered.
|
||||
2. Spot-confirm the remaining blanks (`indicators`/`smartbox`/`vitae`) are runtime-only.
|
||||
3. The dump source is functionally complete as a static previewer; richer (live-data) previews
|
||||
are the dat-import + fixture path (the parked Task 4 bags/equip), not the dump path.
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
# Character window (Attributes tab) — retail reference + polish targets
|
||||
|
||||
Transcribed from two user-provided retail screenshots (2026-06-26): the **not-selected** state and the
|
||||
**Strength-selected** state. The PNGs live in the user's chat; this doc is the durable target spec for
|
||||
the remaining polish on acdream's `CharacterStatController` (LayoutDesc 0x2100002E). "✗" = acdream gap.
|
||||
|
||||
## State 1 — nothing selected
|
||||
|
||||
- **Tabs:** Attributes (active, gold label), Skills, Titles.
|
||||
- **Name:** "Horan" — **WHITE** (✗ acdream renders the name gold).
|
||||
- **Heritage line:** "Female Aluvian Adventurer" — white.
|
||||
- **PK line:** "Non-Player Killer" — white.
|
||||
- **Level area (right):** small caption "Character Level" (two lines: "Character" / "Level"), then the
|
||||
number **"240" as LARGE GOLD SPRITE DIGITS** — a number-sprite font, not plain text (✗ acdream draws
|
||||
"126" as plain gold text, and the caption truncates to "Character Le").
|
||||
- **"Total Experience (XP):"** caption + value "100,000,017,999" (✗ caption missing in acdream).
|
||||
- **"XP for next level:"** caption + value "99,738,801" with the red fill bar behind it (✗ caption +
|
||||
value missing — the value element is consumed by the UiMeter at import).
|
||||
- **Attribute list — 9 rows**, each = icon + name + right-aligned value:
|
||||
- Row text is **LARGER** (roughly the icon height ~24px) and rows are **TIGHTER** (less vertical gap)
|
||||
than acdream's current small-text / wide-spacing.
|
||||
- Order: Strength 200, Endurance 10, Coordination 10, Quickness 200, Focus 10, Self 10,
|
||||
Health 5/5, Stamina 10/10, Mana 10/10.
|
||||
- **Footer:** "Select an Attribute to Improve" (title) / "Skill Credits Available: 96" /
|
||||
"Unassigned Experience: 87,757,321,741". (acdream now matches — confirm the title says **Attribute**,
|
||||
not skill.)
|
||||
|
||||
## State 2 — Strength selected
|
||||
|
||||
- Same header.
|
||||
- **Selected row (Strength):** highlighted with a **DARKER background + bars above/below** — retail's
|
||||
selected-row sprite `0x06001397` (Button state 6). (✗ acdream uses a translucent GOLD tint — replace
|
||||
with the dark-bar sprite.)
|
||||
- **Footer flips to:**
|
||||
- Title: **"Strength: 200"** — **WHITE** text (✗ acdream uses the body/gold color).
|
||||
- "Experience To Raise:" + **"Infinity!"** (Strength is maxed → cost is infinite; ✗ acdream shows a
|
||||
numeric/"maxed" placeholder — show "Infinity!" when the attribute is at max).
|
||||
- "Unassigned Experience:" + "87,757,321,741".
|
||||
- Raise buttons (≜10 + triangle) shown at the selected row's right.
|
||||
|
||||
## Polish checklist (acdream)
|
||||
|
||||
1. [ ] Name color → white.
|
||||
2. [ ] Level → gold sprite digits (find the number-sprite font/ids); caption "Character"/"Level" 2-line.
|
||||
3. [ ] Add "Total Experience (XP):" caption.
|
||||
4. [ ] Add "XP for next level:" caption + value (un-consume from the meter, or render alongside).
|
||||
5. [ ] Row text larger (≈icon height) + rows tighter.
|
||||
6. [ ] Selection highlight → sprite 0x06001397 (dark bars), not gold tint.
|
||||
7. [ ] Selected footer title → white.
|
||||
8. [ ] Maxed attribute → "Experience To Raise: Infinity!".
|
||||
9. [ ] Footer title wording = "Select an Attribute to Improve" (Attribute).
|
||||
126
docs/research/2026-06-26-mockup-stage-handoff.md
Normal file
126
docs/research/2026-06-26-mockup-stage-handoff.md
Normal file
|
|
@ -0,0 +1,126 @@
|
|||
# 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.Import` → `DatWidgetFactory`. **STUDIO path only**; GameWindow passes null (issue **#157**).
|
||||
- **Fix 5** `ad4ed51` — `LayoutImporter.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.
|
||||
Loading…
Add table
Add a link
Reference in a new issue