同步「编辑标签」入口文案的既有断言
- tests/appSurface/project-development.suite.ts:跟随 6bdc8bbd9 把资源卡工具条入口由
「分类与标签」改为「编辑标签」,同步音频资源工具条那条既有断言(否则 HEAD 的树是红的)
- 同一处注释同步说明:该面板只编辑 manifest `assets[].tags`,分类不再有手动入口
This commit is contained in:
@@ -3146,7 +3146,8 @@ export function registerProjectWorkbenchFoundationTests() {
|
||||
),
|
||||
).toBe(false);
|
||||
// 音频资源的选中工具条复用美术画布的音频分支(aria-label「素材工具栏」),
|
||||
// 并且只渲染宿主编排层真实接通的动作:「分类与标签」「重命名」已接面板,
|
||||
// 并且只渲染宿主编排层真实接通的动作:「编辑标签」(面板只编辑 manifest
|
||||
// `assets[].tags`,分类不再有手动入口)「重命名」已接面板,
|
||||
// 「下载按钮」复用资源面板同一条落盘链路,「改造」在宿主编排层仍是空回调,
|
||||
// 不能再渲染成点了没反应的按钮。
|
||||
const audioToolbar = screen.getByRole('toolbar', {
|
||||
@@ -3159,7 +3160,7 @@ export function registerProjectWorkbenchFoundationTests() {
|
||||
within(audioToolbar)
|
||||
.getAllByRole('button')
|
||||
.map((button) => button.getAttribute('aria-label')),
|
||||
).toEqual(['分类与标签', '重命名', '下载按钮']);
|
||||
).toEqual(['编辑标签', '重命名', '下载按钮']);
|
||||
|
||||
// 工具条的「下载按钮」必须真的走通落盘链路:原生保存对话框 + Rust 分块复制,
|
||||
// 而不是只渲染一个按钮。原生对话框由入口文件 mock 成"用户选了
|
||||
|
||||
@@ -0,0 +1,257 @@
|
||||
# AGC 资源工作台 V3 端到端验收
|
||||
|
||||
- 适用分支:`feat/agc-canvas-resource-workbench-v3`
|
||||
- 需求来源:飞书《GameAgent客户端画布与资源工作台 V3.0》
|
||||
- 权威文档:[`【AI游戏创作】项目开发工作台PRD-2026-07-20.md`](../prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md)、Tracking Issue #309、PR #316
|
||||
- 目的:给出一条**连贯的产品使用主线**,让验收人从零到跑通走一遍,就能把当前 PRD 要做的需求全部验收完;每一步都给出**可观察判据**,而不是"看起来对"。
|
||||
|
||||
## 1. 验收基线的先决条件
|
||||
|
||||
| 事实 | 依据 | 对验收的影响 |
|
||||
| --- | --- | --- |
|
||||
| 基线 commit 为 `ea9668e4a`(2026-09-11 15:37) | `git log -1`:`ea9668e4a`「资源总览「所有资源」卡补上资源预览」 | 验收记录表回填此 hash 作为冻结基线 |
|
||||
| 三个验收重点改动已入库,但**必须重新构建 / 重启客户端才会生效** | `2a157ea6f`「预览可见性门禁等 root 就绪再建 observer,并把登记表补挂齐」、`6bdc8bbd9`「AGC 资源分类面板改为「编辑素材标签」:只编辑标签、分类不再有手动入口」、`ea9668e4a`「资源总览「所有资源」卡补上资源预览」 | ① 可见卡停 `idle` 的修复;② 「编辑标签」只改 tags;③ 「所有资源」卡渲染真实预览。客户端若仍在跑旧构建(真机当前进程即属此列),这三条会得到**假失败** |
|
||||
| 工作区仍有 1 个非本用例范围的文件处于修改态 | `git status --short`:`apps/ai-game-creator-shell/tests/appSurface/project-development.suite.ts`(3 insertions / 2 deletions) | 只影响 AGC 全量测试结果,不影响手工验收;该文件相关用例失败时先与基线比对再定性 |
|
||||
| 真机项目落盘分类分布与旧构建指纹一致 | 真机 `.agent/manifest.json` 落盘 `category` = `unclassified 57 / ui-interaction 3 / scene 1` | 未用新构建复测时,卡片预览、标签面板、所有资源预览三条会得到**假失败** |
|
||||
|
||||
> 结论:验收开始前先固化"客户端构建版本 + api-server 是否重启 + 基线 commit"三件事,否则后续结论不可信。
|
||||
|
||||
## 2. 主线一句话概述
|
||||
|
||||
用一次「做游戏」从零建项目 → Agent 对话产出首轮素材并登记进 manifest → 在资源总览与栏目画布上把 6 类资产与项目版本走一遍管理闭环(预览 / 搜索 / 选择多选 / 快速编辑 / 生成入口 / 编辑标签 / 重命名 / 下载 / 删除 / 版本切换)→ 回对话侧 @ 引用素材 + AI 润色 + 发送前提醒 → 在运行视图跑通本地 loopback 预览并完成退出收尾。
|
||||
|
||||
每一步的判据都落在 **F12 控制台可读的 DOM 属性**或 **Tauri IPC 调用**上。
|
||||
|
||||
## 3. 分步验收表
|
||||
|
||||
> PRD 行号指向 `docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`(当前 594 行)。
|
||||
|
||||
### A 阶段 · 从零进项目
|
||||
|
||||
| 步骤 | 操作 | 期望结果 | 对应 PRD 条款 | 怎么判"过了"(可观察判据) | 已知例外 / 未做项 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| **S1** 建项 | 首页「做游戏」→ 输入需求 → 选择目录 → 确认为新项目 | 建项成功并直接进入项目工作台,不新建第二套页面或资产系统 | L7(四区)、L23(创作链路)、L108 | 进入后四区同时在 DOM:左侧平台导航、`.game-workbench-stage`(`aria-label="项目主视窗"`)、右侧 Supervisor 对话栏、底部 Agent Dock | 普通用户「新增资源」入口禁用(L108、L587) |
|
||||
| **S2** 落地视图 | 进项目后不动鼠标,看中央区 | 落 `resource-overview`;manifest 里 `preview.status=running` 的**陈旧记录**不得把你直接丢进运行界面 | L145–146、**L165**(`run` 自动进入只认内存 registry 活体确认) | `document.querySelector('.game-workbench-stage')?.dataset.resourceViewState` ∈ `resources.list` / `resources.selected.<category>` / `resources.ui-editor`;**该属性为 `undefined` 即说明已在 run 视图** | 陈旧记录对齐:命中"记录 running 但 registry 没跑"时调 `stop_local_game_preview` 并按真相投影 `{status:'stopped'}`(`sessionPreview.ts:87-101`) |
|
||||
| **S3** 项目页面板(可跳过) | 「项目组」页:搜索、行尾更多菜单、Escape 关闭 | 表格只显示权威数据列;Escape 关菜单并把焦点还给触发按钮;移除只改本机最近项目记录、不动磁盘 | L135–139 | 无匹配状态提供「清除搜索」;document/body 无页面级横纵向溢出 | **L139 要求 populated fixture 截图 + 视频演示搜索/清除/行尾菜单** —— 视频条款,见 7.1 |
|
||||
|
||||
### B 阶段 · 素材产生与登记
|
||||
|
||||
| 步骤 | 操作 | 期望结果 | 对应 PRD 条款 | 怎么判"过了" | 已知例外 / 未做项 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| **S4** 首轮生成 | 在右侧 Supervisor 对话里发起首轮素材生成 | 生成由**对话启动**(不用画布按钮);素材写入对应栏目并登记进 manifest;未生成的素材不显示为可编辑对象 | L25、L94、L500;飞书 §4 首轮生成 | 对话完成后 `.agent/manifest.json` 的 `assets[]` 长度增加;新卡 `data-resource-card-id` 为 `asset:<id>` 形态 | 「首轮进度投影」只在 Issue 交付范围的"应该"清单出现、**无编码级判据**;现有可见性由底部 Agent 状态栏承载 |
|
||||
| **S5** 栏目归属 | 看资源总览的栏目缩略卡与各栏目卡片 | 固定 7 栏:`UI 交互 → 角色与对象 → 场景与环境 → 音频 → 文档 → 待归类 → 项目版本`;分区口径是 manifest `assets[].category`,前端交互不能改分类 | L50、L56、L324、L358–360、L539 | `[...document.querySelectorAll('.game-resource-book-thumbnail[data-resource-book-category]')].map(e=>e.dataset.resourceBookCategory)`;再数每栏 `.game-resource-card` 数量 | **读时自愈**:落盘 `unclassified` 而 `kind` 能明确分类时按派生值显示(L360)。真机 57 条落盘 `unclassified` 在 UI 上应显示到正确栏目,**判定必须看 UI 计数,不能看 manifest 文件计数** |
|
||||
| **S6** 卡片预览台账 | 逐栏目看卡片是否显示真实主体 | 图片与安全 SVG 直接显示主体并保留透明棋盘底;视频、音频、文档、项目版本用稳定固定卡;失败显示类型占位、不挂破图、不降级为项目外 URL | L55、L61–62、**L67**、L347–349 | 见 5.1 的四条脚本:区分"没读 / 读失败 / 解码为空 / 不适用" | **可见卡停 `idle` 的真 bug 已修但未提交**(`useProjectResourceCardPreviews.ts`);未用新构建时会出现假失败 |
|
||||
|
||||
### C 阶段 · 资源管理闭环
|
||||
|
||||
| 步骤 | 操作 | 期望结果 | 对应 PRD 条款 | 怎么判"过了" | 已知例外 / 未做项 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| **S7** 栏目导航 | 点总览第 0 张「所有资源」卡 → 栏目缩略卡片进栏目 → 页内底部「下一页」 | 「所有资源」是汇总页(第 0 张);全部栏目(含空栏目)都可到达;左侧悬浮栏目大纲导航已删除;滚轮不切栏目 | L56、L512、L539–541 | `[data-resource-book-all-page="true"]`、`[data-resource-book-all-host="true"]`、`[data-resource-book-view="main"\|"child"]`、当前栏目 `.game-resource-book-scene-titlebar.is-active[data-resource-book-category]` | 「所有资源」卡渲染真实预览属**未提交改动**(`game-resource-book-preview-card`);旧构建只有占位摞 |
|
||||
| **S8** 搜索 | 点右下角 `搜索资源` 按钮或按 `Ctrl/Cmd+F` → 输入 → Esc | 浮层收起**不清搜索条件**;`type=search` 原生 Esc 清空被 `preventDefault` 挡掉;存在搜索时 Dock 按钮高亮 | L55、L322(搜索只隐藏卡片、不重排坐标) | `button[aria-label="搜索资源"]` 的 `aria-expanded`、`title="搜索资源(Ctrl/Cmd+F)"`、`.is-active`;输入框 `input[type=search][aria-label="搜索项目资源"]`,其 `id` 等于按钮的 `aria-controls` | 匹配字段 = **名称 / 路径 / 媒体类型 / 任务标题**,不含 `sourceLabel` |
|
||||
| **S9** 选择与多选 | 单击卡片 → `Shift`/`Ctrl`/`Meta` 再点 → 空白处拖拽框选 | 点卡 = 选中并在卡片旁浮出工具条;三种修饰键等效追加;框选可见选框 | L51、L61–62、L318–319 | `data-resource-card-id` + class 含 `is-selected` + `aria-pressed="true"`;浮出容器 `role="toolbar"`(图片 `aria-label="图片工具栏"`,音频 `"素材工具栏"`) | ⚠️ **PRD L61–62 要求的「打开详情」按钮在代码中不存在**(测试显式断言为 `null`),按 Issue #309 C3「取消资源详情面板与全屏编辑路由」判,不要判为缺失;真正入口是 `aria-label="选中资源:<栏目> <名>"` 按钮 |
|
||||
| **S10** 快速编辑(图片) | 选中 PNG/JPEG/WEBP 卡 → 工具条「快速编辑」→ 输入 → 提交 | 先 `normalize_local_project_raster_resource`,再 `derive_local_project_resource(editKind='image-reference')` → 服务端 `/api/editor/images/edits`;**产出一张新素材**,源素材与源文件不变,新资源带 `referenceResourceIds` 血缘 | L123、L193、L476–478、L557;#309 C3/C10 | 提交后 `assets[]` +1、卡片 +1 且 `data-resource-card-id` 为新 id;**源卡仍在且内容未变**;工具条真渲染的只有 `quick-edit` 与 `download` | ① 服务端白名单 17 项(`editor_project.rs:4120-4141`),`character-animation`/`video`/`audio`/`document`/`code` 等**仍会 400**;② 工具条 7 个动作(重绘 / 裁剪扩图 / 去背景 / 像素完美 / 切图集 / 提取 UI 素材 / 角色动画)按 opt-in **不渲染** |
|
||||
| **S11** 画布生成入口 | 画布级入口 → 选「视频 / 音效 / 背景音乐」→ 填名与提示词 → 提交 | 无源新建三类素材之一 → 新卡产出并定位 | 飞书 §4(首轮由对话启动)、L122 | 提交后 `assets[]` +1;面板是独立浮层(非在当前面板下方追加) | ⚠️ **Issue #309 写「画布生成入口未接」,但当前代码已接三类**(`resourceCanvasGenerationModel.ts` 仅 `video`/`sound-effect`/`background-music`);图片等类型会直接报「当前资源类型不支持无源生成」 |
|
||||
| **S12** 编辑标签 | 选中已登记素材 → 工具条「编辑标签」→ 增删标签 → 保存 | 面板**只编辑 `manifest.assets[].tags`**;`category` 读一次权威值后**原样回传**;顶部没有分类 chip 排 | L360(2026-09-11 收口)、#309 C2 | 面板标题「编辑素材标签」,底部只有「取消 / 保存标签」;保存后 `assets[n].tags` 变化而 `assets[n].category` **逐字不变** | 面板内的「删除资源」按钮是**唯一**删除入口,保留;「用户手动设置 category」这项能力已移除 |
|
||||
| **S13** 重命名 | 工具条「重命名」→ 新文件名 → 确认 | 磁盘文件名与 manifest `localPath` 更新;**asset id 不变**;写盘失败回滚文件 | L362(资源身份稳定)、L515 | 弹窗 `ariaLabel="重命名素材"`;改名后「选中资源:…」按钮的无障碍名同步变化;引用 chip 与 @ 候选列表显示名刷新 | ⚠️ **不改游戏源码里对旧 `assets/<name>` 的引用**,后果由用户自负 |
|
||||
| **S14** 下载 | 选中可下载卡 → 工具条「下载」 | 弹出**原生保存对话框**选目标路径 → 分块复制;取消即停止 | L507、飞书 §11.1.2 | 出现 `title="保存素材"` 的原生对话框;成功后状态提示含 `已保存 <路径>` | 语义是"另存为",不是静默直下;虚拟版本条目没有文件、不放行 |
|
||||
| **S15** 删除 | 工具条 / 标签面板「删除」→ 确认弹窗 | 三分支:① 无引用→直接删;② 被引用未勾选→只删素材、版本保留**悬空绑定**、界面不合成幽灵资源卡;③ 勾选→素材与该批版本在**同一次 manifest 写入**内一起删 | L413(版本只追加的唯一例外)、L532–533;#309 C5 | 弹窗 `ariaLabel="确认删除资源"`;被引用时出现 `被 N 个游戏版本使用` + 版本列表 + checkbox `把相关游戏版本一并删除`;无引用时该 body 整块不渲染 | **只摘登记、不删磁盘文件**;读引用命令精确名是 `read_local_project_asset_references` |
|
||||
|
||||
### D 阶段 · 版本与运行
|
||||
|
||||
| 步骤 | 操作 | 期望结果 | 对应 PRD 条款 | 怎么判"过了" | 已知例外 / 未做项 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| **S16** 版本切换 | 运行模块**右上角**版本选择器 → 切到另一版本 | 触发钮显示当前版本;切换后资源卡「当前使用」高亮与 **@ 面板「当前版本素材」同步更新**,并重载当前预览 | L165、L190、L324、L530;#309 C7 | `aria-label="当前版本:<label>"`、菜单 `role="listbox" aria-label="切换游戏版本"`、选项 `role="option" aria-selected`;`[data-used-by-current-version="true"]` 集合随切换变化 | **C7 只做记录层 + UI 层**;"历史版本独有资源集合被运行时按版本加载"需新增版本化资源解析机制,**本轮未实现**;版本卡卡面**不渲染**「项目修订 / 父版本」文字 |
|
||||
| **S17** 播放 | 顶部「播放」 | 直接切运行视图并启动本地预览;**不再弹 `game.run_local` 二次确认**;预览占满中央可用区、无 iframe 滚动条 | L16、L71–72、L122、L493 | `data-resource-view-state` 变为 `undefined`;`await window.__TAURI__.core.invoke('get_local_game_preview_status',{projectPath})` → `status==='running'` 且有 `url`(形如 `http://127.0.0.1:<随机端口>/`) | 播放按钮 `disabled={!runAvailable \|\| !onPlay \|\| uiEditorRoute!==null}`,**不可用时点不动**;不可用提示实现文案为「首个可运行原型尚未完成,运行视图暂不可用」,与 PRD L165 表述不同(见 7.4) |
|
||||
| **S18** 停止与退出收尾 | 聊天侧工程操作组点「停止预览」;或关闭客户端 | 预览停止、manifest `preview` 对齐 `stopped`(清 url/port);退出时也统一收尾 | L165 | 停止后 `get_local_game_preview_status` 非 running;`.agent/logs/preview.log` 追加 `stopped`;退出后重开同一项目应落回 `resource-overview` | 停止按钮**不在**运行视图工具栏,在工作台右侧工程操作组;退出失败会打 `preview.gui_exit.stop_failed` |
|
||||
|
||||
### E 阶段 · 对话侧
|
||||
|
||||
| 步骤 | 操作 | 期望结果 | 对应 PRD 条款 | 怎么判"过了" | 已知例外 / 未做项 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| **S19** @ 引用 | 输入区「@」→ 两个页签 + 筛选 + 搜索 + 多选 → 「添加到对话」 | 两页签**各自独立**的搜索与筛选状态,**不影响**资源画布筛选;已选素材不因筛选或搜索隐藏而被取消;chip 整体插入 | L187、L420–426、L453–461;#309 C4 | `role="tablist"`(`aria-label="素材范围"`)内两个 `role="tab"`:`当前版本素材` / `全部画布素材`;输入框 aria-label 分别为 `搜索当前版本素材` / `搜索全部画布素材`;chip = `span.resource-reference-chip[data-resource-reference-id]` | **chip 原子性有正式定义**:chip 是 Lexical `DecoratorNode`,纯文本表示固定占 **1 个字符** `U+FFFC` ⇒ 一次 Backspace 只能整体删掉一个 chip,不可能删半截;引用身份是稳定 `resourceId`,改名后按 id 刷新显示名 |
|
||||
| **S20** 润色与发送前提醒 | ① 点「AI 润色」→ 看回填与「恢复原文」;② 直接发送(≥40 字)→ 处理提醒弹窗三条动作与「不再提醒」 | 润色只优化文本、不发送不提交;第一次成功润色落下原文快照、可重复润色、失败保留原文;未润色直接发送时弹「发送前提醒」 | L120(气泡对比度)、飞书 §12.1/12.2;#309 C8 | 润色按钮 `aria-label="AI 润色"` + `aria-busy`;出现「恢复原文」按钮;提醒弹窗为 portal `role="dialog"`、标题「发送前提醒」、动作 `关闭` / `使用原文提交` / `AI 润色` + checkbox `不再提醒`;偏好存 `localStorage['agc.chat.prompt-polish-reminder.disabled']` | 触发门禁:trim 后 ≥40 字、不以 `/` 开头、本轮未润色未确认、未勾「不再提醒」。计费不自建,走 server-rs `/api/llm/chat/completions` 与 `/api/llm/responses`;「1 泥点」这个数字源码中未硬编码 |
|
||||
|
||||
### F 阶段 · 台账核对
|
||||
|
||||
| 步骤 | 操作 | 期望结果 | 对应 PRD 条款 | 怎么判"过了" | 已知例外 / 未做项 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| **S21** 全程台账 | 不刷新、不重开项目,回看资源画布与布局 | 新增资源即时投影;两套布局(dependency / type)各自坐标独立、历史 `manuallyPlaced=true` 保留;搜索与详情开关不重置 viewport | L47–51、L262–265、L320–323、L327–329、L500 | 两份 sidecar 都在 `.agent/workbench/resource-layouts/{dependency,type}.json`;拖动超过 5px 阈值才提交一次 CAS,成功后 `revision` 递增 | 双窗口同 revision 的 CAS 冲突(L509)需开两个客户端窗口才可验;`A→B→A` 复用 epoch 的取消语义只能靠定向测试 |
|
||||
|
||||
## 4. 前置条件清单
|
||||
|
||||
| 项 | 结论 | 依据 |
|
||||
| --- | --- | --- |
|
||||
| **是否要重启客户端 / 重新构建** | dev 态 `npm run agc` 走 `devUrl`(默认 3080,运行时被覆盖为实际端口),前端改动走 Vite HMR、**不需要 build**;**Rust 侧改动必须重启客户端**;未提交改动若不在当前进程里,也必须重启一次客户端 | `apps/ai-game-creator-shell/src-tauri/tauri.conf.json:6-11`、`scripts/start-tauri-dev.mjs:26-44` |
|
||||
| **是否要重新构建产物** | 只有 `agc:build` / `ai-game-creator-shell:build` 才产出 `../dist`;若验的是打包后的客户端,改前端必须重新 build 并重装 | `tauri.conf.json` 的 `frontendDist: "../dist"` |
|
||||
| **是否要重启 api-server** | **服务端改动需要重启**。本地 `.env` / `.env.local` / `.env.secrets.local` 修改后必须重启 `api-server`;已在 `npm run dev` 终端里可直接敲 `rs api-server`;provider / worker / fallback 配置改动要按角色重启对应进程 | `docs/【开发运维】本地开发验证与生产运维-2026-05-15.md:114/136/209/215/809` |
|
||||
| **启动命令** | 客户端:`npm run agc`(等价 `agc:dev` / `ai-game-creator-shell:dev`);只起前后端不起窗口:`npm run agc:serve`;后端:`npm run dev:api-server` | `package.json:20/155-159/169/22` |
|
||||
| **端口与健康检查** | api-server 默认 `127.0.0.1:8082`,健康检查 `GET /healthz`(先查 BgFilter worker `/readyz`);**端口会漂移,实际值以 `.app/dev-stack.json` 为准** | `scripts/dev.mjs:237`、`scripts/start-dev-stack.mjs:28-29/351-353` |
|
||||
| **登录与余额** | 需要登录;**不需要余额也能进工作台与资源画布**,消耗泥点的是付费生成与 Agent 请求 | `src/app/AuthenticatedClient.tsx:546/568-574`、`index.tsx:500` |
|
||||
| **测试项目** | 真机 `What do u wanna do kitten` 已有 61 assets / 5 versions、两份布局 sidecar 齐全,适合当**存量数据**验收对象(只读);要验删除与版本三分支时建议另建新项目 | 只读统计 |
|
||||
| **门禁命令** | `npm run ai-game-creator-shell:typecheck`、`npm run test -- apps/ai-game-creator-shell/tests`、`npm run check:encoding`、`git diff --check`;Rust 侧 `cargo test --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml -- --test-threads=1` | `package.json:123/164/193-194/63` |
|
||||
| **build-gate 代理假红** | 本地带系统代理跑 `npm run build` 必假红:`NODE_USE_ENV_PROXY=1` 加 `HTTP_PROXY/https_proxy` 会触发 `[UNDICI-EHPA] Warning: EnvHttpProxyAgent is experimental`,不被 `ExperimentalWarning` 忽略规则放行。跑前清空 `NODE_USE_ENV_PROXY/HTTP_PROXY/http_proxy/HTTPS_PROXY/https_proxy/ALL_PROXY` | `scripts/build-gate.mjs:33-37/57-67` |
|
||||
|
||||
## 5. 每步"失败怎么看"
|
||||
|
||||
### 5.1 资源卡预览:一眼区分「没读 / 读失败 / 解码为空 / 不适用」
|
||||
|
||||
先记住状态枚举:`data-preview-kind` ∈ `raster-image | media-image | video | audio | code | document | version | placeholder`;`data-preview-status` **只有 `idle | loading | loaded | failed` 四个值,不存在 `unsupported`**。"不适用"不是状态,表现为**永远 idle**。
|
||||
|
||||
```js
|
||||
// ① 全量分布(首选)
|
||||
[...document.querySelectorAll('.game-resource-card[data-preview-kind]')]
|
||||
.reduce((m, e) => { const k = `${e.dataset.previewKind}:${e.dataset.previewStatus}`; m[k] = (m[k] || 0) + 1; return m; }, {})
|
||||
|
||||
// ② 「没读」嫌疑人:可读类型却停在 idle
|
||||
// 注意 placeholder / version 永不入队 ⇒ idle 属正常;音频只在点播放后才读取
|
||||
[...document.querySelectorAll('.game-resource-card[data-preview-status="idle"]')]
|
||||
.filter(e => !['placeholder', 'version', 'audio'].includes(e.dataset.previewKind))
|
||||
.map(e => [e.dataset.resourceCardId, e.dataset.previewKind])
|
||||
|
||||
// ③ 「读失败」+ 原因原文
|
||||
[...document.querySelectorAll('.game-resource-card[data-preview-status="failed"]')]
|
||||
.map(e => [e.dataset.resourceCardId, e.dataset.previewError])
|
||||
|
||||
// ④ 「解码为空」:status=loaded 但没有任何主体节点
|
||||
[...document.querySelectorAll('.game-resource-card[data-preview-status="loaded"]')]
|
||||
.filter(e => !e.querySelector('.game-resource-card-visual img, .game-resource-card-visual video'))
|
||||
.map(e => [e.dataset.resourceCardId, e.dataset.previewKind])
|
||||
```
|
||||
|
||||
| 现象 | 说明 | 第一眼做什么 |
|
||||
| --- | --- | --- |
|
||||
| 可读类型 + `idle` | 该卡**从未发出预览请求**(可见性门禁没挂上,或卡片在被裁切的容器里) | 先确认跑的是包含修复的新构建(见第 1 节),再核对 observer 补挂是否生效 |
|
||||
| `failed` + `data-preview-error` | 读取或解码真失败,读 `data-preview-error` 原文 | `retryable=false` 表示永久失败(超尺寸、损坏、类型不支持、不安全 SVG);可见性预取遇 failed 会直接放弃,不会自愈 |
|
||||
| `loaded` 但无主体节点 | 原生返回的 dataUrl 为空、或解码不出画面 | 这类**不报错**,只能靠脚本 ④ 抓 |
|
||||
| `placeholder` + `idle` | **不适用**(游戏代码、无法识别的二进制产物等),正常 | 不要当 bug 报 |
|
||||
|
||||
**IPC 侧**:预览只走 4 条命令 `read_local_project_image_preview`、`read_local_project_media_preview`、`read_local_project_text_preview`、`cancel_local_project_resource_preview_scope`。媒体那条的 `category` **只接受 `art` / `audio`**,传栏目值会被原生拒绝,表现是卡片全空、没有缩略图。
|
||||
|
||||
```js
|
||||
// 想记录 IPC 时,在复现前先执行(每次调用现场读全局,可原地改写)
|
||||
const raw = window.__TAURI__.core.invoke;
|
||||
window.__agcCalls = [];
|
||||
window.__TAURI__.core.invoke = (cmd, args) => {
|
||||
if (/preview/.test(cmd)) window.__agcCalls.push([cmd, args]);
|
||||
return raw(cmd, args);
|
||||
};
|
||||
```
|
||||
|
||||
> ⚠️ **重要更正**:Tauri 客户端 F12 的 Network 面板**看不到 Rust → api-server 的 HTTP**(例如 `/api/editor/images/edits`)。F12 只能观察 WebView → Rust 的 IPC(`window.__TAURI__.core.invoke`,`withGlobalTauri: true`)。Rust 侧发出的 HTTP 请求只能看 api-server 终端日志或项目内 `.agent/logs/`。
|
||||
|
||||
### 5.2 按步骤的排障索引
|
||||
|
||||
| 步骤 | 出问题时第一眼看什么 |
|
||||
| --- | --- |
|
||||
| S2 | `.game-workbench-stage` 的 `data-resource-view-state`;缺失说明误进 run。再调 `get_local_game_preview_status` 看活体,不要看 `manifest.preview` |
|
||||
| S4 | 对话后 `.agent/manifest.json` 的 `assets[]` 是否增长;底部 Agent Dock 的状态文案 |
|
||||
| S5 | 各 `[data-resource-book-category]` 下的卡片数量;某栏全空而 manifest 中明显有该类资产时,通常不是分区轴坏了 |
|
||||
| S6 | 5.1 的四条脚本 |
|
||||
| S7 | `[data-resource-book-view]` 是否为预期值,`.game-resource-book-scene-titlebar.is-active` 的 category 是否为预期栏目 |
|
||||
| S8 | `button[aria-label="搜索资源"]` 的 `aria-expanded`;浮层是否真在 DOM。注意 jsdom 不校验可见性,只有真机截图或 `getBoundingClientRect` 才算数 |
|
||||
| S9 | `.is-selected` 数量与 `aria-pressed`;工具条容器 `role="toolbar"` 是否存在 |
|
||||
| S10 | 快速编辑失败文案;若为「当前素材类型不支持图片快速编辑」,说明源资源权威 `assetKind` 不在 17 项白名单里,这不是 V3 新缺陷 |
|
||||
| S11 | 生成入口在"没有 invoke 桥或项目未就绪"时**整体不渲染**,而不是渲染了但点了没反应 |
|
||||
| S12 | 保存后对比 `assets[n].tags` 与 `assets[n].category`,分类应逐字不变 |
|
||||
| S13 | 弹窗内 `role="alert"` 的错误文案;写盘失败时应看到文件已回滚原名 |
|
||||
| S14 | 是否有原生保存对话框;用户取消时是否停止后续批量保存 |
|
||||
| S15 | 弹窗是否出现 `被 N 个游戏版本使用`;不勾选时版本记录仍在(可读但悬空) |
|
||||
| S16 | `aria-label="当前版本:…"` 是否随切换更新;`[data-used-by-current-version="true"]` 集合是否变化 |
|
||||
| S17 | `get_local_game_preview_status` 返回的 `status/url/port`;`.agent/logs/preview.log` 的 `running` 行 |
|
||||
| S18 | 退出后重开项目是否落回资源总览;Rust 日志中的 `preview.gui_exit.stop_failed` |
|
||||
| S19 | chip 的 `data-resource-reference-id`;两页签 `role="tab"` 的 `aria-selected`;空态文案(`当前版本还没有绑定素材` / `当前项目还没有已登记素材` / `没有匹配的素材`) |
|
||||
| S20 | 弹窗标题「发送前提醒」;`localStorage['agc.chat.prompt-polish-reminder.disabled']` 的值;`润色中…` 或 `AI 润色失败,可重试` 状态行 |
|
||||
|
||||
**通用日志面**:WebView 的 `console.*` 会被镜像进 Rust `application.log`(标记 `__agcWebviewLogBridgeInstalled`),因此 F12 里打的日志事后也能在日志文件里复核。与本次验收最相关的两条是 `preview.gui_exit.stop_failed` 与 `[chat-markdown] render failed`。
|
||||
|
||||
## 6. 验收记录表
|
||||
|
||||
```text
|
||||
验收人:________ 日期:________ 基线 commit:____________
|
||||
未提交改动是否已构建:□ 是 □ 否 客户端构建方式:□ npm run agc(dev/HMR) □ 打包构建
|
||||
api-server 是否本次重启:□ 是 □ 否
|
||||
测试项目:□ 真机 What do u wanna do kitten(只读) □ 新建项目 ____________________
|
||||
```
|
||||
|
||||
| 步骤 | 结果 | 备注(现象 / 证据 / 截图名) |
|
||||
| --- | --- | --- |
|
||||
| S1 建项并进工作台 | ☐ 过 ☐ 不过 | |
|
||||
| S2 落 `resource-overview` | ☐ 过 ☐ 不过 | |
|
||||
| S3 项目页面板(可选) | ☐ 过 ☐ 不过 ☐ 跳过 | |
|
||||
| S4 对话生成 → manifest 登记 | ☐ 过 ☐ 不过 | assets:___ → ___ |
|
||||
| S5 7 栏目归属正确 | ☐ 过 ☐ 不过 | 各栏计数:______ |
|
||||
| S6 卡片预览台账(idle / failed / 空) | ☐ 过 ☐ 不过 | idle:__ failed:__ 空:__ |
|
||||
| S7 栏目导航(含「所有资源」/ 下一页 / 空栏目) | ☐ 过 ☐ 不过 | |
|
||||
| S8 搜索浮层 + 不清条件 | ☐ 过 ☐ 不过 | |
|
||||
| S9 选择 / 多选 / 框选 / 工具条 | ☐ 过 ☐ 不过 | 实际渲染的动作:______ |
|
||||
| S10 图片快速编辑(新素材 + 血缘) | ☐ 过 ☐ 不过 | 是否 400:☐ 否 ☐ 是(原因:____) |
|
||||
| S11 生成入口(视频 / 音效 / 背景音乐) | ☐ 过 ☐ 不过 ☐ 未验 | |
|
||||
| S12 编辑标签(只改 tags) | ☐ 过 ☐ 不过 | category 是否原样:☐ 是 ☐ 否 |
|
||||
| S13 重命名(id 不变) | ☐ 过 ☐ 不过 | |
|
||||
| S14 下载(另存为) | ☐ 过 ☐ 不过 | |
|
||||
| S15 删除三分支 | ☐ 无引用 ☐ 引用未勾选 ☐ 勾选连带 | |
|
||||
| S16 版本切换 + 当前使用高亮 + @ 同步 | ☐ 过 ☐ 不过 | |
|
||||
| S17 播放 → 运行视图 + 本地预览 | ☐ 过 ☐ 不过 | url:______________ |
|
||||
| S18 停止预览 / 退出收尾对齐 stopped | ☐ 过 ☐ 不过 | |
|
||||
| S19 @ 引用两页签 + 独立筛选 + chip 原子性 | ☐ 过 ☐ 不过 | |
|
||||
| S20 润色 + 发送前提醒 + 不再提醒 | ☐ 过 ☐ 不过 | |
|
||||
| S21 台账(即时同步 / 双布局 / 无溢出) | ☐ 过 ☐ 不过 | 1280×800 ☐ 1280×720 ☐ |
|
||||
| 门禁:typecheck / AGC tests / check:encoding / git diff --check | ☐ 过 ☐ 不过 | |
|
||||
|
||||
## 7. 风险与盲区
|
||||
|
||||
### 7.1 只靠视频 / 截图表达的条款
|
||||
|
||||
需求源头文档《GameAgent客户端画布与资源工作台 V3.0》(飞书 wiki 文档,文档 token 以 `N4Kiw…` 开头)的文本层共 **796 行文本、5 段视频、2 张图片**。以下条款**只能靠视频或截图验收**,文本层只有"标题式"描述,缺可判定细节:
|
||||
|
||||
| # | 位置 | 媒体 | 文本只到哪 | 缺什么(无法从文本验收的部分) |
|
||||
| --- | --- | --- | --- | --- |
|
||||
| 1 | §3.1 默认视图 | **截图**(图注「资源管理视图」) | "默认进入'全部资源'视图……功能画布分类:UI 与交互、角色与对象、场景与环境、音频、文档、待归类" | 总览的**版式**:缩略卡片排布、层叠张数、缩略图尺寸与间距、标题栏位置 |
|
||||
| 2 | §3.2 筛选 | **视频** `20260907-0309-29.2729736.mp4`("增加自定义标签") | "点击已有标签 / 输入新标签 / 保存后同步进全局标签库" | 面板**长什么样**、pill 排布、输入框提示文案、保存按钮位置 |
|
||||
| 3 | §3.2 筛选 | **视频** `20260907-0321-46.9223141.mp4`("查看素材标签") | "标签不默认显示在卡片上,需在'编辑标签'中查看" | 查看入口与展示形态 |
|
||||
| 4 | §3.2 筛选 | **视频** `20260907-0322-33.1365605.mp4`("按照标签筛选素材") | 文本给了筛选逻辑(条件即时生效 / 清空恢复 / 不影响归属) | 筛选面板的交互与视觉:多标签是 AND 还是 OR 的**界面表达**、标签汇总列表样式 |
|
||||
| 5 | §3.2 筛选 | **视频** `20260907-0323-34.3948031.mp4`("删除标签") | "删除标签时从所有素材和筛选条件中移除;'管理全部标签'里可删" | 「管理全部标签」这个入口 / 面板本身**只存在于视频**,AGC 侧未找到对应实现 |
|
||||
| 6 | §10.1 / §10.2 @ 引用 | **视频** `20260906-0812-23.9790394.mp4` + **截图**(提示框"请把@开始按钮并保持原位置",截图注明"参考 loveart") | "资源卡工具栏提供 @引用 工具";"所有@后产生的标签都在对话框当中,用户可以在标签前后编辑上下文" | chip 的**外观**(是否带图标 / 缩略图 / 颜色)、插入后光标落点、与 Lovart 的对照样式 |
|
||||
| 7 | PRD L139(项目管理页) | 文档**明确要求**提交截图 + 视频 | "截图必须使用含 Web、Godot 和无效状态的 populated fixture;视频必须演示搜索、清除、行尾菜单及可观察的项目操作结果" | 这条本身就是"必须交视频"的要求,不是可由文本判定的功能条款 |
|
||||
|
||||
**补齐方式**:① 由产品口述 2–3 句关键视觉约束后改写为判据;② 用 `lark-cli docs +media-download` 取出这 5 段视频与 2 张图并抽取静帧后逐帧写判据;③ 降级为"人工目视,不计入自动化判据",记录表里只打勾。
|
||||
|
||||
### 7.2 本地环境无法验证 / 需要额外条件的项
|
||||
|
||||
| 项 | 为什么本地不可验 | 建议 |
|
||||
| --- | --- | --- |
|
||||
| 双窗口布局 CAS 冲突(L509) | 需同一项目同时开两个客户端窗口、基于同一 revision 同时写入 | 标为"需专项构造",或只跑 `resourceCanvasLayoutContract` 定向测试 |
|
||||
| 4096 资源链式 fixture 性能(L522) | 需构造大规模 fixture | 交给定向测试,不进人工主线 |
|
||||
| `1280×720` / `1280×800` 视觉验收(L512、L577) | 必须真实窗口 + 截图或测量 `document/body` 的 `client` 与 `scroll` | 每条视觉结论附截图;jsdom 不做布局,vitest 中的可见性断言是假守卫 |
|
||||
| Windows 平台性断言 | 非 Linux 平台本地预览用**随机临时端口**;端口池耗尽回退、`GENARRATIVE_DEV_PORT_RANGE`、命令沙箱模块**只在 Linux 生效**;文件锁 Unix 用 `flock`、Windows 不共享句柄 | 涉及这些的判据不能反推平台行为。反之"delete-pending 等行为级 Windows 用例 CI 没有 runner,只在 Windows 本地跑"——本地反而能验到 CI 验不到的 |
|
||||
| `build-gate` 代理假红 | 见第 4 节 | 跑前清代理变量,或用 `build:raw` |
|
||||
| worktree 专属假红 | worktree 里 `check-repository-ci.sh` 第一步即"比较基线不可用";jsdom 拿不到 fit key | 改用 Windows git 逐条等价执行;这两类红不当缺陷 |
|
||||
| 既有失败用例 | 技术方案记录过 AGC 前端 5 条既有失败(C3 交互改造后待更新用例);Rust 并行跑 `project::` 约 10 条环境依赖失败,仓库口径是 `--test-threads=1` | 预置"已知红名单",避免重复排查 |
|
||||
| F12 看不到 Rust → api-server 的 HTTP | 见 5.1 更正 | 用 api-server 日志或 `.agent/logs` |
|
||||
|
||||
### 7.3 已知未做 / 已取消(不要报成缺陷)
|
||||
|
||||
1. **C6 替换关系与替换队列整条取消**(#309)。PRD L387 的 `ProjectVersionResourceReplacement` 兼容性三项属该取消范围。
|
||||
2. **画布生成入口只覆盖 3 类**(视频 / 音效 / 背景音乐);图片等类型会直接报"当前资源类型不支持无源生成",这是设计而非缺陷。
|
||||
3. **工具条 7 个动作按 opt-in 不渲染**:重绘 / 裁剪扩图 / 去背景 / 像素完美 / 切图集 / 提取 UI 素材 / 角色动画;只有 `快速编辑` 与 `下载` 真渲染(宿主另附加 `UI 编辑器` / `编辑标签` / `重命名`)。
|
||||
4. **C7 只做记录层 + UI 层**:切换版本即重载当前预览;版本化资源解析机制未实现。
|
||||
5. **「首轮进度投影」无编码级判据**,可见性由底部 Agent 状态栏承载。
|
||||
6. **浮出工具条的视觉位置未做真机核验**(jsdom 不执行 Web Animations,只覆盖结构与 class)。
|
||||
7. **重命名不改游戏源码中的旧 `assets/<name>` 引用**。
|
||||
8. **C9 参考图弹窗在 AGC 侧入口未确认**:全仓检索 `添加参考图` 只命中共享 `src/components/image-editor/*`,AGC 的 `ResourceCanvasGenerationPanelView` 未引用它,可能处于"组件已实现、AGC 入口未接"状态。建议真机点一次生成 / 编辑面板确认;若无入口,C9 不计入本轮主线。
|
||||
9. **运行视图存在「点选素材」按钮**,但 #309 的"不做"清单包含运行画面点选,口径冲突待定。
|
||||
|
||||
### 7.4 文档与代码的偏差(需明确按哪个判)
|
||||
|
||||
| 偏差 | 文档表述 | 代码实际 | 建议 |
|
||||
| --- | --- | --- | --- |
|
||||
| 资源卡「打开详情」 | PRD L61–62 要求"打开详情"与"播放 / 暂停"是可分别键盘聚焦的同级按钮 | 不存在该按钮(测试显式断言为 `null`);真正入口是 `选中资源:<栏目> <名>`,播放按钮是 `播放/暂停 <名>` | 按 #309 C3(取消资源详情面板与全屏编辑路由)判;PRD L61–62 建议同步修订 |
|
||||
| 运行入口不可用文案 | PRD L165 显示"当前无可运行版本",但仍允许点击 | 源码中无此字符串;实现为「首个可运行原型尚未完成,运行视图暂不可用」;且播放按钮 `disabled`,只有"运行"tab 不 disabled、用 `data-unavailable` 表达 | 建议按"tab 可点 + 播放按钮置灰"判,并把 PRD L165 文案对齐实现 |
|
||||
| 画布生成入口 | #309「画布生成入口未接」 | 已接三类(视频 / 音效 / 背景音乐) | 按代码与当前产品口径判,并更新 #309 进度段 |
|
||||
| 分类手设入口 | 旧版 PRD「用户可在分类与标签面板手动设置 category」 | 已移除;面板只编辑 tags(已随 `6bdc8bbd9` 入库) | 按新口径判 |
|
||||
| 真机 manifest 分类分布 | — | 落盘 `unclassified 57 / ui-interaction 3 / scene 1` | **读时自愈**会把 kind 可明确分类的资产显示到正确栏目,判定必须看 UI 栏目计数,不能看 manifest 文件,否则必然误报"分类不生效" |
|
||||
|
||||
## 8. 验收边界
|
||||
|
||||
- 本用例覆盖:资源总览 / 栏目分页画布、资源卡预览与本地受控读取、资源卡选择与多选、快速编辑派生、画布生成入口(3 类)、编辑标签、重命名、下载、删除三分支、版本切换与当前使用高亮、聊天 @ 引用与原子 chip、AI 润色与发送前提醒、本地预览启动与退出收尾、两份布局 sidecar 台账。
|
||||
- 本用例不覆盖(另走专项或定向测试):双窗口 CAS 冲突、大规模 fixture 性能、素材创作无限画布阶段一至五的草稿 / 事务 / 恢复矩阵(见 `【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md` 与配套专题)、UI 编辑器子路由、主站图片编辑器回归(见 PRD §7.7)。
|
||||
Reference in New Issue
Block a user