AGC 画布验收问题修复与栏目画布底部工具栏
- 合并资源画布命名筛选与条件筛选为单一筛选浮层,Dock 只留放大镜入口 - 修复「编辑素材标签」面板标签多时不可见且不可滚:加高度上界与标签区独立滚动 - 替换素材新增点选替换模式,并在资源卡标注会话内替换血缘 - 新增栏目画布底部工具栏,按功能画布分流图片类生成、音频生成与上传入口 - 新增 Tauri IPC generate_local_project_asset,收口图片类无源生成的 kind 与提示词目录 - 同步 PRD、AGC 验收用例、决策日志、踩坑记录与待解决事项文档
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
# AI 游戏创作项目开发工作台 PRD
|
||||
|
||||
更新时间:`2026-08-23`(右侧 Supervisor 对话气泡、可访问对比度与过程卡布局收口;资源卡预览、分区布局、非破坏性资源编辑及 Godot 双根合同保持不变)
|
||||
更新时间:`2026-09-13`(新增功能画布底部工具栏入口矩阵 §3.10 / §7.9;右侧 Supervisor 对话气泡、可访问对比度与过程卡布局收口,以及资源卡预览、分区布局、非破坏性资源编辑、资源替换与 Godot 双根合同保持不变)
|
||||
|
||||
## 1. 产品定位
|
||||
|
||||
@@ -140,6 +140,30 @@
|
||||
- 搜索只在已加载项目行中匹配名称、路径、类型、Godot 相对根和状态,不改 localStorage、不触碰项目目录、不新增后端或 Tauri 命令。无匹配状态提供清除搜索;真正空状态仍只引导用户使用顶部打开或新建,不追加第三个目录选择入口。
|
||||
- 正式视觉验收只覆盖 `1280×720` 最小横屏和 `1280×800` 默认窗口:工具栏保持单行,表头与项目列对齐,项目列表内部滚动,document/body 不出现页面级横向或纵向溢出。截图必须使用含 Web、Godot 和无效状态的 populated fixture;视频必须演示搜索、清除、行尾菜单及可观察的项目操作结果,不能只录静止页面或无结果点击。
|
||||
|
||||
### 3.10 功能画布底部工具栏(入口矩阵)
|
||||
|
||||
实现状态(2026-09-13):功能画布 = 资源栏目页(`resourceBookState.view === 'child'`)。栏目页左下角的底部工具栏按 manifest 资产的**功能分类** `category` 分流,矩阵由用户 2026-09-13 拍板,工具项顺序即下表顺序(最右一项为「上传」)。
|
||||
|
||||
| 栏目 | 工具栏里的生成入口 |
|
||||
| --- | --- |
|
||||
| UI 与交互 `ui-interaction` | 生成图片 | 生成规范(二级:图标规范 / 自定义规范)| 生成图标素材 | 生成 UI 设计图 | 上传 |
|
||||
| 角色与对象 `character` | 生成图片 | 生成规范(二级:角色规范 / 自定义规范)| 生成角色形象 | 上传 |
|
||||
| 场景与环境 `scene` | 生成图片 | 生成规范(二级:自定义规范)| 上传 |
|
||||
| 音频 `audio` | 生成背景音乐 | 生成音效 | 上传 |
|
||||
| 文档 `document`、待归类 `unclassified`、项目版本 `version`、「所有资源」展开态、资源总览 | **不渲染工具栏** |
|
||||
|
||||
- 位置与层级:画布左下角(`left: 14px; bottom: 14px; z-index: 40`)。右下角是既有的缩放 / 撤销 Dock(`right: 14px; bottom: 14px`),左下角是画布上唯一两者都不占的稳定空位。工具栏是管理区 `.game-resource-book-manager` 的**直接子节点**、与画本场景并列,不进带 `scale()` 的场景层;二级菜单与「入口不可用原因」都贴着工具栏上沿弹出,不做内嵌内容。
|
||||
- 外壳用共享 chrome(`packages/image-canvas-react` 的 `CanvasToolbar / CanvasToolbarGroup / CanvasChromeButton`),样式落在 AGC 的 `resourceCanvasChrome.css`;共享包只承接通用表现,不含业务规则。
|
||||
- 接线:图片类入口走本地 IPC `generate_local_project_asset`(`kind` ∈ `image / character / spec / icon-spec / ui-prototype / art-spritesheet`);音频入口复用既有的无源生成链路 `derive_local_project_resource`(`generationMode: 'create'`、`editKind` = `sound-effect` / `background-music`);「上传」复用 `upload_local_asset`。生成 / 上传成功后一律用「配对读 `(revision, manifest)`」交给 `onManifestChange`,走既有 manifest 刷新与资源投影链路,不重算依赖图、不另写布局。
|
||||
- 面板形态:独立浮层(`ThemedModal`),**不在当前面板下面追加内容**;面板内不写功能说明或规则解释文案。
|
||||
- 参数口径:比例 / 尺寸选项来自网页端美术画布的纯模型(`src/components/image-editor/ImageCanvasGenerationModel.ts`),并按本地 IPC 白名单收窄(本地通道明确拒绝 `4:3`);默认档 `1:1 · 1K`,生成 UI 设计图沿用网页端 UI 设计面板的默认 `16:9 · 1K`。本地 IPC 没有 `model` 入参,因此面板**不渲染模型选择器**(渲染一个改不了请求的控件就是假控件)。
|
||||
- 规范类和固定档:生成规范下的图标规范 / 角色规范 / 自定义规范都是固定档——只读展示当前规格,不给点了不生效的比例控件。本地 IPC 只有一条规范通道(`spec` 在 Rust 侧收口到 `icon-spec`),**没有 `specType` 入参**,因此角色规范与自定义规范共用该通道,靠素材名与提示词区分。
|
||||
- 前置规范图:`generate_local_project_asset` 的 `ui-prototype` 与 `art-spritesheet` 要求项目里已有登记并绑定当前账号的 `assets/art-spec.png`(`kind: icon-spec`、图片媒体类型、来源为画布)。缺前置时对应入口**保持可点击**并给出可执行的原因与指路(仓库口径:视觉不可用但可点击说明原因,不用原生 `disabled`),原因文案包含 `assets/art-spec.png` 与「生成规范 → 图标规范」。
|
||||
- 权威规范图由工具栏自身可满足:「图标规范」入口在项目尚无权威规范图时按 `assets/art-spec.png` 落盘;已有权威规范图时**不再传 `outputPath`**(Rust 侧 `replace_existing` 固定为 `false`,指向已存在文件会被硬拒绝「outputPath 已存在,禁止静默覆盖」),改为生成一张新的普通图标规范资产。这条前端策略不扩接口。
|
||||
- 音频入口收敛:音频画布的「生成背景音乐 / 生成音效」由工具栏承载,既有「生成素材」浮层入口**只保留生成视频**(视频能力不删,只是不出现在工具栏里)。
|
||||
- 本轮明确不做:生成视频(保留在既有浮层入口)、宣发素材、生成游戏场景、画布级「选择工具 / 抓手工具」(AGC 现无画布级工具模式)。这五类都**不得**出现在工具栏里。
|
||||
- 本地 IPC 边界(前端只做本地收窄,不改接口):提示词上限 `32000` 字符、素材名上限 `120` 字符、比例白名单 `1:1 / 2:3 / 3:2 / 9:16 / 16:9`、尺寸白名单 `0.5K / 1K / 2K`。
|
||||
|
||||
## 4. 工作台状态机
|
||||
|
||||
### 4.1 主视窗
|
||||
@@ -196,7 +220,7 @@ idle -> focused(document|art|audio|version) -> idle
|
||||
- 资源聚焦不提供通用工具栏或工具侧边栏;图片聚焦态允许一个明确的“精修资源”业务动作进入素材创作无限画布,该动作不是在聚焦容器中内嵌编辑器或恢复通用工具栏。
|
||||
- 点击资源后,中央主视窗从 `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`。项目切换和进入运行视图必须取消旧项目的焦点恢复意图。
|
||||
- 显式收起或按 Escape 后恢复进入前的搜索条件、dependency / type 布局模式、资源画布滚动位置和选中资源,并优先把键盘焦点还给原触发资源卡;这些只属于当前前端会话,不写入布局 sidecar。若资源已经被后台删除,必须清理 stale focused / selected ID、关闭详情并把焦点落到筛选浮层的「查找素材」关键词字段(画布唯一的搜索入口,见 §5.2.4),不得落到 `body`。项目切换和进入运行视图必须取消旧项目的焦点恢复意图。
|
||||
- 资源管理阶段四至阶段七交付上述受控读取、媒体展示、正式版本展示和引用高亮;`2026-08-05` 起图片编辑闭环生效,`2026-08-10` 起全类型非破坏性派生与版本子分支覆盖旧的只读限制。入口必须与草稿或 operation 恢复、正式事务提交、`referenceResourceIds` 血缘、新资源即时投影、两份布局和焦点竞态一次实现,不能只打开一个没有回写的面板。
|
||||
|
||||
### 4.4 历史成果与当前状态
|
||||
@@ -326,15 +350,15 @@ type UpdateProjectResourceCanvasLayoutResult =
|
||||
- 任一资源出现后,普通用户资源管理固定使用 `UI 交互 -> 角色与对象 -> 场景与环境 -> 音频 -> 文档 -> 待归类 -> 项目版本` 七栏目分页画布。前六栏是 manifest 资产的功能分类 `category`(`ui-interaction / character / scene / audio / document / unclassified`),项目版本不是资源资产、6 类资产分类轴对它不适用,因此固定单独成栏;已投影资源都进入画布导航、分页、卡片、搜索与详情入口,不再有被分区轴排除的内部栏目。每个栏目按 `projectId + mode + category` 保留独立 viewport,普通 wheel 平移当前视图,`Ctrl/Cmd + wheel` 以指针位置为锚点缩放当前无限画布,空白拖动只平移当前栏目;非空状态不提供分区高度、分区内部滚动或分区内容倍率。搜索和详情开关不得重置 viewport,项目、mode 或栏目切换只恢复各自会话状态,显式复位才重新适配当前栏目内容。
|
||||
- 布局 sidecar 的 `section` 允许继续出现旧四 / 五栏目取值(`code / document / version / art / audio`),读取时按资源当前分区做一次读时归并:`document / audio / version` 与新分区同名,1:1 保留;旧 `art` 是「扩展名 + mediaType」口径,按资源当前分类落入 `ui-interaction / character / scene / unclassified`;旧 `code` 落入 `unclassified`;无法精确归并时回落到 `unclassified`。归并只改写 `section`,`x`、`y` 与 `manuallyPlaced` 原样保留。读时会把 legacy 分区(`art` / `code`)坐标归并到新分区并写回一次(`revision` +1);不写迁移脚本、不重置坐标位置;但无法归并的坐标会被跳过,跳过条数连同归并条数在画布提示条上可见。
|
||||
- 首次载入项目中的既有资源不显示未读标识。当前会话内,非当前栏目出现稳定 ID 的新资源时,在对应栏目名称右上角显示红点;当前栏目新增资源不显示红点,用户通过点击或程序跳转进入该栏目后立即清除。未读状态只属于当前前端会话,并按 `projectPath + projectId` 隔离,切换项目时清空,不写入 manifest、布局 sidecar 或后端。
|
||||
- 资源筛选固定为**按需弹出的独立浮层**,由右下角 Dock 上与搜索按钮同一排的筛选按钮叫出,不使用常驻筛选条(常驻条已移除,不回归)。浮层固定三个字段,竖向排布:
|
||||
1. **查找素材**:关键词,与画布搜索浮层共用同一份状态,两个入口读写同一个关键词,不得各存一份;判据沿用搜索既有的名称、路径、媒体类型、任务名四字段与大小写不敏感口径。
|
||||
- 资源筛选固定为**按需弹出的独立浮层**,由右下角 Dock 的放大镜按钮或 `Ctrl/Cmd+F`(macOS 为 `Cmd+F`)叫出;Dock 上只有这一颗按钮,筛选与搜索共用同一个浮层,不再有第二个只放关键词的搜索浮层,也不使用常驻筛选条(常驻条已移除,不回归)。浮层固定三个字段,竖向排布:
|
||||
1. **查找素材**:关键词,就是画布搜索本身,与筛选条件共用同一份状态,只有一个入口读写它,不得各存一份;判据沿用搜索既有的名称、路径、媒体类型、任务名四字段与大小写不敏感口径。
|
||||
2. **所在区域**:取值为「全部区域」加上现行分区栏目(`PROJECT_RESOURCE_CANVAS_SECTIONS` 的 6 类资产分类与末尾独立的「项目版本」栏),显示名沿用 `@` 面板资源筛选那份唯一中文口径,不另建译名表。
|
||||
3. **自定义标签**:从 manifest `assets[].tags` 派生的标签库,多选且为 AND 语义(已选标签必须全部命中,空选择不过滤),复用共享判据 `assetTagsMatchSelection`。
|
||||
不提供「状态」字段:manifest 资产与资源投影都没有状态事实源,候选的任务状态与附件导入状态在资源投影里已被上游过滤,任何取值都恒为同一结果,因此不渲染该字段,也不保留占位下拉。
|
||||
- 「所在区域」**既是筛选维度也是画布当前栏目**,两者必须是同一份状态:区域取值由画布栏目状态派生,切换区域走与栏目切换完全相同的那一条路径,因此用户用滚轮、总览卡片或下一页切换栏目时区域显示随之变化,不需要双向同步逻辑,也不会出现两处状态互相漂移。
|
||||
- 区域取值与其它维度叠加时,筛选只作用于当前区域内的资源;「全部区域」作用于全部资源。两种语义共用同一份过滤实现,不为「只筛当前区域」另写一套裁集合逻辑。
|
||||
- 筛选浮层与画布搜索浮层使用同一个右下角锚点,两者不得同时展开:打开任一个时收起另一个。
|
||||
- 筛选浮层与搜索浮层的关闭口径一致:点击浮层外部或按 `Escape` 关闭,点触发按钮只走按钮自身的开合、不被判定为外部点击;`Escape` 在 `document` 阶段截断,画布全局 `Escape`(清选中)不随之触发;`Escape` 关闭后焦点回到触发按钮。筛选条件不随浮层收起一起消失,条件生效时 Dock 上的筛选按钮保持高亮。
|
||||
- 筛选浮层是画布唯一的搜索与筛选入口:Dock 上的放大镜按钮与 `Ctrl/Cmd+F` 叫出的是同一个浮层,不存在第二个浮层,因此也不存在「两个浮层互斥」这一说。
|
||||
- 关闭口径:点击浮层外部或按 `Escape` 关闭,点触发按钮只走按钮自身的开合、不被判定为外部点击;`Escape` 在 `document` 阶段截断,画布全局 `Escape`(清选中)不随之触发;`Escape` 关闭后焦点回到触发按钮。浮层叫出后焦点落到「查找素材」关键词字段(`type=search` 的原生 Esc 清空被 `preventDefault` 挡掉,关闭不等于清除关键词)。筛选条件(关键词 / 区域 / 标签)不随浮层收起一起消失,条件生效时 Dock 上的放大镜按钮保持高亮(判据 = 关键词 ∨ 标签,不含区域)。
|
||||
- 筛选后当前栏目内没有匹配资源时必须给出空态,不显示空白画布。
|
||||
- 打开项目、切换 mode 或当前 mode 首次出现新资源时执行“读取 -> 协调 -> 必要时 CAS 写入”;dependency 模式必须先等待与当前 `projectPath + projectId + resource inputs` 匹配的 Rust 图进入 `ready` 或 `failed` 终态,等待期间不得创建 fallback、读取 sidecar、协调资源或入队保存。`failed` 只允许以空图降级初始化一次。项目或 mode 已切换后返回的旧异步结果必须丢弃。
|
||||
- 同一 `projectPath + projectId + mode` 的首次读取与资源集合协调必须分开:资源集合变化不得取消已经发出的读取或保存。当前 scope 内资源自动协调写入使用单写者 FIFO,任一时刻最多一个 CAS 在途,后一笔必须使用前一笔成功返回的 revision。切换项目或 mode 后,旧 scope 的在途请求不能阻塞新 scope 队列;前端放弃旧请求槽位并丢弃其迟到响应,后端继续依靠 `expectedProjectId + expectedRevision + 系统锁` 仲裁已发出的请求。
|
||||
@@ -412,6 +436,8 @@ type ProjectVersionResourceReplacement = {
|
||||
- **失败即拒绝**:硬门禁任一项不等、源版本不存在、源版本未绑定该素材、替换素材未登记、替换素材与源素材相同、`expectedProjectId` / `expectedProjectRevision` CAS 冲突,都必须在写入前拒绝并说明原因;拒绝时 manifest、`versions` 与项目 revision 都不变,投影里不得出现新的版本卡或新的"当前使用"高亮。
|
||||
- **落盘与留痕**:改绑定属于 `versions` 变化,写入后推进一次 `projectRevision`;并追加一条 `asset.version_binding.replace` 审计(`versionId / sourceResourceId / replacementResourceId / projectRevision`)。**没有可回溯的替换历史**:替换前的身份只有这条审计与 manifest 的 `.previous` 恢复副本。
|
||||
- **入口与可见效果**:入口在资源卡选中工具条,只对「manifest 资产 + 被当前版本绑定」的素材放行;候选弹窗列出全部候选,命中硬门禁的项渲染但禁用并给出原因,只有尺寸规格差异的项仍可选但带提示。成功后**不切换版本**(没有新版本可切),只重读 manifest;可见变化是资源卡"当前使用"高亮移到替换素材、`@` 面板"当前版本素材"更新。按 §7.4 的口径,改绑定不做运行时资源重映射,运行画面本身不会因改绑定而变化;也不自动重载 / 重启运行中的预览(§3.2 末条)。
|
||||
- **点选替换(2026-09-13 补充)**:候选弹窗 footer 提供「点选替换」,用于"目标就在画布上、不想在面板里筛"的场景。点它**必须关闭弹窗**(弹窗外壳是全屏遮罩,留着它画布上的卡点不到)并进入画布点选态,画布上出现提示条(「在画布上点选要替换成的素材;点击空白处不会退出」+「取消」)。点中合法候选直接走**同一条**写入链路(载荷与弹窗确认逐字一致,不产生第二套替换口径),成功后自动退出点选态;点中非法目标(分类 / 类型不同、不在后端给出的权威候选里、源素材本身、未登记资源)**不写入**,在提示条上说明原因(与候选弹窗同一条翻译口径)并**留在**点选态。退出只有三条路:Esc(在 `document` 阶段截断,画布全局 Esc 的清选中不随之触发)、提示条「取消」、替换成功;**空白点击不退出、也不清画布选中**。点选态下卡片左键只用于点选(不起拖拽、不换选中,靠一次性抑制而不是全局关掉单击),滚轮平移与 `Ctrl/Cmd` 缩放照常可用。
|
||||
- **替换血缘标注(2026-09-13 补充)**:替换成功后只改绑定、光环本来就会从源素材移到替换素材,光靠光环说明不了"谁换成了谁",所以在卡面补一层**关系标注**:源素材卡标「已被 <替换素材显示名> 替换」,替换素材卡标「替换自 <源素材显示名>」,两处分别给出稳定 DOM 判据 `data-resource-replaced-by` / `data-resource-replacement-of`,值是对面资源的 **manifest 资产 id**(不是显示名)。这份关系是**宿主会话内状态**:同一会话里再次替换**整条覆盖**上一条(只保留当前有效的一条 A→B,不做历史链,与 §7.8「只展示当前有效关系」同口径);**切换项目 / 关闭工作台 / 重新加载 manifest 都不保留**——客户端没有替换关系的读侧事实源(写入结果只回 `versionId` / `committedProjectRevision`,审计 `asset.version_binding.replace` 只有写侧),manifest 的 `.previous` 只是原子安装的崩溃兜底副本、安装成功即删,不能当数据源,所以本标注**只在本次会话有效,不假装持久**。
|
||||
|
||||
### 5.4 游戏迭代版本(P1)
|
||||
|
||||
@@ -619,6 +645,18 @@ type ProjectAgentMudPointAttribution = {
|
||||
5. 硬门禁任一为 false、源版本不存在、源版本未绑定该素材、替换素材未登记、替换素材与源素材相同、`expectedProjectId` / `expectedProjectRevision` 冲突,都必须在写入前拒绝;拒绝时 manifest、`versions` 与项目 revision 都不变,投影里不得出现新的"当前使用"高亮或版本卡变化。
|
||||
6. 成功后重读 manifest 并**不切换版本**(没有新版本可切);**不**自动重载或重启运行中的预览(§3.2 末条),也不做运行时资源重映射。可见变化只有:资源卡"当前使用"高亮移到替换素材、`@` 面板"当前版本素材"更新。
|
||||
7. 替换成功后追加一条 `asset.version_binding.replace` 审计(`versionId / sourceResourceId / replacementResourceId / projectRevision`);**替换历史不可回溯**——替换前身份只有这条审计与 manifest 的 `.previous` 副本,不得声称能查到"某版本历史上换过什么"。
|
||||
8. 候选弹窗提供「点选替换」(2026-09-13):点它关闭弹窗进入画布点选态,并把「在画布上点选要替换成的素材;点击空白处不会退出」与「取消」放在画布提示条上;点中合法候选按第 3~7 条**同一条**链路提交并自动退出点选态,点中非法目标(分类 / 类型不同、不在权威候选里、源素材本身、未登记资源)零写入、留在点选态并在提示条上说明原因。退出只有 Esc(`document` 阶段截断,不清画布选中)/ 提示条「取消」/ 替换成功三条路,空白点击不退出;点选态下滚轮平移与缩放照常,卡片单击的既有语义在非点选态下不变。
|
||||
9. 替换血缘标注(2026-09-13):替换成功后,源素材卡带「已被 <替换素材显示名> 替换」、替换素材卡带「替换自 <源素材显示名>」,稳定 DOM 判据 `data-resource-replaced-by` / `data-resource-replacement-of` 的值为对面资源的 manifest 资产 id;未参与替换的资源卡两个属性都不出现。同一会话内再次替换时**只保留当前有效的一条 A→B**(旧标注从卡上消失,不做历史链)。这份标注**只在本次会话有效**:切换项目 / 关闭工作台 / 重新加载 manifest 都不保留,也不得声称可回溯(第 7 条仍成立)。
|
||||
|
||||
### 7.9 P1 功能画布底部工具栏验收
|
||||
|
||||
1. 工具栏只在资源栏目页且分类命中矩阵四个栏目时渲染;资源总览、「所有资源」展开态、文档 / 待归类 / 项目版本都不渲染。稳定 DOM 判据 `[data-resource-bottom-toolbar="<category>"]`;总览与「所有资源」展开态下该节点必须不存在。
|
||||
2. 工具栏是管理区 `.game-resource-book-manager` 的直接子节点、与画本场景并列(`closest('.game-resource-book-scene')` 为 `null`),且与右下角 Dock 互不抢位(左下角锚定:`left` 有值、`right` 无声明;Dock 相反)。二级菜单只在展开时出现,展开时可被外部点击 / Esc 收起。
|
||||
3. 每个图片类入口一次提交**一条** `generate_local_project_asset`,载荷字段逐字为 `{ projectPath, kind, prompt, aspectRatio, imageSize, assetName, outputPath }`;`kind` 映射:生成图片 `image`、生成角色形象 `character`、图标规范 `icon-spec`、角色规范 / 自定义规范 `spec`、生成图标素材 `art-spritesheet`、生成 UI 设计图 `ui-prototype`。
|
||||
4. 比例 / 尺寸选项**不含**本地通道拒绝的 `4:3`;比例与尺寸的切换都进入载荷;固定档规范入口只读展示当前规格(如 `1:1·1K`),不渲染可点比例控件。
|
||||
5. 缺权威规范图时「生成图标素材 / 生成 UI 设计图」保持可点击、给出含 `assets/art-spec.png` 的原因说明,且**不发出任何生成请求**;「图标规范」此时把 `outputPath` 指向 `assets/art-spec.png`;项目已有权威规范图时 `outputPath` 为 `null`(两种状态各一条用例)。
|
||||
6. 生成 / 上传成功后走既有刷新路径:`get_local_game_project_revision` + `get_local_game_manifest` 的配对读发生在写入命令之后,结果交给 `onManifestChange`;音频入口提交的是 `derive_local_project_resource`(`editKind` = `sound-effect` / `background-music`、`generationMode: 'create'`、`sourceMediaType: 'audio/mpeg'`)。
|
||||
7. 既有「生成素材」浮层入口只呈现视频:面板不再渲染音效 / 背景音乐类型选择器(单类型入口没有类型选择器),音频入口只出现在音频栏目工具栏。
|
||||
|
||||
## 8. 非目标
|
||||
|
||||
@@ -630,3 +668,4 @@ type ProjectAgentMudPointAttribution = {
|
||||
- 不开放普通用户 Agent.md/Skill。
|
||||
- 不自动确认 Agent 动作,不自动触发可能扣费的生成。
|
||||
- 不把当前项目工作台推广为其它玩法的默认创作模式。
|
||||
- 功能画布底部工具栏不提供生成视频、宣发素材、生成游戏场景,也不提供画布级「选择工具 / 抓手工具」;工具栏之外不新增第二套生成入口。
|
||||
|
||||
@@ -22,6 +22,15 @@
|
||||
- 关联文档:相关 PRD、技术文档、提交或 Issue
|
||||
```
|
||||
|
||||
## 2026-09-13 AGC 资源替换补「会话内血缘标注」:只保留当前有效的一条 A→B,不假装持久
|
||||
|
||||
- 背景:用户验收指出「当前版本使用素材替换后,一个标为当前版本素材一个不是,替换关系不明」。核实后的边界:①光环(`currentVersionBindingIds` → DOM `is-current-version` / `data-used-by-current-version`)本身同源、不会两处打架,替换后光环从源素材 A 移到替换素材 B 是**设计内**的既定变化;②替换关系在客户端**任何一层都不存在** —— 宿主只消费替换结果的 `result.versionId` 与 `result.committedProjectRevision`,`ProjectVersionResourceReplacement` 里的 `sourceResourceId` / `replacementResourceId` / `warning` 无人消费,审计 `asset.version_binding.replace` 只有写侧;③manifest 的 `.previous` 不能当数据源 —— 它只是原子安装的崩溃兜底副本,装盘成功即删,正常项目里根本不存在。
|
||||
- 决策:只在**宿主会话内**记一条血缘(源素材 A → 替换素材 B,两端都是 manifest 资产 id),并把它标回画布:源素材卡「已被 <B 显示名> 替换」、替换素材卡「替换自 <A 显示名>」,稳定 DOM 判据 `data-resource-replaced-by` / `data-resource-replacement-of`(值是对面资源的 manifest 资产 id,不是显示名)。同一会话内再次替换**整条覆盖**上一条,只保留当前有效的一条,不做历史链(与 PRD §7.8「只展示当前有效关系」同口径)。
|
||||
- 生命周期(本方案的天花板,UI 与 PRD 都已写明):**只在本次会话有效** —— 切换项目 / 关闭工作台 / 重新加载 manifest 都不保留,也不写盘,不假装持久。要让关系跨会话可查,必须先有读侧事实源(例如让替换审计可从客户端读取,或新建一份替换血缘 sidecar),那是另一个切片。
|
||||
- 影响范围:`apps/ai-game-creator-shell` 前端(`features/resource-canvas/resourceVersionReplacementModel.ts` 新增纯函数、`view/project-development/index.tsx` 宿主状态与卡面、`styles.css` 角标样式、`tests/resourceVersionReplacement.test.tsx` 与 `tests/appSurface/project-development.suite.ts` 行为断言);不改 Rust、不改版本绑定口径、不改 `game_iteration_resource_bindings`,也**不碰**并行的「点选替换」态。
|
||||
- **遗留(独立事项,本轮未修)**:版本绑定存在口径冲突 —— 版本追加会把绑定重写成「当时全部 manifest assets」(`src-tauri/src/project/manifest.rs:727-738`,由每个成功 Agent 回合 `src-tauri/src/agent/direct_runtime.rs:3818-3821` 触发),而替换把绑定改窄(`version_resource_replacement.rs:409-440`);`activeVersionId` 为 null 时取最后一版(`src/features/project-workspace/resourceReferences.ts:226-241`),于是下一次版本追加后**被替换掉的源素材 A 会重新带上「当前版本使用」光环**。该项已由用户确认单独立项,本次不动。
|
||||
- 关联文档:[项目开发工作台 PRD §5.3 / §7.8](../../prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md)。
|
||||
|
||||
## 2026-09-11 素材类型(功能分类)重新提供用户入口;资源卡角标改显示资源类型而非媒体类型
|
||||
|
||||
- 背景:`6bdc8bbd9` 把「分类与标签」面板收敛为纯标签面板,并明确记下「随之的事实是:**「用户手动设置 `category`」这项能力就此移除**」(本文件 2026-09-11 那条以「用户给出的目标样式截图里,这个面板标题是「编辑素材标签」…」开头的条目,其决策一行即该结论)。用户随后要求「用户可以自己变更素材类型」,并追加要求把资源卡右上角角标从"文件/媒体类型"(图片 / 视频 / 文档 …)改成"资源类型"(功能分类中文名)。现状核实:写入链路本来就是完整的 —— `update_local_project_resource_classification`(`src-tauri/src/commands.rs:2162`)→ `update_manifest_asset_classification_at`(`src-tauri/src/project/manifest.rs:1112`)已接受任意合法 `category` 并校验 6 个合法值,**本次不需要改 Rust**;缺的只有 UI 入口与角标口径。本条**只回收「面板不再编辑分类」这一项**,「面板标题仍是「编辑素材标签」、删除资源入口仍在选中工具条」等其余结论不变。
|
||||
@@ -68,6 +77,7 @@
|
||||
- 未落地(截至本条记录时):`index.tsx` 的 3 处接线(①`visibleResources` 内联过滤换成调用纯函数;②新增 `resourceFilterOpen` state + Dock 筛选按钮 + 浮层挂载,并把区域接到 `openResourceBookChild`;③新增 `activeTags` state)**尚未提交**——当时该文件正被另一条线(美术画布「生成动画」接线)占用。已落地的部分(外壳、纯函数、AGC 侧组件与样式)各自独立提交,接线完成后本条应补记。
|
||||
- 验证方式:`packages/shared/src/components/PlatformFilterPanel.test.tsx` 覆盖 `dialog` 语义与关闭转发、字段逐个渲染、外壳不自带「状态」占位字段、`controlId` 字段名关联、宿主定位类注入且组件不自带 `absolute` / `fixed`;`apps/ai-game-creator-shell/tests/resourceCanvasFilterModel.test.ts` 覆盖区域选项顺序与逐个中文显示名、空筛选不重排、区域恒等比较、关键词四字段与空白归一、标签 AND 语义、无标签事实源资源的口径、三维度叠加、标签库计数与 `zh-CN` 排序、筛选生效判据;`apps/ai-game-creator-shell/tests/resourceFilterPanel.test.tsx` 覆盖三字段渲染且无「状态」占位、区域选项取值与显示名、关键词回显与回调、区域切换只回调、标签受控多选与 `aria-pressed`、空标签库隐藏该字段、关闭键 / `Escape` / 点外部 / 点触发按钮四种关闭口径、`Escape` 后焦点回触发按钮。**变异验证实测**:把区域判据改成恒 `true` → `4 failed`;去掉关键词判据 → `3 failed`;去掉标签判据 → `4 failed`;三处还原后复跑 `13 passed`。既有断言零放宽(`PlatformResourceFilterBar.test.tsx` 与主站弹窗测试一字未改,同跑 `9 passed`)。
|
||||
- 关联文档:`docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`(§5.2.4 资源筛选浮层条款)、本条上一条 2026-09-11 决策、`docs/project-memory/shared-memory/pitfalls.md`(常驻筛选条移除那条)。
|
||||
- 后续(2026-09-13):画布搜索浮层合并进本浮层——Dock 只留放大镜按钮、`Ctrl/Cmd+F` 与它叫出同一个面板,上面那条「两个入口读写同一个关键词」收敛为**唯一入口**,纯搜索浮层及其 state / ref / CSS 与两处手写互斥一并删除。三个字段、区域派生、「状态」字段不做、关闭与焦点口径(Esc 关闭不清条件、焦点回触发按钮)均未变,见本文件 2026-09-13 一条。
|
||||
|
||||
## 2026-09-11 资源画布 sidecar 的读时归并保持写回,但不再静默,并公开丢弃口径
|
||||
|
||||
@@ -8547,3 +8557,33 @@ CI 上 `background_agent_runtime_recovers_stale_running_before_pending_task` 在
|
||||
- 影响范围:`apps/ai-game-creator-shell/src/App.tsx`(新增跟随 effect + `manifestRef`)、`apps/ai-game-creator-shell/tests/resourceTagStatsRefresh.test.tsx`(新增真宿主链路用例)。manifest 契约、Rust、SpacetimeDB 均不动。
|
||||
- 验证方式:`npm run test -- apps/ai-game-creator-shell/tests/resourceTagStatsRefresh.test.tsx`(先红:`expected [] to deeply equal ['主角1']`;修复后 2 条绿)+ 11 个资源相关套件 124 条 + `appSurface.test.ts` 415 条 + `npm run typecheck` + `npm --prefix apps/ai-game-creator-shell run typecheck` + `npm run check:encoding` + `git diff --check`。变异验证(已实测并还原):把跟随 effect 改回「不跟随壳快照」→ 新增用例第一条变红(`expected [] to deeply equal ['主角1']`)。
|
||||
- 关联文档:无(本轮不涉及跨端契约、PRD 或后端口径变化)。
|
||||
|
||||
## 2026-09-13 AGC 资源画布「搜索」与「筛选」合并为单一筛选浮层
|
||||
|
||||
- 背景:右下角 Dock 上并排两颗按钮——放大镜开 `.game-resource-search` 纯搜索浮层,滑块开 `.game-resource-filter-panel` 筛选浮层;两者共用同一份 `searchText`(筛选面板的第一个字段就是同一个关键词),却各存一套开合状态,互斥逻辑在两个 `onClick` 里各写一遍且无测试守护。同一个功能两个入口,本身就是两处随时会漂移的状态。
|
||||
- 决策:**状态不合并、只收敛 UI 入口。** 删除纯搜索浮层的 JSX、`resourceSearchOpen` / `resourceSearchId` / `resourceSearchRef` / `resourceSearchTriggerRef` / `resourceSearchPopoverRef`、它的共享 dismiss hook 调用与 `.game-resource-search` 两条 CSS;Dock 只留放大镜按钮(`aria-controls` → `resourceFilterId`,`is-active` → `resourceFilterActive`),`Ctrl/Cmd+F` 与它打开同一个面板。面板的「点外部 + Esc、Esc 后焦点还给触发按钮」继续沿用它自己那套手写实现(不改成共享 hook,共享 hook 判据用 `.image-canvas-editor__portal-menu`,会把焦点还错地方)。关键词字段改由 `autoFocus` 承接既有「浮层打开后焦点落到搜索框」,并保留 `type=search` 原生 Esc 清空的 `preventDefault`(关闭 ≠ 清条件)。
|
||||
- 原因:`searchText`、`resourceFilterTags` 与派生 region 的语义一行未改,「条件生效只隐藏卡片、不重排坐标」与 Dock 高亮判据(关键词 ∨ 标签,**不含区域**)因此不可能跟着变;真正多余的只是那颗只看关键词的浮层和它那份开合状态。`filterCanvasResources` 的过滤链路与 `resourceCanvasFocusModel` 里 `.game-resource-filter-panel` 的滚轮归属登记一并保留(删了面板内滚轮会带动画布)。
|
||||
- 验证:`appSurface.test.ts` 全量 416 passed(含改写后的 4 条:CSS/Dock 锚定几何、DOM 归属并列、两视图关键词过滤、快捷键开合与不清条件/焦点回按钮);`resourceFilterPanel.test.tsx` 8 passed;`projectResourceLiveIntegration.test.tsx` 25 passed;`npm run check:encoding` 与 `git diff --check` 干净。
|
||||
- 影响范围:`apps/ai-game-creator-shell/src/view/project-development/index.tsx`、`.../ResourceFilterPanel.tsx`、`apps/ai-game-creator-shell/src/styles.css`、`apps/ai-game-creator-shell/tests/appSurface/{harness.ts,project-development.suite.ts}`、`apps/ai-game-creator-shell/tests/projectResourceLiveIntegration.test.tsx`、PRD §5.2.4、`docs/technical/【测试用例】AGC资源工作台V3端到端验收-2026-09-11.md` 的 S8。
|
||||
|
||||
## 2026-09-13 AGC 资源画布替换素材增加「点选替换」入口
|
||||
|
||||
- 背景:用户验收报告诉求「替换弹窗里增加一个『点选替换』按钮,进入后直接在画布上点选目标素材 B 完成替换,避免在面板里筛选」。原实现只有一条路:工具条「替换素材」→ 候选弹窗(`ImageCanvasProjectAssetPickerDialog`,全仓唯一共享副本)里筛/选/确认。
|
||||
- 决策:候选弹窗加一个 **opt-in** prop `onPickFromCanvas`(不传就不渲染该按钮,网页端美术画布参考图多选行为逐字不变,`packages/` 不动);AGC 侧新增 `resourceReplacementPickMode`,与弹窗互斥但**不重置**冻结的 `source` / `candidates` —— 点选的合法性判据与写入载荷用的还是那一份权威候选。点选落点(卡片左键 pointerdown)走新增纯函数 `resolveResourceReplacementPick`(源素材本身 / 不在候选里 / 硬门禁不过 / 合法四类),合法即调既有 `confirmResourceVersionReplacement`(与弹窗确认**同一条**写入链路,不新开一条),非法则把原因写进提示条并留在点选态。弹窗必须卸载(`UnifiedModal` 是全屏遮罩,留着它画布点不到);提示条 `.game-resource-canvas-pick-hint` 渲染在 `.game-resource-book-manager` 里(不在会随视口缩放/平移的场景内,也不随「收起资源」卸载),文案含「在画布上点选要替换成的素材」「点击空白处不会退出」与「取消」。
|
||||
- 原因:替换的合法性判据在 Rust、前端只呈现(PRD §5.3),所以点选不能自己重算一套兼容性;复用同一份候选 + 同一个 `confirm` 是唯一不会分叉的写法。点选态下卡片单击不参与选中,但**只靠一次性抑制**(`skipNextResourceCardClickRef`,与卡片拖拽后抑制误点同一机制;pointerup 处 `deferSkippedResourceCardClickCleanup` 收尾、会话结束时再兜底清一次),而不是全局关掉单击——非点选态的单击语义(选中/浮出工具条)因此一行未改。Esc 挂 `document` 并 `stopPropagation`:资源画布自己的 Esc 挂 `window`(清画布焦点 = 清选中 + 收浮层),不截断就会「退出点选顺带丢选中」。空白处左键在点选态下不起框选、也不清焦点(中键 / 空格平移与滚轮平移缩放照常)。点选会话只在资源画布可见时存在:切运行态 / 进 UI 编辑器即结束,避免留下没有提示条也没有 Esc 的死会话。
|
||||
- 验证:`apps/ai-game-creator-shell/tests/resourceVersionReplacement.test.tsx` 14 passed(新增 4 条:入口关弹窗且零写入、合法点选提交一次且载荷与弹窗一致并自动退出、非法四种目标零写入留态且点空白不退出、Esc 退出不清选中),`appSurface.test.ts` 416 条 415 passed(唯一失败是 `keeps project policy deny and confirm command lists mutually exclusive` 5s 超时,单跑 3.2s 通过,与本次改动无关),`npm run agc:typecheck` 的 `tsc -p tsconfig.json --noEmit` 通过(其后的 `check-config.mjs` 因并发改动新增 `generate_local_project_asset` 未登记而报错,与本轮无关)。变异验证(已实测并还原):删掉点选里的一次性抑制 → 合法点选用例的「源素材仍选中」断言变红;删掉 Esc 的 `stopPropagation` → Esc 用例的 window 监听器收不到断言变红;删掉空白处门禁 → 非法目标用例的「点空白不清选中」断言变红。
|
||||
- 影响范围:`apps/ai-game-creator-shell/src/view/project-development/index.tsx`、`.../features/resource-canvas/resourceVersionReplacementModel.ts`、`.../features/resource-canvas/resourceCanvasChrome.css`、`src/components/image-editor/ImageCanvasProjectAssetPickerDialog.tsx`、`apps/ai-game-creator-shell/tests/resourceVersionReplacement.test.tsx`、PRD §5.3 / §7.8 第 8 条、`docs/technical/【测试用例】AGC资源工作台V3端到端验收-2026-09-11.md` 的 S15a。
|
||||
- 已知未覆盖:点选只能点当前画布上可见的卡(目标在别的栏目要先切栏目,会话跨栏目存活);资源面板列表与 `@` 选择器在点选态下仍走各自的选中/插入语义,不参与点选。
|
||||
|
||||
## 2026-09-13 AGC 资源栏目页落地功能画布底部工具栏(工具矩阵 + 图片类 / 音频 / 上传接线)
|
||||
|
||||
- 背景:用户当日拍板「功能画布 = 资源栏目,按栏目分流底部工具栏」的工具矩阵,并明确不做生成视频 / 宣发素材 / 生成游戏场景(游戏场景本轮不做)与画布级「选择工具 / 抓手工具」(AGC 现无画布级工具模式)。本地 IPC `generate_local_project_asset` 当时已实现并通过 Rust 单测(含 `spec → icon-spec` 收口、图集缺规范图失败、比例 / 尺寸 / 长度边界),但**前端零调用方** —— `check-config.mjs` 的「App invoke 或显式 native-only 白名单」门禁因此一直报它未登记(本日前一条记录已把它记成"与本轮无关的报错"),本轮落地调用方后自然转绿,这正是本任务的验收项之一。
|
||||
- 决策(矩阵):`ui-interaction` = 生成图片 / 生成规范(图标规范、自定义规范)/ 生成图标素材 / 生成 UI 设计图 / 上传;`character` = 生成图片 / 生成规范(角色规范、自定义规范)/ 生成角色形象 / 上传;`scene` = 生成图片 / 生成规范(自定义规范)/ 上传;`audio` = 生成背景音乐 / 生成音效 / 上传。文档、待归类、项目版本、「所有资源」展开态与资源总览**不渲染**工具栏(`resolveResourceCanvasBottomTools` 返回空数组,渲染判据 `resourceBookState.view === 'child'` 且 category 命中四个栏目)。工具项与顺序是纯数据表,事实源落在 `resourceCanvasBottomToolbarModel.ts`。
|
||||
- 决策(落点与几何):工具栏是 `.game-resource-book-manager` 的**直接子节点**(与画本场景、右下 Dock、通知条并列,不进带 `scale()` 的场景层),锚在**左下角**(`left/bottom: 14px; z-index: 40`)——右下角是既有缩放 / 撤销 Dock,左下角是画布上唯一两者都不占的稳定空位;外壳消费共享 `packages/image-canvas-react` 的 `CanvasToolbar / CanvasToolbarGroup / CanvasChromeButton`(AGC 侧第一次真正消费这组共享 chrome)。一处必要覆盖:共享 `.genarrative-image-canvas__toolbar` 是横向滚动容器,会裁掉它自己弹出的二级菜单,消费端改成 `overflow: visible` + `flex-wrap: wrap`。
|
||||
- 决策(接线与收敛):图片类入口 `generate_local_project_asset`(kind 映射:图片 `image` / 角色形象 `character` / 图标规范 `icon-spec` / 角色规范与自定义规范 `spec` / 图标素材 `art-spritesheet` / UI 设计图 `ui-prototype`);音频入口复用既有 `derive_local_project_resource` 无源生成链路(**不另写一份音频生成**);上传复用 `upload_local_asset`。两者成功后的刷新形状统一为「**配对读 `(revision, manifest)`** → `onManifestChange` → `pendingResourceFocusRef` 定位新卡」,不重算依赖图、不另写布局。**音频入口收敛**:既有「生成素材」浮层入口改为只保留视频(`ResourceCanvasGenerationPanelView` 新增 `kinds` prop,单类型入口不再渲染类型选择器,面板标题与提交文案跟类型走),视频能力不删只是不进工具栏。
|
||||
- 决策(参数口径,避免假控件):比例 / 尺寸选项与默认值复用网页端纯模型 `ImageCanvasGenerationModel.ts`,但按本地 IPC 白名单收窄(本地通道明确拒绝 `4:3`,照搬就是一个点了必失败的选项);提示词上限复用 `resourceEditPromptMaxLength`(32000,与 Rust `LOCAL_PROJECT_ASSET_MAX_PROMPT_CHARS` 同口径);**面板不渲染模型选择器**(本地 IPC 没有 `model` 入参)。规范入口是固定档:只读展示规格,不给点了不生效的比例控件。
|
||||
- 决策(前置规范图,本轮的关键设计):`ui-prototype` 与 `art-spritesheet` 在 Rust 侧要求项目里已有登记并绑定当前账号的 `assets/art-spec.png`(`local_path` 精确匹配 + `kind == icon-spec` + 图片媒体类型 + 画布来源)。前端判据 `projectHasIconSpecReference` 与它逐字对齐;缺前置时入口**保持可点击**并给可执行原因(`aria-disabled` 而非原生 `disabled`,文案含 `assets/art-spec.png` 与「生成规范 → 图标规范」),且零生成请求。**同时**把「图标规范」入口在缺前置时按 `assets/art-spec.png` 落盘(`outputPath`),否则那两条入口会被前置条件永久锁死、工具栏自身无法满足自己的前置;已有权威规范图时不再传 `outputPath`(Rust `replace_existing` 固定 `false`,指向已存在文件会被硬拒),改为生成一张新的普通图标规范资产。这条是**前端策略**,不扩接口。
|
||||
- 已知能力缺口(用户口径是「缺参数就停下汇报,不自行扩接口」,记账备查):本地 IPC 没有 ① `model` ② `specType` ③ `replaceExisting` 入参。后果:面板不给模型选择;**角色规范与自定义规范共用 `spec` 通道**(靠 `assetName` / 提示词区分),因此「角色规范」不是真正的角色设定板通道;权威规范图存在时无法重写它。另:网页端 composer 子视图(含 `ImageCanvasSpecGenerationPanelView` 直连 `/api/editor/llm/icon-specs/*`)**没有复用**,因为 AGC 无该 BFF 通道且本地 IPC 不接受模型 / 参考图入参,照搬会渲染改不了请求的控件——本轮只把**纯模型**接进来。
|
||||
- 影响范围:`apps/ai-game-creator-shell/src/features/resource-canvas/{resourceCanvasBottomToolbarModel.ts,ResourceCanvasBottomToolbarView.tsx,ResourceCanvasAssetGenerationPanelView.tsx,ResourceCanvasGenerationPanelView.tsx,resourceCanvasChrome.css}`、`apps/ai-game-creator-shell/src/view/project-development/index.tsx`、测试 `apps/ai-game-creator-shell/tests/{resourceCanvasBottomToolbar.test.tsx(新增),resourceCanvasGenerationEntry.test.tsx,projectResourceLiveIntegration.test.tsx,appSurface/project-development.suite.ts}`、PRD §3.10 / §7.9 / §8、`docs/technical/【AGC】栏目画布底部工具栏入口矩阵-2026-09-13.md`(新增)、验收用例 S11 / S11a。**未动**:Rust、external v1 / OpenAPI、`packages/`(只消费共享 chrome)、SpacetimeDB。
|
||||
- 验证方式:`npx vitest run apps/ai-game-creator-shell/tests/appSurface.test.ts`(423 passed,含新增 6 条:栏目分流与总览不渲染、UI 栏 5 个入口载荷、前置缺失可点击说明且零请求、角色栏 2 个入口、音频入口走既有链路、上传 + 配对读清单,另 1 条工具栏与 Dock 的 CSS 几何契约);`resourceCanvasBottomToolbar.test.tsx` 15 passed(新增);`resourceCanvasGenerationEntry.test.tsx` 11 passed(新增单类型用例 1 条);`projectResourceLiveIntegration.test.tsx` 25 passed(「生成素材」面板改名断言同步更新);`npm run agc:typecheck` 全绿(**其中的 `check-config.mjs` 报错已因本轮落地调用方而消失**)、`npm run check:encoding`、`git diff --check` 干净。未 commit。
|
||||
- 关联文档:`docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`(§3.10 / §7.9 / §8)、`docs/technical/【AGC】栏目画布底部工具栏入口矩阵-2026-09-13.md`、`docs/technical/【测试用例】AGC资源工作台V3端到端验收-2026-09-11.md`(S11 / S11a / §7.3)。
|
||||
|
||||
@@ -5203,6 +5203,7 @@
|
||||
- 处理:搜索框恢复 `display: flex`,常态 `align-self: center` 居中(总览态标题在左上、分页态栏目标题栏左右两端都有内容,只有中段是空的),与缩放 Dock 同级 `z-index: 40`;提示条同样抬到场景之上。
|
||||
- 同批修复(大纲):栏目大纲常态是 `opacity: 0.68` + `scale(0.86)` + 全透明底 + `svg { width: 0 }`,常态只剩 11px 文字,用户认不出那是分区入口;现在常态就给 Dock 底板、14px 栏目图标和 12px 栏目文字并高亮当前栏目,悬停/键盘聚焦只做同一套 token 的加强。同批把 `docs/technical/【技术方案】GameAgent资源自由画板与快速编辑-2026-08-20.md` 的「操作边界」与「验收」两条一起改成"常态即可读可点",不留描述旧收起态的文档。
|
||||
- 关联:`apps/ai-game-creator-shell/src/styles.css`、`apps/ai-game-creator-shell/src/view/project-development/index.tsx`、`apps/ai-game-creator-shell/tests/projectResourceLiveIntegration.test.tsx`。
|
||||
- 后续(2026-09-13):搜索浮层与筛选浮层合并成唯一的 `.game-resource-filter-panel`,`.game-resource-search` 的两条规则连同 Dock 上那颗滑块按钮一起删除。本条教训(可见性只能在 CSS 声明层断言)继续成立,同口径断言现在钉 `.game-resource-filter-panel`(`display: none` / `position: absolute` / z-index / Dock 锚定),见 `decision-log.md` 2026-09-13 一条。
|
||||
|
||||
## AGC 复用共享画布组件时要自己渲染 `.genarrative-image-canvas` 根类(2026-09-10)
|
||||
|
||||
@@ -5271,6 +5272,7 @@
|
||||
- 浮层必须钉住的两条:① 提示层容器整层 `pointer-events: none`、只有提示条自己 `pointer-events: auto`(横跨整行的透明盒子吃点击正是上面那条 bug);② 搜索条件不随浮层收起而清除(`searchText` 是画布状态,`type=search` 的原生 Esc 清空已被 `preventDefault` 挡掉),"当前有搜索"由 Dock 按钮的 `.is-active` 高亮表达。
|
||||
- 门禁为什么抓不到:jsdom 不做布局命中判定,`getByLabelText` 也不校验可见性;可见性与重叠只能钉声明(`tests/appSurface/project-development.suite.ts` 的 `styleRuleBody` 口径),入口另用真实 DOM 用例覆盖——`tests/appSurface/harness.ts` 的 `openResourceSearch()` 先按产品入口打开浮层,浮层没打开时 `getByLabelText` 直接抛错,堵掉"元素在但点不到"的假绿。
|
||||
- 关联:`apps/ai-game-creator-shell/src/styles.css`、`apps/ai-game-creator-shell/src/view/project-development/index.tsx`、`apps/ai-game-creator-shell/tests/appSurface/harness.ts`、`src/components/image-editor/useImageCanvasFloatingOptionDismiss.ts`。
|
||||
- 后续(2026-09-13):搜索浮层与筛选浮层合并成唯一的 `.game-resource-filter-panel`(关键词 / 所在区域 / 自定义标签),Dock 只留放大镜按钮、`.game-resource-search` 已删除,关闭改用面板自己的「点外部 + Esc」;条目里的 `right: 14px` + `bottom: 58px`(= Dock 14px + 34px + 10px 间隙、刻意不写 `top`)与「条件不随浮层收起而清除」两条口径原样沿用,harness 的 `openResourceSearch()` 现名 `openResourceFilterPanel()`,见 `decision-log.md` 2026-09-13 一条。
|
||||
|
||||
## AGC 画布侧分类筛选条已按用户要求移除,共享筛选条组件保留(2026-09-11)
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# 版本绑定口径冲突:追加写全量、替换写窄
|
||||
|
||||
更新时间:`2026-09-13`
|
||||
状态:待裁定;已确认单独立项,本轮仅做会话内止血。
|
||||
|
||||
## 现象
|
||||
|
||||
素材 A 被替换成 B 后,出现「一个标为『当前版本使用』、一个不是,替换关系不明」;追加一次版本后 A 会**重新带上该标注**。
|
||||
|
||||
## 根因(口径冲突)
|
||||
|
||||
- 版本**追加**把绑定重算为「当时全部 manifest assets」:`apps/ai-game-creator-shell/src-tauri/src/project/manifest.rs:727-738`(`game_iteration_resource_bindings`),由 `manifest.rs:792-794` 的 `append_agent_game_iteration_version_at` 使用。
|
||||
- 替换把绑定改**窄**(只留使用中素材):`apps/ai-game-creator-shell/src-tauri/src/project/version_resource_replacement.rs:409-440`。
|
||||
- 每个成功 Agent 回合都追加版本(`apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime.rs:3818-3821`);`activeVersionId` 为 `null` 时取 `versions[]` 末项(`apps/ai-game-creator-shell/src/features/project-workspace/resourceReferences.ts:226-241`)。
|
||||
- 文档口径站窄侧:`docs/technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md:1350` 称 `resourceBindings` 是「该版本使用的素材集合」。
|
||||
|
||||
## 本轮止血(不修本条)
|
||||
|
||||
替换血缘标注(源卡「已被 B 替换」/替换卡「替换自 A」)已落地,但不修口径、不持久:`.previous` 只是崩溃兜底、装盘即删(`manifest.rs:1831`),审计 `asset.version_binding.replace` 只有写侧。决策见 `docs/project-memory/shared-memory/decision-log.md` 的 2026-09-13 条目。
|
||||
|
||||
## 待裁定(二选一)
|
||||
|
||||
1. 追加时**继承上一版绑定**,改成与替换一致的窄口径:改 `game_iteration_resource_bindings` 调用口径,影响所有版本语义,需跑版本链路回归。
|
||||
2. 保持全量口径,「当前版本使用」改按替换关系投影:需新增读侧数据来源(当前替换关系无读侧)。
|
||||
|
||||
## 验证方式
|
||||
|
||||
追加一次版本后复算同一批资源卡的光环归属,把「是否回退」钉成回归。缺口:`apps/ai-game-creator-shell/tests/resourceVersionReplacement.test.tsx` 只看 IPC 载荷与 `onManifestChange` spy,观察不到光环移动。
|
||||
@@ -0,0 +1,82 @@
|
||||
# 【AGC】栏目画布底部工具栏入口矩阵
|
||||
|
||||
- 适用分支:`feat/agc-canvas-resource-workbench-v3`(AGC 客户端 `apps/ai-game-creator-shell`)
|
||||
- 需求来源:用户 2026-09-13 拍板的工具矩阵;PRD §3.10 / §7.9
|
||||
- 关联文档:`docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`、`docs/technical/【测试用例】AGC资源工作台V3端到端验收-2026-09-11.md`(S11 / S11a)、`docs/project-memory/shared-memory/decision-log.md`(2026-09-13)
|
||||
|
||||
## 1. 一句话
|
||||
|
||||
功能画布 = 资源栏目页;栏目页左下角渲染一条由栏目 `category` 决定的工具栏,图片类入口走本地 `generate_local_project_asset`,音频入口复用既有无源生成链路,上传复用 `upload_local_asset`;生成 / 上传成功后一律走既有 manifest 刷新与资源定位。
|
||||
|
||||
## 2. 入口矩阵(事实源)
|
||||
|
||||
| 栏目 | 入口(顺序即渲染顺序) |
|
||||
| ------------------------------------------------------------------------ | ---------------------------------------------------------------------------------- |
|
||||
| `ui-interaction` | 生成图片 → 生成规范(图标规范 / 自定义规范)→ 生成图标素材 → 生成 UI 设计图 → 上传 |
|
||||
| `character` | 生成图片 → 生成规范(角色规范 / 自定义规范)→ 生成角色形象 → 上传 |
|
||||
| `scene` | 生成图片 → 生成规范(自定义规范)→ 上传 |
|
||||
| `audio` | 生成背景音乐 → 生成音效 → 上传 |
|
||||
| `document` / `unclassified` / `version` / 展开态 `all` / 资源总览 `main` | 不渲染 |
|
||||
|
||||
- 事实源是 `src/features/resource-canvas/resourceCanvasBottomToolbarModel.ts` 的 `RESOURCE_CANVAS_BOTTOM_TOOLS_BY_CATEGORY`;`resolveResourceCanvasBottomTools(category)` 是唯一入口,未命中栏目返回空数组。
|
||||
- 明确不做(不得出现在工具栏):生成视频、宣发素材、生成游戏场景、画布级「选择工具 / 抓手工具」。生成视频保留在既有「生成素材」浮层入口。
|
||||
|
||||
## 3. 渲染落点与几何
|
||||
|
||||
- 挂载:`src/view/project-development/index.tsx` 里 `.game-resource-book-manager` 的**直接子节点**,与 `.game-resource-book-scene`、`.game-resource-book-zoom`(右下 Dock)、`.game-resource-book-notices` 并列。
|
||||
- 渲染判据:`resourceBookState.view === 'child'` 且 `resourceBookState.category` 命中矩阵四个栏目(`'all'` 与 `null` 都被排除)。
|
||||
- 位置:左下角(`left: 14px; bottom: 14px; z-index: 40`)。选左下角的原因:右下角是既有缩放 / 撤销 Dock,左下角是画布上唯一两者都不占的稳定空位;1280×720 与 1280×800 横屏下都不压 Dock、不挡栏目画布内容。
|
||||
- 外壳:共享 `packages/image-canvas-react` 的 `CanvasToolbar / CanvasToolbarGroup / CanvasChromeButton`(AGC 侧第一次消费这组 chrome)。样式全部落在 `src/features/resource-canvas/resourceCanvasChrome.css` 的 `.game-resource-bottom-toolbar*`。
|
||||
- 一处必要的共享样式覆盖:`.genarrative-image-canvas__toolbar` 是横向滚动容器(`overflow-x: auto`),会把它自己弹出的二级菜单(`bottom: 100% + 6px`)裁掉;消费端 `.game-resource-bottom-toolbar-strip` 改为 `overflow: visible` + `flex-wrap: wrap`,窄屏靠换行而不是横向滚动兜底。
|
||||
|
||||
## 4. 请求载荷映射
|
||||
|
||||
| 入口 | IPC | 载荷要点 |
|
||||
| --------------------- | ------------------------------- | ------------------------------------------------------------------------------------------- |
|
||||
| 生成图片 | `generate_local_project_asset` | `kind: 'image'`,比例 / 尺寸可调,默认 `1:1 · 1K` |
|
||||
| 生成角色形象 | 同上 | `kind: 'character'`,默认 `1:1 · 1K` |
|
||||
| 生成规范 → 图标规范 | 同上 | `kind: 'icon-spec'`,固定 `1:1 · 1K`;缺权威规范图时 `outputPath: 'assets/art-spec.png'` |
|
||||
| 生成规范 → 角色规范 | 同上 | `kind: 'spec'`,固定 `1:1 · 1K` |
|
||||
| 生成规范 → 自定义规范 | 同上 | `kind: 'spec'`,固定 `1:1 · 1K` |
|
||||
| 生成图标素材 | 同上 | `kind: 'art-spritesheet'`,默认 `1:1 · 1K`;前置:已登记 `assets/art-spec.png` |
|
||||
| 生成 UI 设计图 | 同上 | `kind: 'ui-prototype'`,默认 `16:9 · 1K`;前置同上 |
|
||||
| 生成背景音乐 | `derive_local_project_resource` | `editKind: 'background-music'`、`generationMode: 'create'`、`sourceMediaType: 'audio/mpeg'` |
|
||||
| 生成音效 | `derive_local_project_resource` | `editKind: 'sound-effect'`、其余同上 |
|
||||
| 上传 | `upload_local_asset` | `{ projectPath, fileName, mediaType, bytes }` |
|
||||
|
||||
`generate_local_project_asset` 的完整参数是 `{ projectPath, kind, prompt, aspectRatio, imageSize, assetName, outputPath }`(Rust `src-tauri/src/commands.rs` 的 `generate_local_project_asset`,返回 `{ id, localPath, absolutePath, manifestPath }`)。
|
||||
|
||||
## 5. 参数口径与复用程度
|
||||
|
||||
- **复用**:比例 / 尺寸选项与默认值来自网页端美术画布的纯模型 `src/components/image-editor/ImageCanvasGenerationModel.ts`(`EDITOR_IMAGE_DIMENSION_OPTIONS` / `IMAGE_MODEL_NANOBANANA2`),尺寸标签复用 `resolveEditorImageSizeLabel`;提示词上限复用 AGC 自己的 `resourceEditPromptMaxLength`(图片类默认 32000,与 Rust `LOCAL_PROJECT_ASSET_MAX_PROMPT_CHARS` 同口径);「AI 润色」复用 `ResourcePromptPolishSlot`(`polish_local_project_prompt`);面板外壳复用 AGC 的 `ThemedModal`(与「生成素材」面板同一套宿主 chrome)。
|
||||
- **收窄**:本地 IPC 的比例白名单是 `1:1 / 2:3 / 3:2 / 9:16 / 16:9`,网页端还有 `4:3`。前端做的是「主站纯模型 ∩ 本地白名单」,不是另抄一份选项表(测试钉住两者包含关系与 `4:3` 缺席)。
|
||||
- **没有复用到的**:网页端的生成 composer 子视图(`ImageCanvasBasicGenerationComposerView` / `ImageCanvasCharacterGenerationComposerView` / `ImageCanvasIconSpritesheetComposerView` / `ImageCanvasSpecGenerationPanelView` / `ImageCanvasGenerationImageOptionsView`)。原因有二:① 它们的比例选项含本地通道拒绝的 `4:3`;② 它们自带模型选择器、参考图槽位、BFF 直连(`ImageCanvasSpecGenerationPanelView` 直连 `/api/editor/llm/icon-specs/*`,AGC 无该通道),本地 IPC 既没有 `model` 也没有参考图入参,照搬会渲染一批改不了请求的假控件。因此面板是 AGC 自己的薄壳,只把**纯模型**接进来。
|
||||
- **不做的**:不改 Rust、不改 external v1 / OpenAPI、不改 `packages/`(共享包只被消费)、不复制主站 `src/components/image-editor/` 目录。
|
||||
|
||||
## 6. 前置规范图与不可用原因
|
||||
|
||||
- 判据与 Rust `canonical_art_spec_reference_at` 逐字对齐(`projectHasIconSpecReference`):manifest 里必须存在 `localPath === 'assets/art-spec.png'`、`kind === 'icon-spec'`、`mediaType` 以 `image/` 开头、`source.kind === 'canvas'` 的资产。
|
||||
- 缺前置时入口**保持可点击**(`aria-disabled="true"`,不是原生 `disabled`),点击后在工具栏上沿给 `role="alert"` 的原因说明:`需要先完成并登记图标规范(assets/art-spec.png);请先用「生成规范 → 图标规范」生成`;此时**不发任何生成请求**。
|
||||
- 「图标规范」入口在缺前置时把 `outputPath` 指向 `assets/art-spec.png`,让前置条件能由工具栏自身满足;已有权威规范图时 `outputPath` 为 `null`。这条策略对应 Rust 侧硬约束:`prepare_local_project_asset_generation` 的 `replace_existing` 固定为 `false`,指向已存在文件会被拒绝(`图片生成 outputPath 已存在,禁止静默覆盖`)。
|
||||
- 已知能力缺口(本轮不扩接口,记账备查):本地 IPC 没有 ① `model` 入参(面板因此不给模型选择)、② `specType` 入参(角色规范与自定义规范共用 `spec` 通道,靠素材名与提示词区分)、③ `replaceExisting` 入参(因此不能重写已存在的权威规范图)。
|
||||
|
||||
## 7. 音频入口收敛
|
||||
|
||||
- 音频画布的「生成背景音乐 / 生成音效」由工具栏承载:`ResourceCanvasGenerationPanelView` 新增 `kinds` prop,工具栏各自只放行自己那一种,单类型入口不再渲染类型选择器,面板标题与提交文案跟该类走。
|
||||
- 既有「生成素材」浮层入口传 `kinds={['video']}`,只保留视频;视频能力不删,只是不出现在工具栏里。
|
||||
|
||||
## 8. 验证
|
||||
|
||||
```bash
|
||||
npx vitest run apps/ai-game-creator-shell/tests/appSurface.test.ts
|
||||
npx vitest run apps/ai-game-creator-shell/tests/resourceCanvasBottomToolbar.test.tsx
|
||||
npx vitest run apps/ai-game-creator-shell/tests/resourceCanvasGenerationEntry.test.tsx
|
||||
npx vitest run apps/ai-game-creator-shell/tests/projectResourceLiveIntegration.test.tsx
|
||||
npm run agc:typecheck
|
||||
npm run check:encoding
|
||||
git diff --check
|
||||
```
|
||||
|
||||
- 模型层:四个栏目的工具集与顺序、未命中栏目返回空、「所有资源」与 `null` 返回空、二级菜单分流、比例 / 尺寸白名单收窄、前置判据、不可用原因、`outputPath` 策略。
|
||||
- 组件层:工具栏渲染与二级菜单开合、不可用入口可点击 + 原因可关闭、上传回调。
|
||||
- AppSurface 层:进入栏目出现工具栏 / 回总览与展开态消失、每个图片类入口的 `generate_local_project_asset` 载荷、前置缺失可点击说明且零请求、音频入口走 `derive_local_project_resource`、上传走 `upload_local_asset` + 配对读清单、工具栏与右下 Dock 的几何契约。
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
## 3. 分步验收表
|
||||
|
||||
> PRD 行号指向 `docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`(当前 594 行)。
|
||||
> PRD 行号指向 `docs/prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md`(当前 671 行)。
|
||||
|
||||
### A 阶段 · 从零进项目
|
||||
|
||||
@@ -47,14 +47,17 @@
|
||||
| 步骤 | 操作 | 期望结果 | 对应 PRD 条款 | 怎么判"过了" | 已知例外 / 未做项 |
|
||||
| --- | --- | --- | --- | --- | --- |
|
||||
| **S7** 栏目导航 | 点总览第 0 张「所有资源」卡 → 栏目缩略卡片进栏目 → 页内底部「下一页」 | 「所有资源」是入口(第 0 张);进入后**与栏目页是同一套画本场景**——卡片宿主同为 `.game-resource-book-scene-card`、每张卡可拖、可滚轮缩放平移,各栏目按**竖向分带**平铺在同一张画布上(展开态**不出每栏一条标题栏**,整个场景只有 `all` 那一条钉在视口上的标题栏,与栏目页"一条钉住的标题栏 + 一张平铺画布"同形);全部栏目(含空栏目)都可到达;左侧悬浮栏目大纲导航已删除;滚轮不切栏目 | L56、L512、L539–541 | `[data-resource-book-view="main"\|"child"]`、当前栏目 `.game-resource-book-scene-titlebar.is-active[data-resource-book-category]`(展开态这一条是 `data-resource-book-category="all"`,带「资源总览」收起入口;**展开态 `data-resource-book-category` 非 `all` 的标题栏一个都不该有**——空栏目曾因拿不到分带矩形而集体回落到世界原点叠成一摞)、卡片宿主 `.game-resource-book-scene-card[data-resource-book-category="<真实栏目>"]`(每张资源**恰好一个**宿主)、`[data-resource-book-section-scroll]`。**旧的独立网格判据已随分支删除**:`[data-resource-book-all-page="true"]` / `[data-resource-book-all-host="true"]` / `.game-resource-all-*` 都不应再出现。拖动的判据:在展开态拖一张卡,再进它所属栏目页看同一张卡**落在新位置**(两页共用同一份 `resource-layouts/{dependency,type}.json` 的栏目内局部坐标) | 「所有资源」卡渲染真实预览(`game-resource-book-preview-card`)**已提交**(`ea9668e4a`);旧构建只有占位摞。展开态的视口是内存态(不落盘),只有卡片坐标落盘 |
|
||||
| **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` |
|
||||
| **S8** 搜索 / 筛选 | 点右下角 `搜索资源`(放大镜)按钮或按 `Ctrl/Cmd+F` → 看到关键词 / 所在区域 / 自定义标签三字段同屏 → 输入 → Esc | Dock 上只有这一颗按钮,叫出的是**唯一**的筛选浮层(不存在第二个只放关键词的搜索浮层);浮层收起**不清筛选条件**;`type=search` 原生 Esc 清空被 `preventDefault` 挡掉;条件生效时 Dock 按钮高亮 | L55、L322(筛选只隐藏卡片、不重排坐标)、L329–330、L336–337 | `button[aria-label="搜索资源"]` 的 `aria-expanded`、`title="搜索资源(Ctrl/Cmd+F)"`、`.is-active`;浮层 `.game-resource-filter-panel`(`role="dialog"` 名「筛选资源」)内三字段 `查找素材` / `所在区域` / `自定义标签`;浮层容器 `id` 等于按钮的 `aria-controls`;Esc 后浮层不在 DOM、条件仍生效、焦点回到该按钮 | 匹配字段 = **名称 / 路径 / 媒体类型 / 任务标题**,不含 `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="选中资源:<栏目> <名>"` 按钮。**只读信息浮层已回归**(2026-09-11,工具条「信息」动作,见 `decision-log`):**C3 取消的仍只是可编辑详情面板与全屏编辑路由** |
|
||||
| **S10** 快速编辑(图片) | 选中 PNG/JPEG/WEBP 卡 → 工具条「快速编辑」→ 输入 → 提交(提示词下方另有「AI 润色」) | 先 `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`;「AI 润色」`aria-label="AI 润色"` + `aria-busy`,成功回填并出现「恢复原文」,失败保留原文并给「AI 润色失败,可重试」 | ① 服务端白名单 17 项(`editor_project.rs:4120-4141`),`character-animation`/`video`/`audio`/`document`/`code` 等**仍会 400**;② 工具条 7 个动作(重绘 / 裁剪扩图 / 去背景 / 像素完美 / 切图集 / 提取 UI 素材 / 角色动画)按 opt-in **不渲染**;③ 润色结果超 `resourceEditPromptMaxLength` 会**截断并提示「已按长度上限截断」**;④ 提示词变了(改写或润色回填)必须换 `operationId`/幂等键,否则 Rust `request_fingerprint` 判「已绑定到不同资源编辑请求」 |
|
||||
| **S11** 画布生成入口 | 画布级入口 → 选「视频 / 音效 / 背景音乐」→ 填名与提示词 → 提交(提示词下方另有「AI 润色」) | 无源新建三类素材之一 → 新卡产出并定位 | 飞书 §4(首轮由对话启动)、L122 | 提交后 `assets[]` +1;面板是独立浮层(非在当前面板下方追加);「AI 润色」走 `polish_local_project_prompt` 并带上「这是素材生成提示词(类型)」的场景约束,成功回填、失败保留原文 | ⚠️ **Issue #309 写「画布生成入口未接」,但当前代码已接三类**(`resourceCanvasGenerationModel.ts` 仅 `video`/`sound-effect`/`background-music`);图片等类型会直接报「当前资源类型不支持无源生成」;润色结果超该类型上限(背景音乐 140 / 音效 1900 / 视频 4000)会截断并提示「已按长度上限截断」 |
|
||||
| **S11** 画布生成入口(视频) | 画布级入口 → 只呈现视频 → 填名与提示词 → 提交(提示词下方另有「AI 润色」) | 无源新建视频 → 新卡产出并定位(音效 / 背景音乐已移到栏目工具栏,见 S11a) | 飞书 §4(首轮由对话启动)、L122 | 提交后 `assets[]` +1;面板是独立浮层(非在当前面板下方追加);「AI 润色」走 `polish_local_project_prompt` 并带上「这是素材生成提示词(类型)」的场景约束,成功回填、失败保留原文 | ⚠️ **Issue #309 写「画布生成入口未接」,但当前代码已接三类**(`resourceCanvasGenerationModel.ts` 仅 `video`/`sound-effect`/`background-music`);图片等类型会直接报「当前资源类型不支持无源生成」;润色结果超该类型上限(背景音乐 140 / 音效 1900 / 视频 4000)会截断并提示「已按长度上限截断」 |
|
||||
| **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` |
|
||||
| **S15a** 替换素材(含点选替换) | 选中被**当前版本**绑定的素材 → 工具条「替换素材」→ 候选弹窗(弹窗内可筛可选);再点 footer「点选替换」→ 弹窗关闭、进入画布点选态 → 在画布上直接点目标素材 | 弹窗确认与点选是**同一条**写入链路(`replace_local_project_version_resource`,载荷逐字一致):改该版本绑定、不建新版本;点选态下合法目标直接提交并自动退出点选态,非法目标零写入、**留在**点选态并在提示条上说明原因 | PRD §5.3 / §7.8 第 8 条;#309「直接替换」口径 | 点选态判据:候选弹窗 `role="dialog"` 已卸载;画布出现 `.game-resource-canvas-pick-hint`,文案含「在画布上点选要替换成的素材」与「点击空白处不会退出」,并有「取消」按钮;非法目标后提示条仍在且出现 `role="alert"`(「替换素材不兼容:分类不同」/「替换素材与源素材相同」/「替换素材未登记或已被删除」);点选期间卡片 `aria-pressed` 不变(单击只用于点选)、源素材选中与工具条不因点空白或点非法目标而丢;Esc 退出点选(画布全局 Esc 的清选中不随之触发);点选态下滚轮平移与缩放照常可用 | 点选只能点**当前画布上可见**的卡:目标在别的栏目时先切栏目(点选会话跨栏目存活,不因「收起资源」或切栏目结束);空白点击不退出、也不清画布选中 |
|
||||
|
||||
| **S11a** 栏目画布底部工具栏 | 进「UI 交互 / 角色与对象 / 场景与环境 / 音频」任一栏目 → 点左下角工具栏里的入口生成 → 「收起资源」回总览看工具栏消失 | 工具栏只在矩阵四个栏目(功能画布)渲染;图片类入口走 `generate_local_project_asset`,音频入口复用既有无源生成链路,上传复用 `upload_local_asset`;生成 / 上传成功后走既有 manifest 刷新与资源定位 | PRD §3.10、§7.9 | DOM 判据 `[data-resource-bottom-toolbar="<category>"]`(资源总览、「所有资源」展开态、文档、待归类、项目版本都**没有**这个节点);工具栏是 `.game-resource-book-manager` 的直接子节点(`closest('.game-resource-book-scene')` 为 `null`);每个入口一次 `generate_local_project_asset`,载荷逐字为 `{ projectPath, kind, prompt, aspectRatio, imageSize, assetName, outputPath }`(`kind` 映射见 PRD §7.9 第 3 条);写入命令之后有 `get_local_game_project_revision` + `get_local_game_manifest` 的配对读;缺 `assets/art-spec.png` 时「生成图标素材 / 生成 UI 设计图」仍可点击(`aria-disabled="true"` 但**不是**原生 disabled)并给出含该路径的 `role="alert"` 原因、零生成请求;音频入口面板标题即「生成背景音乐」/「生成音效」且没有类型选择器 | ① 本地通道没有 `model` / `specType` / `replaceExisting` 入参:面板不渲染模型选择器,角色规范与自定义规范共用 `spec` 通道(靠 `assetName` 与提示词区分),「图标规范」在项目已有权威规范图时不再指向 `assets/art-spec.png`(否则会被 Rust 的防覆盖校验硬拒);② 本轮不做生成视频 / 宣发素材 / 生成游戏场景 / 选择工具 / 抓手工具;③ 既有「生成素材」浮层入口只保留生成视频,音频入口只在音频栏目工具栏出现 |
|
||||
|
||||
### D 阶段 · 版本与运行
|
||||
|
||||
@@ -149,7 +152,7 @@ window.__TAURI__.core.invoke = (cmd, args) => {
|
||||
| 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` 才算数 |
|
||||
| S8 | `button[aria-label="搜索资源"]` 的 `aria-expanded`;`.game-resource-filter-panel` 是否真在 DOM(三字段同屏)。注意 jsdom 不校验可见性,只有真机截图或 `getBoundingClientRect` 才算数 |
|
||||
| S9 | `.is-selected` 数量与 `aria-pressed`;工具条容器 `role="toolbar"` 是否存在 |
|
||||
| S10 | 快速编辑失败文案;若为「当前素材类型不支持图片快速编辑」,说明源资源权威 `assetKind` 不在 17 项白名单里,这不是 V3 新缺陷 |
|
||||
| S11 | 生成入口在"没有 invoke 桥或项目未就绪"时**整体不渲染**,而不是渲染了但点了没反应 |
|
||||
@@ -183,7 +186,7 @@ api-server 是否本次重启:□ 是 □ 否
|
||||
| S5 7 栏目归属正确 | ☐ 过 ☐ 不过 | 各栏计数:______ |
|
||||
| S6 卡片预览台账(idle / failed / 空) | ☐ 过 ☐ 不过 | idle:__ failed:__ 空:__ |
|
||||
| S7 栏目导航(含「所有资源」/ 下一页 / 空栏目) | ☐ 过 ☐ 不过 | |
|
||||
| S8 搜索浮层 + 不清条件 | ☐ 过 ☐ 不过 | |
|
||||
| S8 单一筛选浮层(三字段)+ 不清条件 | ☐ 过 ☐ 不过 | |
|
||||
| S9 选择 / 多选 / 框选 / 工具条 | ☐ 过 ☐ 不过 | 实际渲染的动作:______ |
|
||||
| S10 图片快速编辑(新素材 + 血缘) | ☐ 过 ☐ 不过 | 是否 400:☐ 否 ☐ 是(原因:____) |
|
||||
| S11 生成入口(视频 / 音效 / 背景音乐) | ☐ 过 ☐ 不过 ☐ 未验 | |
|
||||
@@ -235,7 +238,7 @@ api-server 是否本次重启:□ 是 □ 否
|
||||
### 7.3 已知未做 / 已取消(不要报成缺陷)
|
||||
|
||||
1. **C6 的候选素材 / 替换关系 / 替换队列 / Agent 审核 / 批量提交整条取消**(#309)。PRD §5.3 的 `ProjectVersionResourceReplacement` 与 §3.2 的「创建下一迭代版本」**当前不实现**(2026-09-11 用户 DDL 口径「替换这块先做成直接替换」),但**保留为未来合同**:今天的实现是「改该版本的绑定,不建新版本」,验收见 PRD §7.8。
|
||||
2. **画布生成入口只覆盖 3 类**(视频 / 音效 / 背景音乐);图片等类型会直接报"当前资源类型不支持无源生成",这是设计而非缺陷。
|
||||
2. **画布生成入口分为两条**(2026-09-13 起):既有「生成素材」浮层入口只保留视频;音频(背景音乐 / 音效)与图片类(生成图片 / 生成规范 / 生成角色形象 / 生成图标素材 / 生成 UI 设计图)改由栏目画布左下角的底部工具栏承载,走本地 `generate_local_project_asset`。工具栏之外的其它无源生成类型仍会直接报"当前资源类型不支持无源生成",这是设计而非缺陷。见 PRD §3.10 / §7.9 与下面的 S11a。
|
||||
3. **工具条 7 个动作按 opt-in 不渲染**:重绘 / 裁剪扩图 / 去背景 / 像素完美 / 切图集 / 提取 UI 素材 / 角色动画;只有 `快速编辑` 与 `下载` 真渲染(宿主另附加 `UI 编辑器` / `编辑标签` / `重命名`,以及**只在素材被当前版本绑定时**出现的 `替换素材`)。
|
||||
4. **C7 只做记录层 + UI 层**:切换版本即重载当前预览;版本化资源解析机制未实现。资源替换同样不做运行时资源重映射:改绑定只改「这个版本用哪些素材」的记录,运行画面要按游戏自身引用的资源路径渲染。
|
||||
5. **「首轮进度投影」无编码级判据**,可见性由底部 Agent 状态栏承载。
|
||||
@@ -254,11 +257,11 @@ api-server 是否本次重启:□ 是 □ 否
|
||||
| --- | --- | --- | --- |
|
||||
| 资源卡「打开详情」 | PRD L61–62 要求"打开详情"与"播放 / 暂停"是可分别键盘聚焦的同级按钮 | 不存在该按钮(测试显式断言为 `null`);真正入口是 `选中资源:<栏目> <名>`,播放按钮是 `播放/暂停 <名>`;**只读信息浮层已回归**(2026-09-11 工具条「信息」动作) | 按 #309 C3(取消资源详情面板与全屏编辑路由)判,**C3 取消的仍只是可编辑详情面板与全屏编辑路由**;PRD L61–62 建议同步修订 |
|
||||
| 运行入口不可用文案 | PRD L165 显示"当前无可运行版本",但仍允许点击 | 源码中无此字符串;实现为「首个可运行原型尚未完成,运行视图暂不可用」;且播放按钮 `disabled`,只有"运行"tab 不 disabled、用 `data-unavailable` 表达 | 建议按"tab 可点 + 播放按钮置灰"判,并把 PRD L165 文案对齐实现 |
|
||||
| 画布生成入口 | #309「画布生成入口未接」 | 已接三类(视频 / 音效 / 背景音乐) | 按代码与当前产品口径判,并更新 #309 进度段 |
|
||||
| 画布生成入口 | #309「画布生成入口未接」 | 已接两条:浮层入口只做视频;音频与图片类在栏目画布底部工具栏(见 S11a / PRD §3.10) | 按代码与当前产品口径判,并更新 #309 进度段 |
|
||||
| 分类手设入口 | 旧版 PRD「用户可在分类与标签面板手动设置 category」 | 已移除;面板只编辑 tags(已随 `6bdc8bbd9` 入库) | 按新口径判 |
|
||||
| 真机 manifest 分类分布 | — | 落盘 `unclassified 57 / ui-interaction 3 / scene 1` | **读时自愈**会把 kind 可明确分类的资产显示到正确栏目,判定必须看 UI 栏目计数,不能看 manifest 文件,否则必然误报"分类不生效" |
|
||||
|
||||
## 8. 验收边界
|
||||
|
||||
- 本用例覆盖:资源总览 / 栏目分页画布、资源卡预览与本地受控读取、资源卡选择与多选、快速编辑派生(含提示词内 @ 引用)、画布生成入口(3 类)、编辑标签、重命名、下载、删除三分支、版本切换与当前使用高亮、**资源替换(入口放行判据 / 候选禁用与原因 / 格式提示 / 写入载荷 / 拒绝零副作用 / 成功后不切版本不重载预览)**、聊天 @ 引用(两页签 + 功能分类 + 多标签筛选 + 搜索)与原子 chip、AI 润色与发送前提醒、本地预览启动与退出收尾、两份布局 sidecar 台账。
|
||||
- 本用例覆盖:资源总览 / 栏目分页画布、资源卡预览与本地受控读取、资源卡选择与多选、快速编辑派生(含提示词内 @ 引用)、画布生成入口(浮层视频 + 栏目工具栏图片类与音频)、编辑标签、重命名、下载、删除三分支、版本切换与当前使用高亮、**资源替换(入口放行判据 / 候选禁用与原因 / 格式提示 / 写入载荷 / 拒绝零副作用 / 成功后不切版本不重载预览 / 弹窗与画布点选两条入口同一条写入链路)**、聊天 @ 引用(两页签 + 功能分类 + 多标签筛选 + 搜索)与原子 chip、AI 润色与发送前提醒、本地预览启动与退出收尾、两份布局 sidecar 台账。
|
||||
- 本用例不覆盖(另走专项或定向测试):双窗口 CAS 冲突、大规模 fixture 性能、素材创作无限画布阶段一至五的草稿 / 事务 / 恢复矩阵(见 `【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md` 与配套专题)、UI 编辑器子路由、主站图片编辑器回归(见 PRD §7.7)。资源替换的后端矩阵(版本绑定改写放行的六条不变式与两组互斥、绑定改写两条路径、硬门禁与提示、CAS、四条拒绝路径、读时自愈口径、审计留痕)见 `apps/ai-game-creator-shell/src-tauri/src/project/manifest/version_binding_rewrite_tests.rs` 与 `apps/ai-game-creator-shell/src-tauri/src/tests/version_resource_replacement.rs`,前端矩阵见 `apps/ai-game-creator-shell/tests/resourceVersionReplacement*.test.ts(x)`。
|
||||
|
||||
Reference in New Issue
Block a user