Skip to content
Merged

Dev #11

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .agents/agent-knowledge.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@

## 当前工作

- [F-20261011-03-theme-owned-views](../features/F-20261011-03-theme-owned-views/summary.md) 已移除 GameView 普通页面和默认玩家样式。主题参数必需,最终映射校验完整性;Page 仅保留 GameShell/GamePage/ScreenshotDialog 行为。Themes 提供布局、通用样式与工具栏;截图独立构造/释放,普通页面 Scoped。Editor 显式 Classic 并采用 ThemesAssembly 别名。

- [F-20261011-02-player-page-layout](../features/F-20261011-02-player-page-layout/summary.md) 已补齐三套内置主题的全部普通页面 XAML View,主题目录自行维护布局、共享 ViewModel/命令;页面无外框,主题内导航位置一致。对白名字比正文大4,Classic/Paper 26/22、Minimal 24/20;默认 GameView 页面回退已由 F-20261011-03 删除。

- [F-20261011-01-game-ui-themes](../features/F-20261011-01-game-ui-themes/summary.md) 已完成启动期主题组合:可选 Themes 提供三布局/三配色,页面顺序已由 F-20261011-03 收敛为共享 GamePage→主题→宿主→完整性校验;GamePage 是稳定 TemplatedControl,四个 PART_* 承载其自有内容,派生页与 Runtime 保持同一 Scoped 实例。样式应用于玩家根,官方主题局部 Fluent 与宿主 Semi 隔离。当前无运行期切换;配置/契约见 Themes README、docs/spec/control.md、ADR 0003。

- [F-20261009-02-video-entries](../features/F-20261009-02-video-entries/feature.md) 已实现剧情视频图层、阻塞分段 seek、停止/替换清理和非阻塞视频定义恢复。平台视频必须经 AssetManager 获取,解码帧通过租约进入场景管线;播放游标不入存档。验证见 feature summary。

- [F-20261010-02-runtime-i18n](../features/F-20261010-02-runtime-i18n/summary.md) Phase 1–5 已实现。Sample 用 Core 2.1.2 + ReflectionBinding;每 JSON 独立 provider,culture-first 查询内嵌/包内/External/额外源;首次选择在 Engine 前原子保存 locale/count。设置切换不重译当前剧情。安装器保留 External 并拒绝覆盖不同包,导出不包含第三方文件。当前契约见 `docs/spec/i18n.md`,人工 GUI 全程与原生媒体 opt-in 验证限制见 summary。
Expand Down Expand Up @@ -81,3 +87,7 @@
- [DynamicLocalization Avalonia 主版本兼容性](lessons/L-20261010-05-dynamiclocalization-avalonia-version.md):2.1.0 与 2.1.2 的 Avalonia extension 都不适用于当前 Avalonia 12 Sample;后者虽能编译仍在启动时调用已移除的 Binding 构造函数。Sample 使用 Core + 自有 `ReflectionBinding` 适配层。
- [JSON provider 的整 culture 替换](lessons/L-20261010-07-json-provider-replaces-culture.md):2.1.2 的 LoadJsonString 不增量合并,也不做父 culture 回退;分文件需独立 provider 与宿主有序查询。
- [Render 内的粒子耗尽清理](lessons/L-20261010-08-render-pass-particle-drain.md):confirmed;UI Dispatch 在 UI 线程同步执行,不能作为渲染回调清理的延迟队列;使用保证异步的 `Post`,并以 emitter identity 防止旧回调删除替换实例。

- [Headless session cleanup](lessons/L-20261011-01-headless-session-cleanup.md):async Dispatch 测试使用 await using,避免同步 Dispose 等待同一个 session worker;动态 Brush 预期值要在资源宿主中解析或比较语义 Color。

- [Editor Avalonia aliases](lessons/L-20261011-02-editor-avalonia-alias.md):Editor 引用 GalNet.Avalonia.* 时采用程序集别名,避免既有 Avalonia 限定名称被相对命名空间解析遮蔽。
20 changes: 20 additions & 0 deletions .agents/lessons/L-20261011-01-headless-session-cleanup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Headless session cleanup in asynchronous Avalonia tests

Status: confirmed
Source: F-20261011-01-game-ui-themes

## Symptom and evidence

The UI action reached an assertion, then the test appeared to hang until the blame timeout killed testhost. The same case passed after asynchronous session disposal and fixing the palette comparison. Avalonia.Headless 12.0.5 decompiled implementation confirms Dispatch uses a TaskCompletionSource without RunContinuationsAsynchronously, while synchronous Dispose waits for the session dispatch task.

## Cause and rule

An awaited Dispatch may resume the test continuation inline on the session worker. Synchronous Dispose can then wait on that same worker task. Use await using for HeadlessUnitTestSession in asynchronous tests; never synchronously dispose the session after awaited Dispatch. A hang at a failed assertion can hide the original assertion error.

## Related palette observation

A newly loaded resource dictionary that is not attached to a resource host has unresolved DynamicResource brush colors. Compare semantic Color resources or attach the expected resources before comparing brush colors; do not treat detached transparent brush values as the expected palette.

## Scope

Avalonia.Headless session tests with asynchronous Dispatch. Tests: GameThemeTests. See feature summary for final verification.
9 changes: 9 additions & 0 deletions .agents/lessons/L-20261011-02-editor-avalonia-alias.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Editor 的 Avalonia 程序集别名

状态:confirmed。来源:F-20261011-03-theme-owned-views。

现象:Editor 直接全局引用 GalNet.Avalonia.Themes 后,既有 Avalonia.Controls/Input 等限定名称被解析为 GalNet.Avalonia 下的成员,出现 CS0234 和接口返回类型错误。

根因与证据:Editor 位于 GalNet.Editor 命名空间;新增 GalNet.Avalonia.Themes 暴露同级 GalNet.Avalonia,使 Avalonia 的相对名称解析受到影响。既有 GameView 引用已采用 GameViewAssembly 别名。Themes 引用改为 Aliases="ThemesAssembly",仅 EditorPreviewHost 用 extern alias 后,原来 13 个编译错误消失,独立输出目录构建成功。

做法:Editor 增加 GalNet.Avalonia.* 引用时沿用程序集别名,局部通过别名访问 API;不要为此批量重写无关 Editor 源码中的 Avalonia 名称。改动后构建 Editor,不能只验证新增程序集。
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Avalonia" Version="12.0.5" />
<PackageVersion Include="Avalonia.Headless" Version="12.0.5" />
<PackageVersion Include="Avalonia.Skia" Version="12.0.5" />
<PackageVersion Include="Avalonia.Android" Version="12.0.5" />
<PackageVersion Include="Avalonia.Browser" Version="12.0.5" />
Expand Down
1 change: 1 addition & 0 deletions GalNet.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<Project Path="src/Presentation/GalNet.Avalonia.Controls/GalNet.Avalonia.Controls.csproj" />
<Project Path="src/Presentation/GalNet.Avalonia.Rendering/GalNet.Avalonia.Rendering.csproj" />
<Project Path="src/Presentation/GalNet.Avalonia.GameView/GalNet.Avalonia.GameView.csproj" />
<Project Path="src/Presentation/GalNet.Avalonia.Themes/GalNet.Avalonia.Themes.csproj" />
</Folder>
<Folder Name="/src/Samples/">
<Project Path="src/Samples/GalNet.Sample.Avalonia/GalNet.Sample.Avalonia.csproj" />
Expand Down
10 changes: 9 additions & 1 deletion GameTestCase/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,17 @@ so the picker reappears. Use `--profile <directory>` for an isolated test profil
- Configurable `bgm`, `sfx`, and `vocal` tracks: the intro uses `心.mp3` to demonstrate in-progress BGM fade-in, `seffect.gain` apply/stop, `seffect.lowPass` / `seffect.highPass` apply/stop, and in-progress fade-out.

The `.rawgalgroup` files are the sole authoring source. Build them with `GalNet.Editor.Headless`;
the generated `.galgroup` files use the version 2 compiled format (`typeId` plus `arguments`). Every
the generated `.galgroup` files use the version 3 compiled format (`typeId`, `arguments`, and reading metadata). The raw authoring format remains version 2. Every
resource reference is its stable GUID; human-readable paths occur only in adjacent `.meta` files.

Entry IDs must be unique within each group, not across groups. Read progress uses
the group ID together with the compiled entry ID. The current `progress.json`
format is version 2; older progress is rejected rather than migrated. For an old
local profile, move `progress.json` aside as a backup before launching again, or
use a new `--profile` directory. This resets read history only; retain player
variables and saves separately. Rebuild content after changing the compiler or
authoring source; do not use `-SkipBuild` with stale output.

The fixture follows the editable project layout: `Graph/graph.json`, `Graph/groups/*.rawgalgroup`, and
`Assets/` with each resource's `.meta`. Gallery data is derived from the metadata rather than stored
as an editable root `gallery.json`. The classroom background is also a `cg` Gallery item (ID `100`),
Expand Down
23 changes: 23 additions & 0 deletions docs/adr/0003-startup-game-layout-themes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# 0003:启动期组合玩家布局主题与独立配色

状态:Accepted,2026-10-11。

## 背景

GalNet 的行为控件应可复用,开发者需要完整玩家界面的主题参考,同时能仅替换某一个页面。GamePage 的 scene/presenter/打字机/NVL 状态已有共享生命周期,复制游戏页面逻辑会破坏状态与输入接线。

## 决定

采用启动期固定的 IGameLayoutTheme:提供局部样式及普通页面注册。页面映射按默认→主题→宿主覆盖组合后固定,最终 View 使用当前游戏 Scope。布局和 Palette 独立,官方示例集中在可选 Themes 项目;Shared 与 GameView 不反向依赖该集合。

普通页面使用共享 ViewModel 配合可替换 View。GamePage 为稳定 TemplatedControl,拥有 Scene/Dialogue/Nvl/Choice 内容,主题以验证过的四个 ContentPresenter 安排布局。自定义 GamePage 类型须继承该稳定宿主,并与 Runtime 使用同一 Scoped 实例。

官方主题把 Fluent 限制在玩家视觉根内,公共色表映射为语义 Color/Brush;外层宿主资源拥有覆盖优先权。截图等独立资源路径显式引入主题。

## 备选与后果

只抽颜色无法改变结构;所有 Page 模板化会增加简单页面的扩展负担;每套主题复制游戏页面会复制生命周期。选定方案集中运行行为,并允许单页覆盖而无需新主题。

运行中切换主题需要独立处理打字机任务、NVL 历史、模板部件和媒体状态,本期不支持。DynamicResource 用于资源引用,不代表完整运行期切换保证。官方布局适合 Sample 的宽屏设计画布;宿主不同尺寸需自行验证。

来源:[feature](../../features/F-20261011-01-game-ui-themes/feature.md)、[设计](../design/game-ui-themes.md)、[当前契约](../spec/control.md)。
Loading
Loading