完善 Game Agent 资源画布与图片精修生成事务 (#181)
Project CI / Repository checks (push) Successful in 4m5s
Project CI / Frontend tests (push) Successful in 4m48s
Project CI / Backend tests (push) Successful in 6m6s
Project CI / Native shell tests (push) Successful in 15m24s

## 背景

本合并请求整合 Game Agent 资源管理的栏目分页自由画布,以及图片持续精修、候选生成和本地恢复事务。

当前远端比较基线:

- base:`master` @ `b00a3f80576949567bf3320f0c6c92b4ed0a649d`
- head:`codex/game-agent-resource-section-pages` @ `5795d2f9b8797c342dcf2920ccefec2a3ff3f472`
- 相对 base:28 commits、33 files(`+3746 / -1333`)

## 主要改动

### 资源管理分页自由画布

- 固定展示“设计文档 → 美术资源 → 音乐音效 → 游戏代码 → 项目版本”五个栏目;空栏目仍可从悬浮 Dock 打开空画布。
- `按依赖` 与 `按类型` 共用栏目分页画布;每个“排序模式 + 栏目”组合独立保存 viewport。
- 普通滚轮使用有界意图队列切换栏目;Ctrl/Meta + 滚轮以指针为锚点缩放;空白拖拽可沿 x/y 无限平移,不以资源 extent 作为导航边界。
- 资源卡支持拖拽布局与一次 CAS 持久化;切页、排序切换和卸载会统一取消拖拽及 pointer capture。
- 资源详情为非模态独立卡片:打开、切换或关闭详情不卸载背景画布、资源卡或依赖连线,也不重置 viewport、搜索或排序状态。
- 顶部已移除“生成视频 / 生成音效 / 生成背景音乐 / 新增 UI 设计”的手动入口;保留播放、未完成编辑恢复、排序和画布复位,以及资源详情中的既有编辑动作。

### 图片持续精修与生成事务

- 图片资源可进入唯一活动精修草稿,支持原生批量导入、图片下方快速编辑卡、候选图层、任务侧栏和“设为最终图”。
- 生成成功只合并候选图层和 generation 权威事实,不以全量 hydrate 覆盖生成期间的本地编辑。
- 候选媒体、图层和公开 generation 记录写入并回读成功后,才推进私有 `candidate-ready`。
- 候选确认接入 Surface 保存 FIFO 与重新打开 hydrate;revision-sensitive 操作等待确认屏障。
- 失败归档和正式图提交采用可恢复中间态,避免中断后出现幽灵任务、重复 revision 或不一致的正式资产。

### 合同与文档

- `acknowledgeCandidateLayers`、`importLocalImages`、`archiveFailedGeneration` 为必选 Host Port;不支持的宿主返回结构化 `unsupported-capability`。
- 同步更新 TypeScript / Rust 合同、Tauri command 可达性、PRD、技术方案与项目共享记忆。
- 明确资源栏目无限画布合同:普通平移不夹取;资源 extent、图片测量、布局变更和 resize 不得重置用户 viewport;仅首次可测量布局与显式复位按真实卡片包围盒适配内容。

## 验证

CI run [#1290](https://git.genarrative.world/git/GenarrativeAI/Genarrative/actions/runs/1290) 针对当前 head 已完成且四项均成功:

- [x] Repository checks
- [x] Frontend tests(235 test files、3166 tests passed;App Surface 410 tests)
- [x] Backend tests
- [x] Native shell tests

另有本地定向检查:

- [x] `npm run agc:typecheck`
- [x] 资源画布、App Surface、Asset Canvas 与资源实时集成定向回归
- [x] Rust 候选确认、候选中断恢复、失败归档与恢复定向测试
- [x] `cargo fmt --check`
- [x] `npm run check:encoding`
- [x] `git diff --check`

## 评审 provenance

- review #247、#248、#252 都针对旧 head,当前在 Gitea 中均标记为 stale;不将其写作“已处理 #247”或当前 head 的评审结论。
- review #253 针对旧 `5456a1d…` head,提出 PRD 退役入口、共享 pitfalls 的 extent 旧合同和 PR 正文事实更新三项 P2;对应文档已在 `5795d2f…` head 修复并推送。
- 早期由 PR #176 引入的候选持久化、归档、Host Port 与快速编辑卡问题,当前已在 base/head 中具备对应修复,不作为本轮重复阻断项。
- 当前 head 仍需新的、针对最新提交的审批;历史 stale review 不能替代当前 head approval。

## 影响范围

- 修改 AI 游戏创作 App 的资源管理、图片精修、Tauri 本地持久化与恢复语义,以及共享合同和权威文档。
- 会调整本地资源布局、精修 draft 和私有 generation ledger 的状态机。
- 不修改 SpacetimeDB schema,不新增或变更 `/api/external/v1` 路由。

---------

Co-authored-by: kdletters <kdletters@qq.com>
Reviewed-on: http://192.168.35.82/git/GenarrativeAI/Genarrative/pulls/181
Co-authored-by: suzmii <suzmii@qq.com>
Co-committed-by: suzmii <suzmii@qq.com>
This commit was merged in pull request #181.
This commit is contained in:
2026-08-24 20:42:58 +08:00
committed by 段舒康
parent b00a3f8057
commit 532c6d51fd
33 changed files with 3770 additions and 1357 deletions
@@ -14409,6 +14409,13 @@
- 素材生成确认、离开确认和旧服务身份确认沿用现有独立 modal,不在当前面板下方追加内容。modal 打开后焦点必须进入对话框,并同时隔离工具栏、画布视口、缩放/小地图、状态区和并存操作层,使背景从键盘焦点顺序与 accessibility tree 中退出。
- `Tab` / `Shift+Tab` 必须在当前 modal 内双向循环;非异步 pending 状态允许 `Escape` 安全关闭。关闭后优先恢复到原触发器,自动弹出的 modal 则回退到可操作的工具栏入口,不能把焦点遗留在已卸载节点或被隔离背景中。
## 2026-08-23 Game Agent 资源分页与精修生成并发合同
- 资源管理固定展示 `设计文档 -> 美术资源 -> 音乐音效 -> 游戏代码 -> 项目版本` 五个栏目;空栏目仍可从 Dock 打开空画布。资源卡、Dock、复位和下一页按钮上的普通滚轮继续切页,Ctrl/Meta + 滚轮继续缩放;只有显式标记为原生滚动区域的控件隔离画布 wheel。资源详情为非模态浮层,打开时背景画布 listener 保持可用。
- 资源卡在 pointerdown 即建立 capture,栏目切换、滚轮切页、排序切换和卸载统一取消拖拽并清理预览;媒体播放等交互控件不得启动坐标写入。导航 extent 按全部卡片计算 `minX/minY/maxX/maxY`,负坐标必须进入 fit bounds 并把非零原点传给 viewport 约束。
- 图片生成成功时先持久化并回读候选媒体、候选图层和公开 generation,再发布私有 `candidate-ready`。前端不得用生成返回的整份草稿 hydrate 覆盖生成期间的本地编辑;只合并候选图层和 generation 权威事实,并排在已有保存队列之后用最新本地 layers、viewport、selection 和 background 保存草稿,再在同一 FIFO 内执行独立幂等候选确认。提交、导入、生成、归档和放弃草稿等 revision-sensitive 操作必须先等待该确认屏障;重新打开含 `candidate-ready` 的权威草稿时,在开放编辑前对当前图层 ID 执行幂等确认,以覆盖候选落盘后、前端确认前退出的窗口。
- `ImageCanvasProjectPort.acknowledgeCandidateLayers`、`ImageCanvasAssetPort.importLocalImages` 与 `ImageCanvasGenerationPort.archiveFailedGeneration` 都是必选 Host Port;不支持的宿主必须返回结构化 `unsupported-capability`,共享 UI 不以方法缺失推断能力。快速编辑卡使用实测尺寸在图层上下方自动翻转,并钳制到 viewport 四边。
## 2026-08-23 AGC 素材画布失败结算使用可重放中间态
- 失败结算的 `expectedDraftRevision` CAS、结算意图和公开投影必须在同一草稿锁边界内串行化;显式失败结算和生成流程内部错误都先持久化 `failure-settlement-pending` 或 `reconciliation-settlement-pending`,不能直接跨文件发布终态。
@@ -14458,6 +14465,14 @@
- 决策:将 External v1 `/api/external/v1/editor/images/background-removals` 通过 `agc_remove_background` 加入受控 `agc_tools`。工具只接受当前 manifest 的图片 `sourceLocalAssetId` 与结果名称;客户端负责正式 resourceId、画布/素材目录、稳定 operation/idempotency 身份、权限和错误脱敏,不向 Codex 暴露内部 BgFilter worker、凭据或任意 API。
- 约束:异步结果只投影有界队列状态,不允许模型自行构造源 URL 或在不确定提交后更换请求身份;External v1 负责 API Key、幂等接收与统一 operation 查询,客户端不得绕过该契约。
## 2026-08-24 资源详情动作、空态滚动与最终图多步恢复
- 角色资源详情的“生成动画”和“编辑资源”使用同一显式动作样式类,不再以 `first-child` 决定哪个业务按钮获得样式。
- 资源总览只有在至少存在一个资源、进入栏目分页画布时才挂载 paged 与 dependency 交互壳;完全空项目保留五分区展览和纵向滚动。
- viewport 按“排序模式 + 栏目”隔离保存;在“按依赖 / 按类型”之间来回切换,或离开资源管理进入运行视图后返回时,必须恢复对应组合的平移与缩放,不得通过自动点击复位或复用另一模式的 viewport 覆盖用户视角。只有该组合首次获得可测量容器尺寸或用户显式点击复位时才重新适配内容。
- 资源画布的导航范围与复位适配范围分离:导航范围继续保留最小世界尺寸和负坐标可达性;复位只按当前栏目资源卡真实包围盒计算,使用 `16px` 留白并允许在共享上限内放大,使至少一个轴贴合可用视口。
- 最终图恢复的 supersede 证明按相邻 transaction 的 after/before manifest 与 project revision 快照逐笔遍历,直到精确到达当前状态。三次及以上连续提交的早期事务不再因缺少“直连当前事务”而误报对账;任一中间账本、快照、正式文件或当前资产身份不完整时仍失败关闭。
## 2026-08-24 AGC 资源自由画板顶部移除手动新建入口
- 背景:无源视频、音效、背景音乐和 UI 设计已经有 Agent/编辑器权威生成链路,资源自由画板顶部继续并列手动新建按钮会形成第二套普通用户入口,并挤占画布级操作空间。
+10 -3
View File
@@ -4827,10 +4827,10 @@
- 画布内部 absolute overlay 只能覆盖宿主网格,嵌在左右分栏时不会遮住整个窗口。阻断性失败必须 portal 到 `document.body`,并用 fixed inset 覆盖整个 WebView;测试应验证 portal 的直接宿主和 fullscreen modifier。
## 自由画板只限制缩放比例不能防止资源整体离屏(2026-08-21)
## 自由画板 viewport 与资源 extent 分离(2026-08-24)
- 只依赖共享画布的全局 `MIN_SCALE/MAX_SCALE`,或只在“复位”时 fit,不会约束滚轮和平移拖拽;用户仍能把整个资源世界移到视口外。每一个 viewport 写入口都必须经过同一个边界函数,容器尺寸和资源 extent 改变后也要重新约束。
- 隐藏边界不能从搜索后的 `visibleResources` 派生,否则输入搜索词会突然收缩边界并跳动 viewport。应使用全项目布局 positions、真实卡片尺寸和分区世界 extent,搜索只控制渲染与连线可见性。
- 非空资源栏目使用无限画布:普通平移的 `x / y` 不按资源 extent 夹取,窗口 resize、媒体测量和资源 extent 变化也不得把用户 viewport 拉回内容边界;共享 `MIN_SCALE/MAX_SCALE` 只约束缩放比例。只有首次进入组合或用户显式复位时,才用真实卡片包围盒计算 fit。
- 持久化布局坐标仍受 `-1_000_000..=1_000_000` 合同限制,负坐标必须进入真实内容包围盒和显式 fit;这与 viewport 能否继续平移是两层独立语义。搜索只控制卡片与连线可见性,不删除、压缩或重排布局坐标,也不能让 viewport 随 `visibleResources` 收缩跳动。
## 素材画布旧提交不能只按当前状态猜测恢复(2026-08-22)
@@ -4839,6 +4839,13 @@
- 门禁:同一资源存在 prepared 或 reconciliation 事务时,新的“设为正式图”提交必须先被拒绝并引导安全恢复,不能继续制造另一笔可能覆盖旧结果的提交。`committed/rolled-back/superseded` 是可继续后续提交的终态。
- 关联坑:正式文件路径会追加 commitId,重新打开 refine 时不能把整个文件 stem 当作下一次素材名,否则多次精修后超过 80 字符并阻塞提交。路径只推导显示名,且必须循环剥离历史 `--<uuid>` 后缀;确定性名称/用途校验要发生在读取候选与 staging 前,并按输入错误处理而不是进入事务恢复。
## 素材画布候选确认不能复用普通草稿保存(2026-08-23)
- 现象:候选挂载后用 fire-and-forget `updateDraft` 确认时,候选本身不增加前端 document version;用户立即设为最终图、导入、生成、归档或放弃草稿会携带旧 revision 与隐藏确认写入竞态。重新打开含 `candidate-ready` 的草稿还会重复保存同一画布、无意义推进 revision,并制造多窗口冲突。
- 处理:候选确认使用独立幂等宿主操作,在现有草稿锁内只更新匹配当前项目、草稿、generation、仍存在权威图层且尚未确认的私有 ledger,记录当前草稿 revision,但不改写草稿或推进 revision。普通草稿 update 只保护未确认候选,不再顺带确认。
- 前端门禁:确认任务进入 autosave FIFO,连续候选 ID 合并并串行处理;所有消费 draft revision 的提交、导入、生成、归档和 discard 必须先等待确认屏障。确认失败不得继续 revision-sensitive 操作,重复 hydrate 可以重发但后端必须零写入。
- 验证:前端覆盖确认 pending 时立即设为最终图和删除,断言提交 / update 在确认完成前均未发生;重复打开不调用 `updateDraft` 且 revision 不变。Rust 覆盖首次确认、重复确认、未知或非候选 ID、普通 update 不确认,以及确认后显式删除。
## 隔离 Codex app-server 会误吃代理的 ChatGPT 额度头(2026-08-20)
- 现象:同一自定义 Responses endpoint 和 API Key 直接 HTTP 为 200,普通用户 HOME 下的 smoke 也完成,但隔离 `CODEX_HOME/HOME` 的 app-server 在真正发请求前返回 `usageLimitExceeded`,并投影 credits balance 0。