修复资源分页与图片生成事务
修复资源画布滚轮、拖拽取消、负坐标与快速编辑卡边界 修复生成占位、候选持久化、归档恢复与并发保存顺序 收紧 Host Port 合同并补齐回归测试 统一栏目合同并更新权威文档与项目记忆
This commit is contained in:
@@ -235,7 +235,7 @@ type ProjectResourceCanvasLayout = {
|
||||
revision: number;
|
||||
positions: Array<{
|
||||
resourceId: string;
|
||||
section: 'document' | 'version' | 'art' | 'audio';
|
||||
section: 'document' | 'art' | 'audio' | 'code' | 'version';
|
||||
x: number;
|
||||
y: number;
|
||||
manuallyPlaced: boolean;
|
||||
@@ -344,14 +344,14 @@ type UpdateProjectResourceCanvasLayoutResult =
|
||||
|
||||
### 5.3 资源类型与替换兼容性(P1)
|
||||
|
||||
实现状态(2026-08-22):当前资源投影与栏目页顺序收口为“文档 -> 美术资源 -> 音乐音效资源 -> 游戏代码 -> 项目版本”。文档接收受支持的 UTF-8 文档、代码资产中的文档类型和合法 Agent 文本回执;项目版本只接收显式 `ProjectVersionResourceSummary` read model,未知任务产物不得兜底为版本;美术接收图片、SVG、动画和视频类产物;音频接收 manifest 资产、上传登记资产和已完成任务 `artifacts` 明确声明的音频产物;游戏代码接收 Direct Codex / 任务产物登记的 HTML、CSS 和 JavaScript。无法识别的二进制任务产物和附件不进入资源画布。受控读取、中央聚焦、失败空态与媒体播放不改变 manifest 真相;编辑成功后只追加新的 asset 或版本子记录。
|
||||
实现状态(2026-08-23):当前资源投影与栏目页顺序收口为“设计文档 -> 美术资源 -> 音乐音效 -> 游戏代码 -> 项目版本”。设计文档接收受支持的 UTF-8 文档、代码资产中的文档类型和合法 Agent 文本回执;项目版本只接收显式 `ProjectVersionResourceSummary` read model,未知任务产物不得兜底为版本;美术资源接收图片、SVG、动画和视频类产物;音乐音效接收 manifest 资产、上传登记资产和已完成任务 `artifacts` 明确声明的音频产物;游戏代码接收 Direct Codex / 任务产物登记的 HTML、CSS 和 JavaScript。无法识别的二进制任务产物和附件不进入资源画布。受控读取、中央聚焦、失败空态与媒体播放不改变 manifest 真相;编辑成功后只追加新的 asset 或版本子记录。
|
||||
|
||||
资源身份固定使用 manifest asset ID、正式 version ID、Agent ID + run ID 或已导入资源稳定路径;显示标题、来源文案变化不得改变 `resourceId`,从而避免布局、依赖边、选择和聚焦状态因改名失效。
|
||||
|
||||
```ts
|
||||
type ProjectResourceDescriptor = {
|
||||
resourceId: string;
|
||||
category: 'document' | 'version' | 'art' | 'audio';
|
||||
category: 'document' | 'art' | 'audio' | 'code' | 'version';
|
||||
subtype: string;
|
||||
width?: number;
|
||||
height?: number;
|
||||
@@ -495,7 +495,7 @@ type ProjectAgentMudPointAttribution = {
|
||||
4. 两个窗口基于同一 revision 执行资源自动协调写入时最多一个成功;失败方收到 `conflict` 与最新完整布局,界面不静默覆盖成功方结果。
|
||||
5. 布局文件缺失的旧项目可以无迁移打开;损坏、未知 schema、身份冲突、超限和链接文件失败关闭,且原文件不被空布局覆盖。
|
||||
6. 布局读写不改变 manifest、游戏项目 mutation revision、Runtime verification、Agent 权限与预览状态。
|
||||
7. `1280×720` 最小横屏下完全空项目可通过分区展览查看全部栏目,非空项目可通过栏目大纲和滚轮访问全部非空栏目,不出现页面级横向或纵向溢出,右侧对话和底部 Agent 状态栏保持可见;`1280×800` 继续作为默认窗口基线。
|
||||
7. `1280×720` 最小横屏下完全空项目可通过分区展览查看全部栏目,非空项目可通过栏目大纲和滚轮访问全部栏目(包括空栏目),不出现页面级横向或纵向溢出,右侧对话和底部 Agent 状态栏保持可见;`1280×800` 继续作为默认窗口基线。
|
||||
|
||||
### 7.3 P1 资源依赖关系图验收
|
||||
|
||||
@@ -523,7 +523,7 @@ type ProjectAgentMudPointAttribution = {
|
||||
### 7.5 资源分区独立高度验收
|
||||
|
||||
1. 四个分区均可独立缩小、放大和恢复默认高度;任一分区变化时其它分区高度、资源卡尺寸和媒体比例不变。到达上下限时对应按钮同时具备正确的 disabled 视觉与辅助技术语义。
|
||||
2. 高度、倍率和分区内部滚动按 `projectId + dependency|type + document|version|art|audio` 隔离,外层资源画布位置按 `projectId + dependency|type` 隔离;切换模式、切换项目和打开 / 关闭资源详情后,分别恢复当前会话中的内外滚动位置,且键盘焦点不被无关重置。
|
||||
2. 历史分区高度、倍率和内部滚动状态按 `projectId + dependency|type + document|art|audio|code|version` 隔离,外层资源画布位置按 `projectId + dependency|type` 隔离;切换模式、切换项目和打开 / 关闭资源详情后,分别恢复当前会话中的内外滚动位置,且键盘焦点不被无关重置。
|
||||
3. 最小高度可操作标题控制并完整容纳至少一排卡片;最大高度不超过中央资源画布当前可用高度。窗口变小后超界尺寸被永久夹取到新上限,后续放大窗口不自动恢复旧超界值。
|
||||
4. 分区放大只在正常文档流中下推后续分区,不使用浮层、绝对定位、负 margin 或 z-index 覆盖。分区内容超出时内部可滚动,外层画布仍可滚动访问其它分区,`1280×800` 无页面级溢出。
|
||||
5. dependency 线在高度、分区内部滚动、外层滚动和窗口 resize 后仍与可见卡片端点对齐;单端卡片被分区 viewport 裁掉时只保留边界继续线,不保留悬空主路径,两端都被裁掉时隐藏,线段不穿过标题栏。滚动与 resize 在每个分区内使用单一 `requestAnimationFrame` 合帧,每个分区 plane 最多创建一个 `ResizeObserver` 并在卸载时清理全部监听。
|
||||
|
||||
@@ -14322,7 +14322,7 @@
|
||||
## 2026-08-20 AGC 图片精修候选与最终图事务
|
||||
|
||||
- 只有可栅格编辑的图片资源拥有持续精修草稿;同一 `sourceAssetId` 只恢复一个活动 refine 草稿。生成成功只把 PNG 写入草稿私有 `draft-media` 并追加候选图层,不自动修改 manifest,也不自动关闭画布。
|
||||
- 每次生成在提交远端任务前冻结并持久化 `sourceLayerId` 与有边界的 `placeholder`。任务侧栏和画布占位共同消费 generation record;成功候选复用冻结落点,失败只终结当前任务,重启恢复不得重新提交或重复扣费。
|
||||
- 每次生成在前端确认时先冻结 `sourceLayerId` 与有边界的 `placeholder`,但宿主创建私有 ledger 并接受请求前只保留内存任务,不得把 `generation-running` 写入公开草稿。宿主接受后,任务侧栏和画布占位共同消费公开 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` 约束;碰撞、世界范围和依赖连线共同消费同一实际矩形。
|
||||
@@ -14331,7 +14331,7 @@
|
||||
|
||||
- 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 状态,不进入业务持久化。
|
||||
- 生成失败任务的“删除”固定为归档私有 ledger 并移出 draft 公开投影;只允许明确 `failed`,结果未知和 `reconciliation-required` 必须继续留在恢复队列。归档顺序固定为私有 `archiving`、删除并回读公开 generation、私有 `archived`;恢复必须幂等收敛 `archiving`,也要清理历史 `archived` 账本残留的公开记录,且不得依赖生成服务凭据。任务侧栏折叠是会话 UI 状态,不进入业务持久化。
|
||||
|
||||
## 2026-08-21 Game Agent 图片生成恢复与正式图来源身份
|
||||
|
||||
@@ -14392,3 +14392,10 @@
|
||||
|
||||
- 素材生成确认、离开确认和旧服务身份确认沿用现有独立 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 完成确认保存。
|
||||
- `ImageCanvasAssetPort.importLocalImages` 与 `ImageCanvasGenerationPort.archiveFailedGeneration` 是必选 Host Port;不支持的宿主必须返回结构化 `unsupported-capability`,共享 UI 不以方法缺失推断能力。快速编辑卡使用实测尺寸在图层上下方自动翻转,并钳制到 viewport 四边。
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
## 目标
|
||||
|
||||
资源管理在空项目中展示现有按类型式分区展览。任一栏目出现资源后,“按依赖”和“按类型”共用同一套栏目分页画布:每个非空栏目拥有一个铺满资源管理区域的独立画布,栏目大纲悬浮在画布左侧中间,只显示图标和栏目名称,不占用画布布局宽度;顶部显示当前栏目标题,底部显示下一页标题。普通滚轮切换栏目,按住 Ctrl/Meta 的滚轮以指针为锚点缩放当前画布。两种模式唯一差异是“按依赖”在当前栏目内绘制引导线。资源详情是叠加在资源画板之上的非模态卡片;打开、切换和关闭详情都不得卸载背景工具栏、资源卡、依赖连线或重置 viewport、搜索和排序模式。
|
||||
资源管理在空项目中展示现有按类型式分区展览。任一栏目出现资源后,“按依赖”和“按类型”共用同一套栏目分页画布:固定五个栏目各自拥有一个铺满资源管理区域的独立画布,空栏目也保留可打开的空画布;栏目大纲悬浮在画布左侧中间,只显示图标和栏目名称,不占用画布布局宽度;顶部显示当前栏目标题,底部显示下一页标题。普通滚轮切换栏目,按住 Ctrl/Meta 的滚轮以指针为锚点缩放当前画布。两种模式唯一差异是“按依赖”在当前栏目内绘制引导线。资源详情是叠加在资源画板之上的非模态卡片;打开、切换和关闭详情都不得卸载背景工具栏、资源卡、依赖连线或重置 viewport、搜索和排序模式。
|
||||
|
||||
图片资源从“编辑资源”进入持续精修草稿后,点击任意图片直接在图片下方显示快速编辑卡。每次快速编辑创建新的候选图,不覆盖来源图层;生成任务立即在画布中创建占位并进入任务侧栏。用户显式选择候选图“设为最终图”后,保持原资产 ID 不变,以事务方式切换 manifest 指向的正式 PNG;精修草稿和其它候选图继续保留。
|
||||
|
||||
@@ -12,8 +12,8 @@
|
||||
|
||||
### 栏目分页投影与设计文档缺失边界
|
||||
|
||||
- 非空栏目页只由当前权威资源投影构造:manifest assets、已完成任务的 artifacts、用户已导入附件和持久化 Agent 文本回执。前端不得扫描项目目录自行发明资源。
|
||||
- 设计文档栏因此只有在上述来源存在 Markdown / TXT / JSON / YAML 等文档类条目,或存在 Agent 文本回执时才出现。若 manifest 没有文档资产、任务 artifacts 未完成且没有附件 / 回执,资源页不显示设计文档页;这是生成或登记缺失,不由 UI 补假数据。
|
||||
- 栏目内容只由当前权威资源投影构造:manifest assets、已完成任务的 artifacts、用户已导入附件和持久化 Agent 文本回执。前端不得扫描项目目录自行发明资源。
|
||||
- 固定栏目页始终为 `设计文档 -> 美术资源 -> 音乐音效 -> 游戏代码 -> 项目版本`。若某栏目没有对应投影,Dock 仍显示该栏目并允许打开空画布;空画布只表达“当前没有已登记资源”,不得由 UI 补假数据。
|
||||
- Direct Codex 的游戏生成链路以 `game/index.html`、`game/style.css`、`game/game.js` 作为代码资产登记;是否额外产出设计文档由当前任务和 Agent 决定,资源页只消费已登记结果。
|
||||
|
||||
### 持续精修草稿
|
||||
@@ -22,7 +22,7 @@
|
||||
- 草稿 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 恢复。
|
||||
- generation 占位与稳定 `generationId` 绑定。确认后 UI 立即显示内存占位;宿主创建私有 ledger 并接受请求后,才把落点、目标尺寸、阶段和结果图层身份投影到公开草稿。接受前失败或异常不得留下公开 `generation-running` 记录;成功候选必须使用同一落点,页面重启后从草稿和私有 ledger 恢复。
|
||||
|
||||
### 最终图事务
|
||||
|
||||
@@ -75,7 +75,7 @@
|
||||
### 失败生成任务归档与任务侧栏
|
||||
|
||||
- 用户界面的“删除失败任务”语义是归档,不物理销毁私有 generation ledger。只有平台明确失败的 `failed` 任务可归档;`reconciliation-required`、已受理、运行中和结果未知任务不得移出恢复队列。
|
||||
- 归档命令校验 project、draft、generation 与 expected draft revision,在私有 ledger 先写入 `archived/archivedAt`,再从 `draft.generations` 公开投影移除并推进一次 revision。重放必须幂等;中途退出后以 archived ledger 收敛,重载不得复活已归档任务。
|
||||
- 归档命令校验 project、draft、generation 与 expected draft revision,先把私有 ledger 写入可重放的 `archiving/archivedAt`,再从 `draft.generations` 移除公开投影并推进一次 revision;草稿删除成功并回读后才发布 `archived`。`archiving` 以及历史上已写 `archived` 但仍残留公开记录的状态都必须在恢复阶段幂等收敛,且不依赖图片生成服务凭证。
|
||||
- 失败占位和右上角任务项复用同一个归档动作,成功后两处同时消失,其它任务、图层和候选不受影响。
|
||||
- 任务侧栏折叠只属于当前会话 UI 状态,不写入 draft 或 manifest。视觉和交互复用现役美术画布:右上角独立“任务列表”图标按钮、20rem 白色模糊卡、总数徽标、`排队/生成中` 与 `已完成` 双 Tab、状态圆形图标、阶段进度和时间信息;折叠后只保留图标按钮,不显示摘要卡。用户显式新建 generation 时自动展开并切回活动 Tab,普通进度更新不得推翻用户已有折叠选择。Game Agent 的失败归档作为任务行扩展保留。
|
||||
|
||||
@@ -96,7 +96,7 @@
|
||||
- 快速编辑卡底部统一承载“删除”“设为最终图”“修改”。“删除”只删除当前绑定图层并进入画布 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,而不是只覆盖左侧画布区域。
|
||||
- 生成提交后立即创建内存画布占位和任务侧栏记录,宿主接受后再持久化公开 generation 投影;排队、运行、远端完成、下载、候选就绪、失败和对账是任务级状态,不得用全屏 `canvas.generating/canvas.failed` 遮挡或终结整张画布。候选持久化必须先写入并回读候选媒体、图层和公开记录,再发布私有 `candidate-ready` 终态;生成返回时前端只合并候选与 generation 权威事实,并在保存队列屏障后把生成期间的最新本地画布状态写回,禁止全量 hydrate 覆盖并发编辑。重新进入时,关键提交事务由 `host.recover()` 在 hydrate 前收敛;草稿 hydrate 完成后立即进入 `canvas.editing`,耗时的 `recoverImages()` 转为后台任务投影恢复,只更新任务、候选和 notice,不能继续锁住画布或因后台恢复失败切换为全屏失败。真正阻断草稿/提交/恢复的错误使用挂到 `document.body` 的全窗口 portal,必须覆盖整个 Tauri WebView,而不是只覆盖左侧画布区域。
|
||||
|
||||
## 美术预览几何
|
||||
|
||||
|
||||
Reference in New Issue
Block a user