完善Game Agent资源画板与图片精修链路

将资源依赖视图改为统一自由画板,并增加真实比例卡片和隐藏导航边界。

复用素材画布实现图片快速编辑、候选生成、导入、失败任务归档和正式图提交。

补齐原生事务恢复、服务身份确认、全屏失败遮罩及任务列表交互。

同步资源投影、布局、预览、前后端测试和技术文档。
This commit is contained in:
2026-08-21 15:03:59 +08:00
parent aaac44a622
commit a86b7db951
29 changed files with 8060 additions and 5105 deletions
@@ -52,9 +52,9 @@
#### 3.3.1 资源管理三个串行阶段
1. 阶段一“资源卡本体化”:`dependency / type` 两种布局共用同一个资源卡组件和预览调度器。图片与安全 SVG 直接显示主体并保留透明棋盘底;视频显示首个可解码画面并可在卡内播放;音频使用清晰的音频视觉和独立播放键;文档显示安全纯文本摘要或稳定类型占位;项目版本不伪造媒体,只显示稳定版本视觉和必要的父子 / 绑定状态。中央详情以元数据、Rust 权威依赖关系和版本信息为主,不重复放大图片、SVG 或视频;文档正文和按用户意图读取的音频控件位于元数据之后。
2. 阶段二“分区独立缩放”(已完成)同时包含互不替代的分区可视高度和内容倍率。文档、项目版本、美术资源、音乐音效资源的标题栏分别保留高度减小 / 恢复 / 增大,并新增 `50%..200%` 内容缩放减小 / 百分比复位 / 增大;触摸板捏合、`Ctrl/Cmd + wheel` 与按钮使用同一倍率模型,普通双指滚动仍只滚动。高度与倍率都以 `projectId + layoutMode + category` 隔离并只保留在当前会话内存;倍率以分区 viewport 中的手势中心为锚点,逻辑 `x / y`、`180×128` 本体尺寸和媒体比例不变。最小高度固定容纳标题操作与至少一排卡片,最大高度以中央资源画布当前可用高度为上限。分区保持正常文档流,后续分区由外层资源画布滚动访问,单区内容在缩放后仍只在该区内部滚动。两类表现态都不修改 manifest、`game-creator-resource-layout.v1`、布局 CAS 或资源身份,不重建媒体控件、不抢走焦点,也不恢复卡片拖动。
3. 阶段三“依赖布局聚类”(已完成):仅在 `dependency` 模式中,按固定资源分区对当前 Rust 只读图返回的同类型精确引用边和同类型聚合任务流做弱连通分组;任务流以流节点连接其成员,绝不展开成资源笛卡尔积,但只作为布局邻近提示,不绘制虚线、不进入画布关系说明。`dependencyDepth` 是唯一横向层级权威;每个相关簇先按“最小深度 -> 最小稳定资源 ID”排序,再在同层进行固定两轮由左至右、由右至左的中位数扫描,平局回退稳定资源 ID;同 SCC/环成员保持连续,环后资源继续按 Rust 深度推进。孤立资源是稳定单例组,紧凑排在相关簇之后,簇之间保持固定留白。跨分类 read model 关系不改变 Rust 业务真相,但不得进入前端布局邻接、关系说明或 SVG;搜索只隐藏卡片和橙色精确引用边,不重算组或压缩坐标。历史 `manuallyPlaced=true` 坐标继续原样保留并参与自动坐标避让。聚类是可派生表现,不新增 manifest 字段、sidecar schema、SpacetimeDB 表或第二套依赖真相。
1. 阶段一“资源卡本体化”:`dependency / type` 两种布局共用同一个资源卡组件和预览调度器。图片与安全 SVG 直接显示主体并保留透明棋盘底;图片卡按真实纵横比在统一边界内计算展示矩形,视频、音频、文档和项目版本继续使用稳定固定卡片。资源详情是叠加在原画板上的非模态独立卡片,以元数据、Rust 权威依赖关系和版本信息为主;打开详情不得卸载“新增资源 / 按依赖 / 按类型”、资源卡、依赖连线或重置 viewport、搜索和排序模式。
2. 阶段二“分区独立缩放”(已完成)只适用于 `type` 模式,同时包含互不替代的分区可视高度和内容倍率。文档、项目版本、美术资源、音乐音效资源的标题栏分别保留高度减小 / 恢复 / 增大,并新增 `50%..200%` 内容缩放减小 / 百分比复位 / 增大;触摸板捏合、`Ctrl/Cmd + wheel` 与按钮使用同一倍率模型,普通双指滚动仍只滚动。高度与倍率都以 `projectId + type + category` 隔离并只保留在当前会话内存;倍率以分区 viewport 中的手势中心为锚点,逻辑 `x / y` 和媒体比例不变;非图片资源维持 `180×128`,美术图片按真实比例和统一边界计算本体矩形。最小高度固定容纳标题操作与至少一排卡片,最大高度以中央资源画布当前可用高度为上限。分区保持正常文档流,后续分区由外层资源画布滚动访问,单区内容在缩放后仍只在该区内部滚动。两类表现态都不修改 manifest、`game-creator-resource-layout.v1`、布局 CAS 或资源身份,不重建媒体控件、不抢走焦点,也不恢复卡片拖动。`dependency` 模式使用统一世界自由画板,不展示这些分区滚动与缩放操作。
3. 阶段三“依赖布局聚类”(已完成):仅在 `dependency` 模式中,按固定资源分类对当前 Rust 只读图返回的同类型精确引用边和同类型聚合任务流做弱连通分组;任务流以流节点连接其成员,绝不展开成资源笛卡尔积,但只作为布局邻近提示,不绘制虚线、不进入画布关系说明。`dependencyDepth` 是唯一横向层级权威;每个相关簇先按“最小深度 -> 最小稳定资源 ID”排序,再在同层进行固定两轮由左至右、由右至左的中位数扫描,平局回退稳定资源 ID;同 SCC/环成员保持连续,环后资源继续按 Rust 深度推进。孤立资源是稳定单例组,紧凑排在相关簇之后,簇之间保持固定留白。跨分类精确引用不参与前端布局邻接,但会在统一世界 SVG 中显示并进入同源的无障碍关系说明;搜索只隐藏卡片及任一端点被隐藏的橙色精确引用边,不重算组或压缩坐标。历史 `manuallyPlaced=true` 坐标继续原样保留并参与自动坐标避让。聚类是可派生表现,不新增 manifest 字段、sidecar schema、SpacetimeDB 表或第二套依赖真相。
#### 3.3.2 本体化资源卡交互与性能合同
@@ -110,7 +110,7 @@
- 客户端 UI 对齐采用“同视觉、同组件、保留四区布局”,不复制主站整页布局。资源总览、运行视图和 Supervisor 对话继续是 Game Agent 工作台独有语义。
- 客户端正式产品仍只按最小 `1280×720` 横屏合同交付,并保留 `1280×800` 默认窗口与既有基线验收;更窄浏览器样式只负责不崩溃和开发兼容,不改成移动端创作工作台。
- 普通用户界面不默认展示内部错误码或本机绝对路径,也不展示 External Editor Base URL 或 Developer API Key;官方服务地址由客户端构建固定,远端编辑自动使用当前陶泥儿登录态。仅独立 standalone game-chat release 或显式高级自定义模式在隔离配置页维护 External v1 URL/Key,确需诊断的信息进入受控详情或开发模式。
- 素材画布的“素材名称”是用户可编辑的正式输出名称;“资源用途”是 manifest subtype,不向普通用户开放自由文本。新增资源默认“普通游戏美术”,可从普通游戏美术、统一视觉规范、游戏界面原型、核心美术图集四项中选择;精修资源继承源用途且不可改。导出格式继续限定 PNG/JPEG/WebP。工具动作与保存设置分层展示,“保存到项目”在 `1280×800` 和窄容器中都必须完整可见。
- 创建模式素材画布的“素材名称”是用户可编辑的正式输出名称;“资源用途”是 manifest subtype,不向普通用户开放自由文本。新增资源默认“普通游戏美术”,可从普通游戏美术、统一视觉规范、游戏界面原型、核心美术图集四项中选择。图片精修继承源名称和用途,不显示创建模式保存设置;候选图片只从选中图片的“设为最终图”提交。精修顶栏只保留返回、导入、定位当前最终图、撤销和重做,删除进入图片上下文工具栏,通用 AI 生成只保留给创建模式。图片输出统一使用 PNG;创建模式工具动作与保存设置分层展示,“保存到项目”在 `1280×800` 和窄容器中都必须完整可见。
### 3.8 现有 Godot 项目
@@ -175,14 +175,14 @@ idle -> focused(document|art|audio|version) -> idle
```
- 文档:合法 Agent 文本回执直接使用对话投影内容;项目文件只允许读取当前 manifest 已登记资产或已完成任务产物中的 Markdown、文本、JSON、YAML、TOML,必须经过 `file.read` auto 权限、相对路径、项目边界、普通文件、符号链接 / 硬链接、读取漂移、2 MiB、UTF-8 与扩展名白名单校验。正文使用不执行 HTML、不加载远程图片、不产生可点击外链的安全 Markdown 渲染,并在中央画布内独立滚动;读取失败显示错误空态。
- 美术:PNG、JPEG、WEBP、GIF、SVG、AVIF、BMP、MP4、WebM、MOV 只在资源卡本体中按既有受控读取、文件签名与解码门禁展示;中央详情不重复加载或放大图片 / 视频本体。SVG 继续拒绝脚本、事件处理器、外部资源引用和实体声明。
- 美术:PNG、JPEG、WEBP、GIF、SVG、AVIF、BMP、MP4、WebM、MOV 只在资源卡本体中按既有受控读取、文件签名与解码门禁展示;中央详情不重复加载或放大图片 / 视频本体。SVG 继续拒绝脚本、事件处理器、外部资源引用和实体声明。
- 音频:只读取 manifest 已登记音频或已成功导入且登记到 manifest 的附件,按文件签名接受 MP3、WAV、OGG / Opus、M4A、AAC、FLAC;聚焦态展示实际格式、浏览器解码后的时长以及带播放进度和暂停能力的内置播放器。音频任务声明中的未登记路径继续不得读取或播放。
- 版本:只展示 manifest 中正式、不可变的迭代版本记录;版本卡展示项目修订、创建原因与父版本,聚焦态同时展示直接子版本和资源绑定。点击版本卡后高亮仍存在于当前资源投影中的引用资源;缺失历史资源只保留绑定身份,不生成幽灵资源卡。资源替换仍留给后续切片。
- mentor 最新决定:资源聚焦不提供工具栏,也不提供工具侧边栏。
- 音频:只读取 manifest 已登记音频或已成功导入且登记到 manifest 的附件,按文件签名接受 MP3、WAV、OGG / Opus、M4A、AAC、FLAC;聚焦态展示实际格式、浏览器解码后的时长以及带播放进度和暂停能力的内置播放器。音频任务声明中的未登记路径继续不得读取或播放。
- 版本:只展示 manifest 中正式、不可变的迭代版本记录;版本卡展示项目修订、创建原因与父版本,聚焦态同时展示直接子版本和资源绑定。点击版本卡后高亮仍存在于当前资源投影中的引用资源;缺失历史资源只保留绑定身份,不生成幽灵资源卡。资源编辑只允许追加继承源绑定并记录提示词的子版本,不允许原地替换或修改源版本。
- 资源聚焦不提供通用工具栏或工具侧边栏;图片聚焦态允许一个明确的“精修资源”业务动作进入素材创作无限画布,该动作不是在聚焦容器中内嵌编辑器或恢复通用工具栏。
- 点击资源后,中央主视窗从 `resource-overview.list` 切换为 `resource-overview.focused.document / art / audio / version`,左侧平台导航、右侧 Supervisor 对话和底部 Agent 状态栏保持原位;聚焦容器以路径、类型、来源任务、依赖层级、同类型上下游和版本字段为首屏主体,不使用页面级浮层或可拖动标题栏。文档正文与按意图加载的音频控制位于元数据之后。
- 音频:只读取 manifest 已登记音频或已成功导入且登记到 manifest 的附件,按文件签名接受 MP3、WAV、OGG / Opus、M4A、AAC、FLAC;聚焦态展示实际格式、浏览器解码后的时长以及带播放进度和暂停能力的内置播放器。音频任务声明中的未登记路径继续不得读取或播放。
- 版本:只展示 manifest 中正式、不可变的迭代版本记录;版本卡展示项目修订、创建原因与父版本,聚焦态同时展示直接子版本和资源绑定。点击版本卡后高亮仍存在于当前资源投影中的引用资源;缺失历史资源只保留绑定身份,不生成幽灵资源卡。资源编辑只允许追加继承源绑定并记录提示词的子版本,不允许原地替换或修改源版本。
- 资源聚焦不提供通用工具栏或工具侧边栏;图片聚焦态允许一个明确的“精修资源”业务动作进入素材创作无限画布,该动作不是在聚焦容器中内嵌编辑器或恢复通用工具栏。
- 点击资源后,中央主视窗从 `resource-overview.list` 切换为 `resource-overview.focused.document / art / audio / version`,左侧平台导航、右侧 Supervisor 对话和底部 Agent 状态栏保持原位;聚焦容器以路径、类型、来源任务、依赖层级、同类型上下游和版本字段为首屏主体,不使用页面级浮层或可拖动标题栏。文档正文与按意图加载的音频控制位于元数据之后。
- 焦点转换以稳定资源 ID 为准。只有从资源列表进入详情或从一个资源 ID 切换到另一个 ID 时聚焦详情 region;同一资源 ID 因 manifest 更新而重新投影时,不得抢走详情内音频 / 视频控件、文档链接或收起按钮的当前焦点。
- 显式收起或按 Escape 后恢复进入前的搜索条件、dependency / type 布局模式、资源画布滚动位置和选中资源,并优先把键盘焦点还给原触发资源卡;这些只属于当前前端会话,不写入布局 sidecar。若资源已经被后台删除,必须清理 stale focused / selected ID、关闭详情并把焦点落到“搜索项目资源”,不得落到 `body`。项目切换和进入运行视图必须取消旧项目的焦点恢复意图。
- 资源管理阶段四至阶段七交付上述受控读取、媒体展示、正式版本展示和引用高亮;`2026-08-05` 起图片编辑闭环生效,`2026-08-10` 起全类型非破坏性派生与版本子分支覆盖旧的只读限制。入口必须与草稿或 operation 恢复、正式事务提交、`referenceResourceIds` 血缘、新资源即时投影、两份布局和焦点竞态一次实现,不能只打开一个没有回写的面板。
@@ -328,8 +328,8 @@ type UpdateProjectResourceCanvasLayoutResult =
- 阶段五实现状态(2026-08-03):dependency 自动排列同时消费任务 DAG 与精确资源引用。Rust 把可信 producer 的任务深度作为资源深度下限,再对 `asset-reference` 图做迭代式 SCC 压缩与确定性层级传播;被引用资源位于引用资源之前,同一引用环共享稳定深度,环后资源继续递增,没有引用关系的资源保持默认不重叠位置。布局深度通过独立 `dependencyDepths` 返回,不能把 producer assignment 冒充全部资源的布局结果。
- `producerMappingTruncated=true` 只关闭依赖有界 Agent DB 审计的 `producerAssignments`、`taskFlows` 和 `cyclicTaskIds`。Rust 返回的 `dependencyDepths` 仍是 manifest / 精确引用 read model 的权威结果,前端必须过滤未知资源、负数、非整数和非安全整数后继续消费;不得因 producer 截断清空全部深度,也不得在前端重算替代深度。`referenceEdges`、connection index 中的 reference 关系、`cyclicResourceIds` 和 unresolved reference 继续保持可信。
- 图层只在 dependency 模式挂载;type 模式不得渲染 SVG、连线或 marker。切换 mode、切换项目或卸载工作台时必须销毁旧图层,并清理尺寸观察和窗口事件监听。
- 输入固定为当前资源投影的全部卡片身份 / 坐标与 Tauri Rust 返回的 `ProjectResourceGraph` 只读 DTO;Rust 负责资源过滤、去重、迭代式环检测、SCC 压缩后的确定性依赖深度、任务流聚合和一跳连接索引,前端只负责 DTO 防御归一化、逻辑 viewport 裁剪与原生 SVG path / marker。每个固定分区在自己的 `.game-resource-plane` 内持有一个 SVG,SVG 与卡片直接消费同一逻辑坐标、卡片尺寸、CSS scale 和 viewport scroll;端点主路径不得再通过 `getBoundingClientRect` 拼接全局屏幕坐标。SVG 位于资源卡底层并设置 `pointer-events: none`,不得引入 D3、React Flow 等图表库,也不得阻断卡片点击。
- `asset-reference` 表示精确资源引用;只有两端属于同一固定资源分类时才进入前端布局邻接、无障碍关系说明和 SVG,使用明亮橙色实线与实心箭头。跨分类引用仍可存在于 Rust read model,但资源管理画布不得绘线、不得以它形成布局簇或边界偏置。`GameCreationAppAssetManifestEntry.source.referenceResourceIds` 中的外部资源 ID 必须先唯一匹配另一项资产的 `source.resourceId`,再映射为当前资源卡 ID;缺失、重复或已删除的目标均不得渲染幽灵连线。
- 输入固定为当前资源投影的全部卡片身份 / 统一世界坐标与 Tauri Rust 返回的 `ProjectResourceGraph` 只读 DTO;Rust 负责资源过滤、去重、迭代式环检测、SCC 压缩后的确定性依赖深度、任务流聚合和一跳连接索引,前端只负责 DTO 防御归一化、世界 viewport 变换与原生 SVG path / marker。dependency 内容平面持有一个覆盖统一世界的 SVG,SVG 与卡片直接消费同一世界坐标和 viewport transform;端点主路径不得通过 `getBoundingClientRect` 拼接屏幕坐标。SVG 位于资源卡底层并设置 `pointer-events: none`,不得引入 D3、React Flow 等图表库,也不得阻断卡片点击。
- `asset-reference` 表示精确资源引用;当前可见端点之间的同分类与跨分类引用都进入统一世界 SVG 和无障碍关系说明,使用明亮橙色实线与实心箭头。只有同分类引用参与自动布局邻接,跨分类引用不形成布局簇或边界偏置。`GameCreationAppAssetManifestEntry.source.referenceResourceIds` 中的外部资源 ID 必须先唯一匹配另一项资产的 `source.resourceId`,再映射为当前资源卡 ID;缺失、重复或已删除的目标均不得渲染幽灵连线。
- `task-flow` 表示同一资源类型内的任务产物流转,但资源画布不再绘制灰色虚线或任务流 marker,也不把它加入 `aria-describedby` 关系说明。它只按 `sourceTaskId -> targetTaskId + section` 作为聚合超边参与 dependency 连通簇和中位数排序,禁止对上下游资源生成笛卡尔积邻接;详情仍可展示 Rust read model 已证明的任务信息。
- 画布资产 producer 只能来自 `agent.runtime.canvas.asset_generate` 的 `assetId -> agentId` 审计且 `agentId` 必须存在于当前 manifest;External Editor `source.taskId` 属于平台生成任务命名空间,禁止当作 manifest task ID。证据缺失、冲突或有界审计读取未覆盖时不生成对应 task flow,不猜测归属。
- 图模型必须对资源引用图和完整任务依赖图做迭代式环检测,不得用无界递归遍历;参与环的可见边保留渲染并标记 cyclic,环本身不能造成重复生成或死循环。
@@ -337,8 +337,8 @@ type UpdateProjectResourceCanvasLayoutResult =
- 搜索只允许为当前可见端点生成几何;任一精确引用端点隐藏时该橙线隐藏。task-flow 不产生显示几何,搜索也不因此重排布局。
- 同类型精确引用按稳定边 ID 和对端次序为同一卡片同侧的多条边分配独立端口;横向层级可用时优先左右连接,同列或横向间隙不足时才上下连接。同轴端点直接用直线,需转向时使用正交线段与最大 `10px` 的小圆角,不使用大范围贝塞尔控制柄。端口顺序不使用显示名称、随机数或浏览器枚举顺序,相同输入必须产生相同路径。资源点击只进入中央聚焦并保留当前选中卡片,不改变依赖卡片或连线的颜色、线宽与透明度;关系线始终直接展示,不提供点击后的上下游高亮或无关线弱化。
- 资源卡 Pointer Move 不改变基础 positions 或 SVG 几何。连线只随布局读取、资源自动协调、搜索、项目切换或 section origin 变化而更新。
- 每个已挂载 dependency 分区最多构造一个 `ResizeObserver`,四区总数最多四个;observer 只维护该分区的逻辑 viewport,不测量或重建卡片屏幕端点。dependency section 额外提供至少 `64px` 右侧视觉 gutter,确保最右侧自环和箭头可完整滚动显示,但不得修改卡片坐标或布局 sidecar。
- 分区高度、分区内部滚动和窗口 resize 在各分区内通过单一 `requestAnimationFrame` 调度器合并逻辑 viewport 更新,不得在每个 scroll event 中同步重算。浏览器以分区 viewport 原生裁剪同属该 plane 的 SVG 和卡片,因此线段不能穿过标题栏或泄漏到其它分区;一端可见时绘制入向 / 出向边界继续线,两端都离屏时不渲染。项目 / mode 切换或卸载时必须清理对应 observer、scroll 与 window resize 监听。
- 统一世界 dependency 平面最多构造一个 `ResizeObserver`;observer 只维护世界 viewport,不测量或重建卡片屏幕端点。世界 extent 必须为最右侧自环和箭头保留视觉 gutter,但不得修改卡片坐标或布局 sidecar。
- 拖拽、滚轮平移、Ctrl/Meta + 滚轮缩放和窗口 resize 共用单一世界 viewport 变换;原生 `{ passive: false }` wheel 监听在 dependency 模式阻止 WebView 默认滚动或缩放,type 模式普通滚轮继续交给原生分区滚动。项目 / mode 切换或卸载时必须清理 observer、wheel 与 window resize 监听。
- 阶段五不改变手动位置边界:已有 `manuallyPlaced=true` 坐标原样保留,资源引用新增或变化只允许重新派生 `manuallyPlaced=false` 的自动坐标;任务流继续按任务对与资源分区聚合,禁止为了布局分组生成资源笛卡尔积,且不进入 SVG。
### 5.3 资源类型与替换兼容性(P1)
@@ -498,9 +498,9 @@ type ProjectAgentMudPointAttribution = {
### 7.3 P1 资源依赖关系图验收
1. dependency 模式只显示同一资源类型分区内的精确引用,使用对画布背景至少 `3:1` 对比度的橙色实线箭头;聚合任务流只参与布局,不绘制灰色虚线或 marker。type 模式没有图层或连线。
1. dependency 模式在统一世界中显示当前可见端点之间的全部合法精确引用,包括跨分类引用,并使用对画布背景至少 `3:1` 对比度的橙色实线箭头;聚合任务流只参与同类型布局,不绘制灰色虚线或 marker。type 模式没有图层或连线。
2. 精确引用只接受唯一有效的外部资源 ID 映射,删除或不存在的资源不产生幽灵连线。
3. 多资源任务依赖按资源类型分区后只形成布局超边,以 `O(S+T)` 成员关系参与聚类,不产生 `S×T` 邻接、SVG 主线、端点分支或跨分区虚线。
3. 多资源任务依赖按资源类型拆分后只形成布局超边,以 `O(S+T)` 成员关系参与聚类,不产生 `S×T` 邻接、SVG 主线或端点分支;跨分类精确引用只显示,不参与布局聚类。
4. 资源引用环和无资源产物参与的任务环都可被有限遍历识别,界面不死循环。
5. 搜索触发端点过滤;资源点击不改变上下游卡片或任何连线的视觉状态,资源卡指针移动不更新线段,点击与中央聚焦行为不回归。
6. 切换布局模式或项目后旧 SVG、ResizeObserver 与窗口监听全部清理;图层从不写入 layout sidecar、manifest 或其它持久化。
@@ -508,7 +508,7 @@ type ProjectAgentMudPointAttribution = {
8. Rust 图读取延迟时,dependency sidecar 在图进入 `ready / failed` 前没有读取或写入;首次布局直接使用 Rust 返回的最终 producer 与 dependency depth。Agent DB 有界读取截断时 producer、task flow 与 `cyclicTaskIds` 失败关闭,精确 manifest 引用及 Rust 返回的合法 `dependencyDepths` 继续到达布局层。重新打开包含深度 `0 / 1 / 2` 自动坐标的旧布局时不得降成全 `0` 或持久化扁平布局;手动位置逐项不变,自动位置按最终拓扑协调且相同结果不增加 revision。
9. 依赖 SVG 作为装饰层不可聚焦并对辅助技术隐藏;画布通过关联的视觉隐藏文本逐条说明当前可见的橙色精确引用,搜索过滤或模式切换后文本与可见关系同步变化。task-flow 不进入该关系说明。
10. 精确引用使用橙色实线和可见箭头;dependency 自动卡片之间保留横向 `48px`、纵向 `40px` 走线区,相关簇的窄层按本簇最大层高度居中。不同横向层级且卡片间有空间时使用左右端口优先的横线,同列、同深度或横向空间不足时才使用上下端口纵向降级。同行 / 同列直接连线,需要转向时只使用正交线段与最大 `10px` 小圆角。同一卡片同侧的多条精确引用按稳定边 ID 与对端位置分配不同端口;自引用继续在卡片右侧外绕,箭头端点与卡片保留固定显示间隙。
11. 每个分区 SVG 必须位于拥有相应资源卡的 `.game-resource-plane` 内,以同一逻辑坐标随原生滚动和 CSS scale 同步移动;滚动 / 缩放不得通过异步 DOM 屏幕测量重新绑定端点。分区 viewport 原生裁剪本区 SVG,不使用四区 viewport 并集裁剪;一端离屏时只显示对应方向、带同语义箭头的边界继续线,不保留指向已裁剪卡片的悬空主路径,两端离屏时隐藏。搜索隐藏精确引用任一端点时整边隐藏。
11. 统一世界 SVG 必须位于拥有全部 dependency 资源卡的内容平面内,以同一世界坐标随 viewport 平移和缩放同步移动;滚动 / 缩放不得通过异步 DOM 屏幕测量重新绑定端点。世界 viewport 裁剪 SVG 与卡片,搜索隐藏精确引用任一端点时整边及对应无障碍说明同时隐藏。
### 7.4 P1 正式项目版本阶段六验收
@@ -541,8 +541,8 @@ type ProjectAgentMudPointAttribution = {
实现状态(2026-08-15):当前产品切片禁用“新增资源”,只从现有资源进入非破坏性编辑。图片进入中央 refine 画布,其他现役类型进入统一派生编辑壳;取消恢复、正式 manifest/revision 实时合并、依赖图重建、dependency/type 双布局协调和三阶段自动定位已经接通。command/event 任意顺序按项目、commit、event 与 revision 去重;低 revision、旧 graph/layout 和失效 focus generation 均不能倒灌。普通 Tauri 客户端登录陶泥儿后直接使用固定官方 origin 下的网站现役 `/api/editor/*`、`/api/assets/*` 与 `/api/runtime/external-generation/jobs/*`,不展示或要求填写 Base URL/API Key;短期 Access Token 只进入 WebView、GUI 与 Runner 内存。独立 standalone game-chat release/高级自定义模式保留隔离 AppData Developer API Key 与 `/api/external/v1/*`。重启恢复只继续原 operation,不以新请求、新幂等键或新 operationId 替代结果未知的旧任务。
1. 网站与 Tauri 实际 import 同一份 `@genarrative/image-canvas-core` 和 `@genarrative/image-canvas-react`,客户端没有复制的主站画布目录;viewport、selection、变换、renderer 与 history 算法位于共享层,宿主只保留事件接线与 adapter 副作用。
2. “新增资源”在当前产品切片中保持禁用;“编辑资源”只接受现有资源。图片 refine 保留原资产与原文件、创建新资产,并用规范 `referenceResourceIds` 登记直接血缘;其他类型同样只追加派生文件/asset 或子版本,不覆盖、删除或重排源记录。
3. 草稿 schema、revision、容量、项目身份、OS 锁、CAS、恢复副本和媒体引用符合权威专题;损坏、未知 schema、身份错配和超限均失败关闭。应用重启后必须按 `projectId + intent + sourceAssetId + active status` 从正式 sidecar 唯一发现原 refine 草稿并保留 `draftId`、生成新 `sessionId`;零条才允许创建,多条必须进入对账,不得依赖进程内 Map 或按时间猜测。
2. “新增资源”在当前产品切片中保持禁用;“编辑资源”只接受现有资源。图片 refine 为稳定 `sourceAssetId` 保存一个持续精修草稿和多个私有 `draft-media` 候选;只有用户显式“设为最终图”时才安装新的正式 PNG,并保持原 asset ID 不变、事务化更新其 manifest `localPath/mediaType`。候选图不进入 manifest 或资源总览。其他类型继续只追加派生文件/asset 或子版本。
3. 草稿 schema、revision、容量、项目身份、OS 锁、CAS、恢复副本和媒体引用符合权威专题;损坏、未知 schema、身份错配和超限均失败关闭。应用重启后必须按 `projectId + intent + sourceAssetId + 可恢复/可继续状态` 从正式 sidecar 唯一发现原 refine 草稿并保留 `draftId`、候选图和生成占位,同时生成新 `sessionId`;零条才允许创建,多条必须进入对账,不得依赖进程内 Map 或按时间猜测。
4. 正式提交携带 `expectedProjectId + expectedRevision + expectedDraftRevision + commitId + idempotencyKey`,按事务快照/journal/ledger、文件、manifest/revision、回读、ledger/draft、事件顺序完成;首个快照、全部快照、journal 写入、文件、manifest、revision、验证和 ledger 各崩溃阶段均有确定结果,未发布快照残留只在证明正式文件、manifest 与 revision 均未变化时清理。
5. 保存成功后不刷新、不重开项目即可进入 manifest 投影、依赖图、dependency/type 布局和允许时的选中定位;切项目、切中央状态、改选择或改搜索后的迟到结果不得抢焦点。
6. 搜索/筛选隐藏新资源时保留条件,明确提示“新资源已保存,当前筛选条件下不可见”,只通过显式动作清除条件并定位。
@@ -554,7 +554,7 @@ type ProjectAgentMudPointAttribution = {
12. `remote-failed` 已是远端明确终态,重启后不再 POST、不再轮询、不再扣费;`archived` 仅表示用户已将它移出活动恢复队列,不等于 `committed`。`result-unknown`、鉴权临时失败和 `reconciliation-required` 均不允许归档或重新生成。
13. 派生资产提交必须通过 durable asset transaction journal 串起最终文件、manifest 和 project revision。任一崩溃阶段恢复后只有一份派生文件、一条 manifest asset,revision 精确推进一次;manifest 已写而 revision 未写时只前向补 revision,无法证明的组合进入人工对账。journal 已证明目标 asset、媒体和 target revision 写入后,即使后续合法提交继续推进 manifest/revision,也应按目标 asset 精确身份与 `currentRevision >= targetRevision` 补齐 ledger/draft,不得要求整个 manifest 永远等于历史 after 快照。durable committed 后遗留的 staging 只有在 staging/正式媒体摘要一致,且 manifest 中按 asset ID 或路径唯一命中并与 journal asset 精确相等时才尽力删除;删除失败不降级已提交结果,身份或媒体漂移则保留 staging 并进入对账。
14. generation progress、草稿保存队列、生成/提交回包与延迟 `loadDraft` 必须共用单调 revision 门禁,低 revision 不得覆盖已落地的新草稿。Shift 指针与键盘选择必须与共享 core 一致;单选自身 Shift 不能清空选择,已选多图层普通指针拖动应保持并同步移动选择集。零位移不得产生 undo、documentVersion 或草稿保存。
15. 失败 UI 必须按 `generation / draft-save / asset-commit / recovery / cancellation` 五类 operation 显示可访问名称与安全动作。只有生成失败可以保留参数并“返回修改/重新确认”;草稿、CAS、提交、恢复和取消故障不得出现会发起新生成的按钮。任何非 `editing` 生命周期都必须同时禁用或 inert 背景画布/顶部工具栏,并由生成 handler 再次校验当前状态;modal 遮罩、视觉 disabled 或旧闭包都不能充当业务门禁。
15. 失败 UI 必须按 `generation / draft-save / asset-commit / recovery / cancellation` 五类 operation 显示可访问名称与安全动作。生成是任务级状态:排队、运行、失败和对账不得用全屏遮罩终结画布;失败占位保留原参数并允许重新修改。草稿、CAS、最终图提交、恢复和取消故障不得出现会发起新生成的按钮。真实写入和恢复阶段仍由 handler、revision 与锁负责门禁,视觉 disabled、旧闭包或遮罩不能充当业务门禁。
16. 派生子版本 journal 必须冻结 project revision before/after 身份和目标 after 记录。manifest 已有目标子版本但 journal 缺失时失败关闭;旧 journal 缺少 revision 身份且当前 revision 已推进、无法证明是同一事务写入时进入人工对账,不得把现状猜测为已提交。
17. 文本、SVG 与 Agent 回执编辑必须在调用 Provider 前持久化 request-issued;Provider 成功正文必须在解析、格式校验和 staging 之前原子写入私有 durable handoff,并绑定原 operation、请求指纹和内容摘要。issued 后缺少可信 handoff 只能对账,已有可信 handoff 则只消费原正文,两者都不得再次调用 Provider。普通模式首次 `401` 只允许当前账号 generation 的单飞 refresh,成功后以更高 generation CAS 安装 Token,并使用原正文、原幂等键重试一次;refresh 失败、换号或再次 `401` 时保留原 operation,停止网络。`403` 不触发 refresh。高级 External v1 模式修正 Developer Key 后仍只查询或恢复原 operation。
@@ -569,10 +569,10 @@ type ProjectAgentMudPointAttribution = {
## 8. 非目标
- 当前不开放“新增资源”产品入口;现有资源只允许非破坏性编辑并追加新文件、asset 或子版本,源记录不覆盖、删除或重排。
- 资源总览不实现资源卡手动拖动、通用聚焦工具栏/侧边栏、资源直接替换、运行版本切换、版本回滚、运行模块扩展、测试切片、运行态消费版本、数值参数或泥点归因。
- 当前不开放“新增资源”产品入口。图片精修候选保持私有且不污染资源总览;“设为最终图”只允许保持原 asset ID、以新正式文件和事务方式切换 manifest 指针,不原地覆写旧文件。其他资源编辑继续追加派生 asset 或子版本。
- 资源总览不实现资源卡手动拖动、通用聚焦工具栏/侧边栏、运行版本切换、版本回滚、运行模块扩展、测试切片、运行态消费版本、数值参数或泥点归因;资源详情仅为不影响背景画板的非模态卡片。
- 素材创作不实现高级蒙版/毛发级抠图、图集、角色动画、视频时间线或音频波形级编辑;图片画布提供现役编辑闭环,其他类型使用统一非破坏性派生面板。
- 资源聚焦、搜索、筛选、dependency/type mode 与分区滚动/倍率均为当前会话态;素材创作 viewport 和图层状态使用独立草稿 schema,不能混用资源总览 sidecar。
- 资源聚焦、搜索、筛选、dependency/type mode 与分区滚动/倍率均为当前会话态;打开详情不得改变这些状态。素材创作 viewport、候选图层、generation 占位和最后最终图提交投影使用独立草稿 schema,不能混用资源总览 sidecar。
- 不修改 SpacetimeDB schema。
- 不开放普通用户 Agent.md/Skill。
- 不自动确认 Agent 动作,不自动触发可能扣费的生成。
@@ -14271,3 +14271,31 @@
- 验证:`cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml canvas_generation_tests:: -- --test-threads=1`。
- 2026-08-19 追加:同一默认认证切换也覆盖资源编辑和 autonomous main-loop fixture。`resource_editor` 的 External Editor 视频提交/轮询/服务身份恢复测试同样使用 task-local 凭据;平台账号语义测试使用隔离测试会话。main-loop 的视觉任务配置测试不再通过旧 `editorApi` 文件伪造登录态。所有 loopback listener 在 accept 时设有 5 秒 deadline,并把 accepted stream 恢复为 blocking,避免 Windows `WouldBlock(10035)` 或请求未发出时无限等待。
- 定向验证:`project::asset_canvas::generation::tests::` 14/14、`project::resource_editor::tests::` 36/36、`agent::runtime_driver::main_loop_tests::` 48/48、`agent::runtime_protocol::autonomous_completion_contract_tests::` 107/107 通过。此前一次 Windows 全量 Native Rust 为 1811 passed、108 failed、15 ignored;失败集合仍包含 Provider/mock 调度与既有专业链断言。HEAD 基线独立复现 `tests::project::generate_platform_art_asset_downloads_and_registers_external_image` 的同一登录态缺失,故不能把全量结果伪报为本次 fixture 修复引入;本次新增认证/accept deadline 相关用例均已隔离通过,最后两个 autonomous completion fixture 的认证迁移已单独通过,完整套件未在该两行测试改动后重新执行。
## 2026-08-20 AGC 图片精修候选与最终图事务
- 只有可栅格编辑的图片资源拥有持续精修草稿;同一 `sourceAssetId` 只恢复一个活动 refine 草稿。生成成功只把 PNG 写入草稿私有 `draft-media` 并追加候选图层,不自动修改 manifest,也不自动关闭画布。
- 每次生成在提交远端任务前冻结并持久化 `sourceLayerId` 与有边界的 `placeholder`。任务侧栏和画布占位共同消费 generation record;成功候选复用冻结落点,失败只终结当前任务,重启恢复不得重新提交或重复扣费。
- “设为最终图”是唯一正式资源切换入口:refine 保持原 asset ID,把正式文件切换到 `assets/canvas/<name>--<commitId>.png`,并用候选图层 ID 与媒体 SHA-256 交叉验证 staging、ledger 和 transaction journal。manifest 继续是当前最终图权威。
- 最终图事务完成或恢复后,草稿必须回到 `editing`,清空 `pendingCommit`,保留其它候选与 generation records,并在 `lastCommit.sourceLayerId / mediaSha256` 记录当前最终候选;不能把持续精修草稿永久停在 `committed`。
- 资源详情保持非模态,不能卸载资源工具栏或背景画板。图片卡展示尺寸来自安全预览元数据,受 `220x180`、最小短边 `96` 和 `1:2..2:1` 约束;碰撞、世界范围和依赖连线共同消费同一实际矩形。
# 2026-08-21 Game Agent 精修来源、批量导入与失败任务归档
- refine 主来源不能仅凭已有 Editor Resource ID 直接复用;历史 Game Agent 私有 kind 必须基于本地正式图片重新登记为 External v1 canonical kind,并使用绑定服务身份、owner、Editor Project、源 SHA256 和 canonical kind 的本地私有缓存避免重复上传。不得扩大 External v1 快速编辑白名单或向 edit DTO 补发 `assetKind`。
- Tauri 图片导入采用原生多选与 Rust 批量事务:安全读取、媒体安装、图层绑定和一次 draft revision 推进必须作为一个可恢复单元,前端只 hydrate 权威 draft,不再依赖隐藏 input 加后续 autosave 完成正式绑定。
- 生成失败任务的“删除”固定为归档私有 ledger 并移出 draft 公开投影;只允许明确 `failed`,结果未知和 `reconciliation-required` 必须继续留在恢复队列。任务侧栏折叠是会话 UI 状态,不进入业务持久化。
## 2026-08-21 Game Agent 图片生成恢复与正式图来源身份
- 图片 generation 恢复是任务级后台工作:关键提交事务恢复和草稿 hydrate 完成后,画布立即进入 editing;`recoverImages` 继续恢复原 operation 并更新任务投影,但不得锁住整张画布。
- 精修候选设为最终图保持入口资产 ID。入口 manifest 已有有效 `source.resourceId` 时原样保留;只有未登记来源才使用 `local-asset:<assetId>`。提交与恢复回读使用同一解析规则。
- Game Agent 任务列表视觉复用现役美术画布的右上角独立按钮、白色面板、活动/完成双 Tab 和状态图标;失败归档是 Game Agent 的业务扩展。阻断性画布错误通过 body portal 覆盖整个 Tauri WebView。
## 2026-08-21 Game Agent 资源自由画板使用稳定隐藏边界
- 依赖画板的导航范围由当前项目全部资源的权威世界 extent 决定,并在屏幕坐标外扩 96px 安全留白;搜索过滤、详情卡和临时可见性只影响展示,不缩小导航边界。
- 滚轮、指针平移、缩放、复位和容器 resize 统一经过 viewport constraint。最小缩放为全量资源边界 fit scale;内容小于视口时居中,内容较大时边缘不能越过安全留白。该边界是会话级导航约束,不持久化为第二份布局真相。
@@ -4773,3 +4773,30 @@
- 原因:测试代码仅为动态生成 RSA fixture 引入 `openssl` dev-dependency,从而把原本使用纯 Rust 加密实现的 crate 额外绑定到本机原生 OpenSSL 工具链;Strawberry 附带的库面向 MinGW,不等于可用的 MSVC OpenSSL SDK。
- 处理:测试优先使用明确标记、只供测试的固定 PEM fixture,并继续通过项目自身的密钥解析与签名验证路径覆盖真实行为;不要仅为生成 fixture 引入系统原生库,也不要把 MinGW OpenSSL 路径写入 `OPENSSL_DIR` 冒充 MSVC 依赖。
- 验证:运行目标 crate 的 `cargo tree -i openssl-sys --target all` 确认依赖已退出,再执行包含测试目标的 `cargo test --tests`,不能只用不会编译 dev-dependency 的 `cargo check --lib` 代替。
## 图片编辑请求与资源卡几何不能依赖宽松 DTO 或可淘汰预览(2026-08-20)
- 现象一:Game Agent 快速编辑返回“平台明确拒绝”,远端没有创建任务。原因是 `/api/external/v1/editor/images/edits` 使用 `deny_unknown_fields`,客户端却把创建接口的 `assetKind` 以及本地来源字段一起发送;统一 400 文案又掩盖了真实参数错误。处理时必须按 create/edit 各自权威 DTO 组装请求,Game Agent kind 先映射为编辑器 canonical kind,并把安全业务错误分类,不能回显远端敏感 body。
- 现象二:图片预览已经读取真实宽高,但大量资源触发 LRU 淘汰后,卡片又退回 `180x128`,布局和连线随之跳动。原因是布局尺寸直接从可淘汰的 Blob/Object URL 预览缓存派生。处理时预览二进制仍按预算淘汰,但已验证的轻量 `pixelWidth/pixelHeight` 必须在当前项目、模式和资源 identity 作用域内独立保留;identity 或 scope 变化时再清理。
- 验证:分别覆盖严格 edit body、错误分类、生成占位恢复,以及超过预览缓存条目上限后首张图片仍保持真实比例、布局碰撞和依赖端点不退化。
# 2026-08-21 已有资源 ID 不等于可用于快速编辑的 canonical 来源
- manifest 中的 `source.resourceId` 可能指向历史按 `game-background` 等 Game Agent 私有 kind 登记的远端资源。只检查 ID 前缀并直接传给 `sourceReferenceId` 会在 External v1 入队前得到 `unsupported-source-kind`;本地 kind 映射只有在真正重新登记来源时才生效。
- 隐藏文件 input、逐张写 `draft-media`、前端追加图层再等待 autosave 的组合不是导入事务。失败可能表现为“按钮没反应”、留下孤立媒体或让图片出现在不可见的固定坐标。Tauri 正式链路应由原生多选和后端批量草稿更新闭环。
- 批量导入不能在安装首个媒体后继续执行带 `?` 的 ID、路径、层序或 revision 计算;这些步骤必须先完成。原子草稿写入返回错误后,回读失败属于提交结果未知,必须保留媒体并报对账错误,不能把回读错误压成“未提交”后删除可能已被草稿引用的文件。所有回滚删除失败也必须显式上报。
- 失败 generation 同时存在私有 ledger 和 draft 投影,只在 React state 中 `filter` 会在重启后复活。删除 UI 必须调用只允许明确失败任务的后端归档操作,结果未知任务不能删除。
## 运行中 generation 与 refine 来源身份不能按 create 链路处理(2026-08-21)
- 草稿 hydrate 后同步等待远端 generation recovery,会让生命周期长期停在 recovering,连平移和选图也被 inert。关键事务恢复与任务恢复必须拆开:前者先完成,后者后台推进且失败只进入任务/notice。
- refine 提交若无条件校验 `source.resourceId == local-asset:<assetId>`,会拒绝本来合法的 `editor-resource-*` 已登记来源,并可能把事务卡在 `revision-installed`。创建与未登记本地资产才补 local identity;已登记 refine 必须保留并按解析后的来源身份回读。
- 画布内部 absolute overlay 只能覆盖宿主网格,嵌在左右分栏时不会遮住整个窗口。阻断性失败必须 portal 到 `document.body`,并用 fixed inset 覆盖整个 WebView;测试应验证 portal 的直接宿主和 fullscreen modifier。
## 自由画板只限制缩放比例不能防止资源整体离屏(2026-08-21)
- 只依赖共享画布的全局 `MIN_SCALE/MAX_SCALE`,或只在“复位”时 fit,不会约束滚轮和平移拖拽;用户仍能把整个资源世界移到视口外。每一个 viewport 写入口都必须经过同一个边界函数,容器尺寸和资源 extent 改变后也要重新约束。
- 隐藏边界不能从搜索后的 `visibleResources` 派生,否则输入搜索词会突然收缩边界并跳动 viewport。应使用全项目布局 positions、真实卡片尺寸和分区世界 extent,搜索只控制渲染与连线可见性。
@@ -0,0 +1,101 @@
# Game Agent 资源自由画板与快速编辑
## 目标
资源管理的“按依赖”视图使用统一世界坐标的自由画板,支持拖拽平移、滚轮平移以及按住 Ctrl/Meta 的锚点缩放。资源详情是叠加在资源画板之上的非模态卡片;打开、切换和关闭详情都不得卸载背景工具栏、资源卡、依赖连线或重置 viewport、搜索和排序模式。
图片资源从“编辑资源”进入持续精修草稿后,点击任意图片直接在图片下方显示快速编辑卡。每次快速编辑创建新的候选图,不覆盖来源图层;生成任务立即在画布中创建占位并进入任务侧栏。用户显式选择候选图“设为最终图”后,保持原资产 ID 不变,以事务方式切换 manifest 指向的正式 PNG;精修草稿和其它候选图继续保留。
资源总览中的美术图片卡按真实纵横比计算展示矩形,并使用统一边界限制极端长图或宽图。非图片资源继续使用固定卡片尺寸。
## 权威数据与持久化
### 持续精修草稿
- 一个图片 `sourceAssetId` 唯一发现一个活动精修 `draftId`;重新进入时恢复原草稿,不依赖进程内状态或创建平行画布。
- 草稿 JSON 位于 `.agent/workbench/asset-canvas/drafts/<draftId>.json`,只保存图层、viewport、选择、generation 投影和提交记录,不内嵌图片 Base64。
- 入口图片图层使用 `project-asset` 引用稳定 `sourceAssetId`。AI 生成、导入和中间图片使用不可变 `draft-media`,二进制位于 `.agent/workbench/asset-canvas/media/<draftId>/<mediaId>.png`,引用同时冻结 SHA256、字节数和像素宽高。
- 候选媒体只属于精修草稿,不写入 manifest、不出现在资源总览,也不成为正式依赖。删除图层只删除草稿引用;物理媒体在确认不被草稿、generation ledger、staging、transaction journal 或恢复记录引用后延迟清理。
- generation 占位与稳定 `generationId` 绑定,持久化落点、目标尺寸、阶段和结果图层身份;成功候选必须使用同一落点,页面重启后从草稿和私有 ledger 恢复。
### 最终图事务
- “最终图”权威定义为 manifest 中 `sourceAssetId` 当前指向的正式文件,不使用前端布尔值作为业务真相。
- 前端只提交 `projectId + draftId + sourceAssetId + sourceLayerId + expectedProjectRevision + expectedDraftRevision + commitId + idempotencyKey`。后端重新读取权威草稿,验证目标图层属于当前 refine 草稿并引用完整 PNG `draft-media`。
- 每次提交将候选图安装到新的 `assets/canvas/<name>--<commitId>.png`,保持原 `assetId` 不变,只更新该 manifest 条目的 `mediaType/localPath`,随后推进 project revision。已登记 refine 入口必须保留原 `source.resourceId`(例如 `editor-resource-*`);只有 create 或缺少登记来源的本地资产才补为 `local-asset:<assetId>`。事务回读校验按同一来源解析规则验证,不能无条件要求 `local-asset:*`。
- 文件、manifest、revision、ledger、draft 和事件继续使用现有 transaction journal、before/after snapshot、回读验证、幂等返回、回滚与 reconciliation 合同;任何 revision 冲突都不得静默覆盖其它窗口的更新。
- 提交成功后草稿回到可编辑状态,并记录 `sourceLayerId + mediaSha256 + assetId + commitId + committedProjectRevision` 的最后提交投影。不得清空候选图或永久终结草稿;再次选择其它候选图可产生下一次最终图事务。
- 删除已提交候选图层只影响草稿;正式项目资源继续指向已验证的 PNG。当前最终图显示必须由 manifest/revision 与最后提交摘要共同投影。
### 精修来源规范化
- refine 主来源不得仅凭 manifest 中已有的 `source.resourceId` 判断其可直接用于 `sourceReferenceId`。历史 Game Agent 资源可能以 `game-background`、`art-spritesheet` 等私有 kind 登记,虽然 ID 形态是正式 Editor Resource,仍不满足 External v1 快速编辑的 canonical 类型合同。
- 客户端必须从本地正式图片重新取得受控字节,按现役映射登记 canonical 主来源,例如 `game-background -> scene`、`art-spritesheet -> icon-spritesheet`;`/api/external/v1/editor/images/edits` 仍只提交登记后返回的业务资源 ID,不扩大服务端白名单,也不在 edit DTO 中补发 `assetKind`。
- canonical 来源缓存属于本地私有恢复数据,不写入 manifest。缓存身份至少绑定平台服务身份、平台 owner、Editor Project ID、源图片 SHA256 与 canonical kind;任一身份或图片内容变化都必须重新登记。
- 缓存命中时复用已登记资源,避免每次快速编辑重复上传。平台明确返回来源不存在或类型不支持且请求尚未入队时,可以失效缓存并重建一次;不确定受理、已受理和对账状态不得换幂等键重提。
### 本地图片批量导入
- Tauri 正式链路使用原生多选文件对话框,不以隐藏 DOM `input[type=file]` 作为唯一入口。取消选择返回受控 `cancelled`,不修改草稿。
- 一批导入在 Rust 中完成安全读取、PNG/JPEG/WebP 签名与尺寸校验、总量校验、媒体安装、图层追加和一次 draft revision 推进。全部身份、路径、层序和新 revision 必须在首个文件安装前完成验证;任一文件失败时不得留下公开部分成功,回滚删除失败必须返回明确对账错误。草稿写入结果无法回读确认时保留本批媒体并进入显式对账错误,禁止冒险删除可能已被新草稿引用的文件。
- 导入图层保存真实原始宽高,显示矩形保持比例并受当前 viewport 可见边界限制;首张落在当前视口中心附近,多张确定性错位并避让现有图层。成功后返回权威 draft 与 `importedLayerIds`,前端 hydrate 后选中并定位。
- UI 必须区分正在选择、正在导入、已导入数量和安全失败原因。不得把文件读取异常或 host failure 留成未处理 Promise,也不得依赖稍后的前端 autosave 才把已写媒体绑定到图层。
### 失败生成任务归档与任务侧栏
- 用户界面的“删除失败任务”语义是归档,不物理销毁私有 generation ledger。只有平台明确失败的 `failed` 任务可归档;`reconciliation-required`、已受理、运行中和结果未知任务不得移出恢复队列。
- 归档命令校验 project、draft、generation 与 expected draft revision,在私有 ledger 先写入 `archived/archivedAt`,再从 `draft.generations` 公开投影移除并推进一次 revision。重放必须幂等;中途退出后以 archived ledger 收敛,重载不得复活已归档任务。
- 失败占位和右上角任务项复用同一个归档动作,成功后两处同时消失,其它任务、图层和候选不受影响。
- 任务侧栏折叠只属于当前会话 UI 状态,不写入 draft 或 manifest。视觉和交互复用现役美术画布:右上角独立“任务列表”图标按钮、20rem 白色模糊卡、总数徽标、`排队/生成中` 与 `已完成` 双 Tab、状态圆形图标、阶段进度和时间信息;折叠后只保留图标按钮,不显示摘要卡。用户显式新建 generation 时自动展开并切回活动 Tab,普通进度更新不得推翻用户已有折叠选择。Game Agent 的失败归档作为任务行扩展保留。
## 操作边界
- 依赖画布复用 `@genarrative/image-canvas-core` 的 viewport 计算,并复用现有资源卡片、布局和依赖连线模型。
- 依赖模式不使用资源分区滚动条、分区缩放或分区高度操作作为主要导航;分区仍是画布世界中的视觉分组。
- 依赖画布存在不可见导航边界:以当前项目全部资源的权威布局矩形、真实卡片尺寸、分区标题和世界 extent 的并集为内容范围,再在屏幕坐标四周外扩 `96px` 安全留白。搜索、详情卡和临时隐藏不得缩小边界,只有项目资源或权威布局真正变化时才重算。
- 滚轮平移、指针拖动画布、Ctrl/Meta 缩放、复位以及容器 resize 后都必须经过同一个 viewport 约束。缩小下限是“全部内容加安全留白刚好可见”的 fit scale;内容小于视口时居中,内容大于视口时四条边最多移动到安全留白位置,禁止把资源整体拖出屏幕。边界只约束会话 viewport,不写入 manifest 或资源布局 sidecar,也不显示可见边框。
- 统一世界画板显示当前可见资源之间的全部合法精确引用,包括跨视觉分组引用;装饰 SVG 与视觉隐藏的关系说明消费同一组可见边,搜索隐藏任一端点时两者同步移除。任务流仍只参与同类型布局聚类,不绘线也不进入关系说明。
- 资源详情卡包含元数据、媒体预览和“编辑资源”操作,但不使用全屏 backdrop、不声明 `aria-modal=true`、不把 `focusedResource` 作为背景工具栏渲染条件。桌面端允许继续操作背景画板;窄屏可以使用有边界的贴边卡,但背景组件必须保持挂载。
- 资源详情卡定位相对 `.game-workbench-stage`,桌面端在中间主视窗居中并受主视窗宽高边界约束;禁止使用相对整个窗口的右上角 `position: fixed` 定位。
- 图片选中后的浮动工具栏整体退役。普通点击图片直接打开唯一的图片下方快速编辑卡;拖动不误触发,点击另一张图片切换卡片,点击画布空白关闭卡片。
- 快速编辑卡底部统一承载“删除”“设为最终图”“修改”。“删除”只删除当前绑定图层并进入画布 history;“设为最终图”只对完整候选媒体可用;“修改”创建新 generation,不覆盖来源图层。
- 页面级返回、导入、撤销和重做不属于图片浮动工具条,本次不以复制另一套图片操作条替代已退役工具条。
- refine `/api/external/v1/editor/images/edits` 严格遵循 `EditorImageEditRequest`,不得携带 `assetKind`、`sourceImageSrc` 或 `sourceResourceId`。Game Agent 本地 kind 在登记编辑器来源前映射到现役 canonical kind;服务端安全业务错误必须结构化返回,不能把全部 400 折叠为同一个“平台明确拒绝”。
- 生成提交后立即创建画布占位和任务侧栏记录;排队、运行、远端完成、下载、候选就绪、失败和对账是任务级状态,不得用全屏 `canvas.generating/canvas.failed` 遮挡或终结整张画布。重新进入时,关键提交事务由 `host.recover()` 在 hydrate 前收敛;草稿 hydrate 完成后立即进入 `canvas.editing`,耗时的 `recoverImages()` 转为后台任务投影恢复,只更新任务、候选和 notice,不能继续锁住画布或因后台恢复失败切换为全屏失败。真正阻断草稿/提交/恢复的错误使用挂到 `document.body` 的全窗口 portal,必须覆盖整个 Tauri WebView,而不是只覆盖左侧画布区域。
## 美术预览几何
- 图片预览读取返回真实 `pixelWidth/pixelHeight`。PNG、JPEG、WebP 复用 Tauri 已完成的头部尺寸校验;其它受支持媒体使用安全元数据,读取失败回退固定卡片尺寸。
- 第一版图片展示边界为最大宽 `220px`、最大高 `180px`、最小短边 `96px`,展示框纵横比夹取在 `1:2..2:1`;图片始终 `object-fit: contain`,不得拉伸或裁剪。
- `resourceCanvasLayoutModel` 的碰撞、自动布局、section/world extent、适配视口和拖动几何必须消费每个资源自己的 `width/height`,不能只改 CSS。
- `ResourceDependencyOverlay` 的端点和绕障矩形必须消费同一资源矩形表,保证横图、竖图和 fallback 卡片的连线均对齐。
- `code/document/audio/version` 继续使用固定 `180x128`;尺寸只属于展示投影,不写入 manifest,也不成为第二份资源业务真相。
## 验收
1. 在依赖视图拖拽画布可以平移,滚轮可以平移,Ctrl/Meta + 滚轮以指针位置为锚点缩放,复位按钮可以适配全部依赖内容;极端拖拽和连续缩小均停在全部资源加 `96px` 安全留白形成的隐藏边界,搜索过滤不会改变该边界。
2. 同类型和跨视觉分组的合法精确引用均在统一世界中连线,并具有与当前可见连线一致的无障碍关系说明;搜索隐藏任一端点后连线和说明同时消失。
3. 点击任意资源后“新增资源 / 按依赖 / 按类型 / 复位”仍保持挂载和原状态;详情为非模态独立卡片,背景画板 viewport、搜索、排序、卡片和连线不卸载、不重置。
4. 图片资源点击“编辑资源”后恢复唯一活动 refine 草稿,入口图进入默认参考;点击任意图片直接在其下方显示快速编辑卡,图片上方不再出现浮动工具栏。
5. 快速编辑卡底部显示适用的删除、设为最终图和修改动作。拖动图片不误触发卡片;删除只影响草稿图层;候选设为最终图后保持原 asset ID、更新正式 PNG,草稿及其它候选仍可继续编辑。
6. 点击修改后立即创建避让现有内容的生成占位和任务侧栏记录;成功图在同一落点出现,失败只影响该占位和任务,重启后恢复原 generation/operation,不重新扣费提交。
7. refine 请求通过严格 DTO 合同且不携带非法 `assetKind`;参数、来源类型、余额、鉴权和对账错误显示不同的安全错误码。
8. 1:1、16:9、9:16、2:3、3:2 和极端横竖图片按真实比例显示并受边界限制;卡片不重叠,世界 extent、复位视口和依赖连线端点正确。非图片资源尺寸不变。
9. GIF、AVIF、BMP、SVG 等不满足栅格快速编辑能力的资源继续使用能力模型中的降级编辑路径,不伪装成可提交 PNG 候选。
10. 历史 `game-background` 等私有类型来源不得直接作为 refine 主来源;首次快速编辑完成 canonical 登记,后续同源请求复用缓存,并实际到达 `candidate-ready`。
11. 点击导入可以原生多选图片;成功批次只推进一次草稿 revision、立即出现在当前视口并被选中,取消和失败不改变草稿且不遗留部分导入。
12. 明确失败任务可从占位或任务侧栏归档,重启后不恢复;`reconciliation-required` 不显示删除能力。任务列表具有与现役美术画布一致的独立按钮、双 Tab、状态图标与时间/进度信息;折叠后只保留按钮且不丢任务数据,新建任务自动展开。
13. 画布带运行中 generation 重新进入时,在远端恢复完成前已经可选图、平移和继续编辑;后台恢复继续推进原 operation,不重复 POST、不重新扣费。阻断性提交/恢复失败的遮罩覆盖整个 Tauri 窗口。
14. 已登记 refine 资产的候选设为最终图后保持原 `assetId`、原 `source.resourceId` 与新正式 PNG 路径;`revision-installed` 中断后重新加载可前向收敛为 committed。
## 验证命令
```powershell
npm run ai-game-creator-shell:typecheck
node node_modules/vitest/vitest.mjs run apps/ai-game-creator-shell/tests/assetCanvasSurface.test.tsx apps/ai-game-creator-shell/tests/projectResourceLiveIntegration.test.tsx apps/ai-game-creator-shell/tests/resourceCanvasLayoutModel.test.ts apps/ai-game-creator-shell/tests/ResourceDependencyOverlay.test.ts apps/ai-game-creator-shell/tests/useProjectResourceCardPreviews.test.ts apps/ai-game-creator-shell/tests/resourceCanvasLayoutContract.test.ts
node node_modules/vitest/vitest.mjs run apps/ai-game-creator-shell/tests/appSurface.test.ts
cargo fmt --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml -- --check
cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml project::asset_canvas -- --test-threads=1
npm run check:encoding
git diff --check
```
@@ -965,9 +965,9 @@ confirmation-required
## 15. 2026-08-06 画布 UI 对齐阶段三至五收口
- 阶段三没有新建平行画布。现有 `AssetCanvasSurface` 直接使用 `CanvasChromeButton`、`CanvasToolbar`、`CanvasToolbarGroup` 和 `CanvasToolbarDivider`,覆盖返回、导入、撤销、重做、删除、AI 生成、保存、生成确认、失败重试、停止等待和缩放。共享组件只接收图标节点、名称、事件和原生状态,不读取 Tauri、账号、钱包或 HTTP。
- 阶段三没有新建平行画布。现有 `AssetCanvasSurface` 直接使用 `CanvasChromeButton`、`CanvasToolbar`、`CanvasToolbarGroup` 和 `CanvasToolbarDivider`。精修顶栏以紧凑尺寸覆盖返回、导入、定位当前最终图、撤销和重做;删除与“设为最终图”属于选中图片的上下文动作,通用 AI 生成与保存设置只保留给创建模式。生成确认、失败重试、停止等待和缩放继续复用共享 chrome。共享组件只接收图标节点、名称、事件和原生状态,不读取 Tauri、账号、钱包或 HTTP。
- Tauri 样式只导入 `packages/shared/src/theme.css`、`@genarrative/image-canvas-react/styles.css` 与客户端宿主样式,没有导入主站 `src/index.css`。宿主删除了覆盖全部按钮的重复视觉规则,只保留保存/确认主操作、字段、弹窗、进度/失败卡、状态栏和 viewport 定位;颜色、边框、surface、文字、focus 和主次动作均使用平台 token。
- 生成设置继续通过独立 dialog 展示。生成/失败卡和底部状态使用受限高度加内部滚动,保存设置在窄宽下换为两行并保持主按钮整行可见;工具组不拆字,工具栏需要时横向收纳。普通确认文案不显示 manifest、幂等实现、开发者 API 配置或本机绝对路径。
- 创建模式的通用生成设置继续通过独立 dialog 展示。图片精修从选中图片的快速编辑卡片进入生成确认,不在顶栏提供第二个通用生成入口;精修保存设置整排隐藏,候选图只通过上下文“设为最终图”提交。生成/失败卡和底部状态使用受限高度加内部滚动,创建模式保存设置在窄宽下换为两行并保持主按钮整行可见;工具组不拆字,工具栏需要时横向收纳。普通确认文案不显示 manifest、幂等实现、开发者 API 配置或本机绝对路径。
- 阶段四保留左导航、中央主视窗、右侧 Project Supervisor 和底部 Agent Dock 四区 DOM。中央壳使用主面板层级;Supervisor 降为协作面板,其消息和 Runtime 状态内部滚动、composer 固定在栏底;Dock 使用较轻 surface 和可收缩状态卡,不与中央主要操作争抢层级。
- 桌面 `>760px` 统一使用 `100dvh` 的两行工作台网格,第一行是内部滚动的主视窗/Supervisor,第二行是常驻 Dock。正式 `1280×800` 基线下页面级尺寸合同为 window/document/body `1280×800`;无登录会话的真实 Vite 入口按安全合同停在登录页,不绕过门禁,工作台内部由 AppSurface、共享 chrome 和 CSS 合同测试提供可重复证据。
- 阶段五定向测试覆盖共享 chrome 的 pressed、Tauri 实际消费的工具组/分隔符、按钮名称、expanded/disabled、生成确认、失败重试、保存/取消,以及生成 dialog、进度/失败卡、状态栏、Supervisor composer 和 Agent Dock 的非裁剪/可见性合同。生成请求、登录态、泥点计费、Host Port、草稿 CAS、manifest/revision、Runtime、审批和保存事务均未改变。