完善Game Agent资源画板与图片精修链路
将资源依赖视图改为统一自由画板,并增加真实比例卡片和隐藏导航边界。 复用素材画布实现图片快速编辑、候选生成、导入、失败任务归档和正式图提交。 补齐原生事务恢复、服务身份确认、全屏失败遮罩及任务列表交互。 同步资源投影、布局、预览、前后端测试和技术文档。
This commit is contained in:
@@ -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、审批和保存事务均未改变。
|
||||
|
||||
Reference in New Issue
Block a user