feat(studio): #156 #157 mockup desktop and font resolver

Create AGENTS.md as a thin bridge to CLAUDE.md, fix isolated dat-layout previews by normalizing panel roots to the studio canvas, add a --mockup mode that mounts production-backed UI windows into one UiHost, and thread the per-element dat-font resolver through GameWindow's live imports.

Verified with dotnet test and headless studio screenshots for inventory and the mockup desktop.
This commit is contained in:
Erik 2026-06-26 12:56:05 +02:00
parent ca94b479bf
commit 9444a328fa
8 changed files with 405 additions and 44 deletions

View file

@ -1861,6 +1861,15 @@ public sealed class GameWindow : IDisposable
AcDream.App.UI.UiDatFont? vitalsDatFont;
lock (_datLock)
vitalsDatFont = AcDream.App.UI.UiDatFont.Load(_dats!, _textureCache!);
var datFontCache = new System.Collections.Concurrent.ConcurrentDictionary<uint, AcDream.App.UI.UiDatFont?>();
if (vitalsDatFont is not null)
datFontCache.TryAdd(AcDream.App.UI.UiDatFont.DefaultFontId, vitalsDatFont);
AcDream.App.UI.UiDatFont? ResolveDatFont(uint fontDid)
=> datFontCache.GetOrAdd(fontDid, id =>
{
lock (_datLock)
return AcDream.App.UI.UiDatFont.Load(_dats!, _textureCache!, id);
});
Console.WriteLine(vitalsDatFont is not null
? "[D.2b] vitals dat-font 0x40000000 loaded for numeric overlay."
: "[D.2b] vitals dat-font 0x40000000 unavailable — falling back to debug font.");
@ -1872,7 +1881,7 @@ public sealed class GameWindow : IDisposable
AcDream.App.UI.Layout.ImportedLayout? imported;
lock (_datLock)
imported = AcDream.App.UI.Layout.LayoutImporter.Import(
_dats!, 0x2100006Cu, ResolveChrome, vitalsDatFont);
_dats!, 0x2100006Cu, ResolveChrome, vitalsDatFont, ResolveDatFont);
if (imported is not null)
{
AcDream.App.UI.Layout.VitalsController.Bind(imported,
@ -1916,7 +1925,7 @@ public sealed class GameWindow : IDisposable
chatRootInfo = AcDream.App.UI.Layout.LayoutImporter.ImportInfos(
_dats!, AcDream.App.UI.Layout.ChatWindowController.LayoutId);
chatLayout = chatRootInfo is null ? null
: AcDream.App.UI.Layout.LayoutImporter.Build(chatRootInfo, ResolveChrome, vitalsDatFont);
: AcDream.App.UI.Layout.LayoutImporter.Build(chatRootInfo, ResolveChrome, vitalsDatFont, ResolveDatFont);
}
if (chatRootInfo is not null && chatLayout is not null)
{
@ -2058,7 +2067,7 @@ public sealed class GameWindow : IDisposable
AcDream.App.UI.Layout.ImportedLayout? toolbarLayout;
lock (_datLock)
toolbarLayout = AcDream.App.UI.Layout.LayoutImporter.Import(
_dats!, 0x21000016u, ResolveChrome, vitalsDatFont);
_dats!, 0x21000016u, ResolveChrome, vitalsDatFont, ResolveDatFont);
if (toolbarLayout is not null)
{
_toolbarController = AcDream.App.UI.Layout.ToolbarController.Bind(
@ -2196,7 +2205,7 @@ public sealed class GameWindow : IDisposable
AcDream.App.UI.Layout.ImportedLayout? invLayout;
lock (_datLock)
invLayout = AcDream.App.UI.Layout.LayoutImporter.Import(
_dats!, 0x21000023u, ResolveChrome, vitalsDatFont);
_dats!, 0x21000023u, ResolveChrome, vitalsDatFont, ResolveDatFont);
if (invLayout is not null)
{
var inventoryRoot = invLayout.Root;