同步文档:生成进度面收敛为常驻可折叠侧栏、提交即关面板、定位终局化、类型选项纵向列表

- PRD §3.10:进度面由「非模态浮层」改为「常驻画布的可折叠任务侧栏」(折叠把手带在途计数、两个分栏、已完成封顶 20、左侧覆盖式、提交后自动展开);定位动作终局化(跨栏目先切栏目 / 不在投影给结论 / 3 秒有界兜底)
- PRD §3.10 面板形态:改为「点「生成」即同步关闭面板、面板内不出现阶段文案;只有点击瞬间失败才带草稿重开;受理后失败只在侧栏收口」
- 栏目画布入口矩阵 §4/§4a:同步上述两条,并把 4a 标题由「「生成任务」面板」改为「「生成任务」侧栏」
- decision-log:新增 2026-09-14 条目(进度面收敛为常驻可折叠侧栏 + 提交即关面板 + 定位终局化,含位置取舍与覆盖式理由、五条变异证据);并在「素材类型」条目补记选项形态修订(纵向单选列表、radio 语义、方向键只移焦点、行列与滚动口径、建议后续抽 PlatformRadioList)
This commit is contained in:
2026-09-14 18:50:48 +08:00
parent e6d92b2d98
commit a45172e055
3 changed files with 18 additions and 6 deletions
@@ -155,8 +155,8 @@
- 位置与层级:画布左下角(`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 `start_local_project_asset_generation``kind``image / character / spec / icon-spec / ui-prototype / art-spritesheet`;**提交即返回任务记录**,生成由 Rust 后台任务跑完写回项目,进度用 `list_local_project_asset_generations` 读回项目内账本 `.agent/runtime/asset-generation-tasks/tasks.json`);音频入口复用既有的无源生成链路 `derive_local_project_resource``generationMode: 'create'``editKind` = `sound-effect` / `background-music`);「上传」复用 `upload_local_asset`。生成 / 上传成功后一律用「配对读 `(revision, manifest)`」交给 `onManifestChange`,走既有 manifest 刷新与资源投影链路,不重算依赖图、不另写布局。
- 本地排队与进度可见:AGC 本地 durable 输出槽已按**精确动作指纹**分槽(不同 prompt / 素材名各自独立成槽,具备并行能力),但本批前端仍按「同一时刻只派发一条」排队——真并行派发需要并发收口设计(配对读 + manifest CAS + 聚焦意图互不覆盖),留待下一批;所以第一条未终态时第二条提交停在**前端本地队列**里(不调用提交 IPC,显示本地排队的「排队中。」),前一条终态后自动补发;任务状态与阶段文案(后端 `phaseDetail`)由任务账本提供,前端不拼阶段、不做百分比。工具栏另给一个**非模态**「生成任务」浮层(入口按钮 `aria-label="生成任务"`,不铺全屏遮罩、不做焦点陷阱、不参与模态遮挡判据)列出每条任务的状态 / 阶段 / 已耗时 / 素材名,已完成条目可点击定位到对应素材卡(复用既有 `pendingResourceFocusRef` 聚焦链)
- 面板形态:独立浮层(`ThemedModal`),**不在当前面板下面追加内容**;面板内不写功能说明或规则解释文案。生成在后台跑,所以**提交期间面板不锁关闭**:× / 遮罩 / Esc /「后台运行并关闭」四条路径都通;关闭只是收起表单,**不等于取消请求**(请求挂在任务上,不挂在面板生命周期上);失败仍保留草稿可重试
- 本地排队与进度可见:AGC 本地 durable 输出槽已按**精确动作指纹**分槽(不同 prompt / 素材名各自独立成槽,具备并行能力),但本批前端仍按「同一时刻只派发一条」排队——真并行派发需要并发收口设计(配对读 + manifest CAS + 聚焦意图互不覆盖),留待下一批;所以第一条未终态时第二条提交停在**前端本地队列**里(不调用提交 IPC,显示本地排队的「排队中。」),前一条终态后自动补发;任务状态与阶段文案(后端 `phaseDetail`)由任务账本提供,前端不拼阶段、不做百分比。进度面是**画布上常驻的可折叠任务侧栏**(形态对齐网页端美术画布的任务侧栏):折叠时留贴边把手并在把手上显示在途数量,展开是两个分栏「排队/生成中」与「已完成」(各带条数,「已完成」封顶 20 条 + 列表滚动 + 高度有界);每项显示状态徽标 / 阶段文案 / 已耗时 / 素材名,可「定位到素材」。侧栏非模态(不铺全屏遮罩、不做焦点陷阱、不参与模态遮挡判据),位置在画布左侧标题栏之下、**覆盖式**(不 reflow 挤窄画布视口),提交受理后自动展开。定位动作**每次点击都终局化**:能定位就定位并选中;素材在别的栏目先切栏目;不在投影里给「素材已不在项目里 / 已登记但尚未同步」的结论;3 秒内有界兜底,不允许提示条永久停在「正在定位生成的素材…」
- 面板形态:独立浮层(`ThemedModal`),**不在当前面板下面追加内容**;面板内不写功能说明或规则解释文案。**点「生成」即同步关闭面板**(不等 IPC、不等排队、不等生成),面板里**不出现**「排队中。」「正在生成。」「提交中…」这类阶段文案——阶段文案的唯一去处是任务侧栏与工具栏提示条。**只有「点击瞬间就失败」**(校验不过、权限拒绝、start IPC 立即报错)才自动重开面板并带回草稿与原因;**受理之后才失败**只在侧栏把该任务收口为失败 + 原因,不重开面板。关闭 ≠ 取消请求(请求挂在任务与账本上,不挂在面板生命周期上)。
- 参数口径:比例 / 尺寸选项来自网页端美术画布的纯模型(`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` 入参**,因此角色规范与自定义规范共用该通道,靠素材名与提示词区分。
- 前置规范图:`start_local_project_asset_generation``ui-prototype``art-spritesheet` 要求项目里已有登记并绑定当前账号的 `assets/art-spec.png``kind: icon-spec`、图片媒体类型、来源为画布)。**后台化没有改这条前置判据**:Rust 侧仍是同一口径,前端 `projectHasIconSpecReference` 仍与它逐字对齐,缺前置时对应入口**保持可点击**并给出可执行的原因与指路(仓库口径:视觉不可用但可点击说明原因,不用原生 `disabled`),原因文案包含 `assets/art-spec.png` 与「生成规范 → 图标规范」。
@@ -3,6 +3,17 @@
> 用途:记录已经确认、会影响后续开发的长期技术/产品/协作决策。短期讨论不要写在这里。
> 当前口径:历史条目的旧路径、旧版本和已退役对象只用于追溯,不构成现行实现依据;如与当前代码或 `docs/README.md` 冲突,以当前代码和最新专题文档为准。
## 2026-09-14 生成进度面收敛为「常驻可折叠任务侧栏」;提交即关面板、阶段文案只归侧栏;定位动作终局化
- 背景(验收人在真机上连报三条):① 提交按钮上渲染了后端 `phaseDetail`,「生成图片」的主按钮变成写着「排队中。」的状态胶囊;② 提交后提交面板不关、一直占着屏幕等生成,用户原话「不要显示排队中,点生成直接把窗口藏起来啊,你留个窗口意义何在」;③ 任务进度面是一个工具条按钮 + 非模态浮层,跟网页端美术画布的任务侧栏不是一个形态,用户原话「你把美术画布的照抄过来都不会吗」;④ 「定位到素材」点了没反应,提示条永久停在「正在定位生成的素材…」。
- 决策(提交面板):**点「生成」即同步关闭面板**——不等 IPC、不等排队、不等生成;面板内**不出现**任何阶段文案(主按钮文案恒为动作名)。**只有「点击瞬间就失败」**(后端校验 / 权限拒绝 / start IPC 立即报错)才自动重开面板并带回草稿与原因;**受理之后才失败**只在任务侧栏把该任务收口为失败 + 原因,不重开面板。关闭 ≠ 取消(请求挂在任务与项目内账本上,不挂在面板生命周期上)。
- 决策(进度面形态):改为**常驻画布的可折叠任务侧栏**(对齐网页端 `ImageCanvasTaskSidebarView`)——折叠时留贴边把手并在把手上显示在途数量;展开是两个分栏「排队/生成中」与「已完成」(各带条数,「已完成」封顶 20 条 + 提示「仅显示最近 N 条」);每项显示状态徽标 / 后端阶段文案 / 已耗时(前端 1s 计时)/ 素材名 + 「定位到素材」;侧栏非模态(不铺遮罩、不做焦点陷阱、不进模态遮挡判据),工具栏入口按钮常驻并显示 `生成任务 · N`,提交受理后自动展开。**原先的非模态浮层形态已删除,不留平行入口。**
- 决策(侧栏位置):挂在画布**左侧、标题栏之下、工具栏之上**,宽 300px、**覆盖式**(不 reflow 挤窄画布视口)。理由:右侧已被「智能创作」对话面板占用、顶部是栏目标题栏、底部是栏目工具栏;覆盖式不触碰画布视口数学与资源卡排布,收起即完全让出画布。若产品要求「画布被挤窄」的 flex 兄弟列形态(网页端是那种),需要改 `game-resource-book-manager` 那段布局并单独排期。
- 决策(定位终局化):根因是聚焦 effect 的依赖全是画布自身状态,**手动点定位不改其中任何一项** → effect 不重跑、`pendingResourceFocusRef` 无人消费、提示条永久停在中转文案。修法:新增聚焦请求序号并加入 effect 依赖;handler 重写为「能定位就定位并选中;素材在别的栏目先切栏目;不在投影里给『素材已不在项目里 / 已登记但尚未同步』的结论;挂 intent 后推进序号 + **3 秒有界兜底**intent 被判 invalid 时也给『定位请求已失效』」,并修掉「已聚焦过」提前返回分支不清提示的同类问题(自动落卡那条链同源)。
- 影响范围:`src/features/resource-canvas/{ResourceCanvasAssetGenerationPanelView.tsx,ResourceCanvasGenerationPanelView.tsx,ResourceCanvasAssetGenerationTasksPanelView.tsx,resourceCanvasAssetGenerationTaskModel.ts,resourceCanvasAssetGenerationQueue.ts}``src/view/project-development/index.tsx`,测试 `tests/{resourceCanvasAssetGenerationBackgroundClose.test.tsx,resourceCanvasAssetGenerationTasksPanel.test.tsx,resourceCanvasBottomToolbar.test.tsx,appSurface/project-development.suite.ts}`。**未改** IPC 形状与 Rust 生成通道、未改本地排队语义(仍单条在途)、未改 `packages/**`
- 验证方式:`appSurface.test.ts` 439 passed、定向 7 文件 93 passed、侧栏用例 7 passed;变异验证(均已实测):① 提交后不关闭面板 → 面板用例 `expected "spy" to be called 1 times, but got 0 times` 与 AppSurface `expected <section …> to be null` 红;② 去掉即时失败重开 → `Unable to find role="dialog" and name "生成 UI 设计图"` 红;③ 去掉提交后自动展开 → `Unable to find an accessible element with the role "region" and name "生成任务"` 红;④ 折叠顺手清空任务列表 → `expected '0' to be '1'` 红;⑤ 去掉聚焦请求依赖 → `expected null not to be null`(卡片从未被选中)与 `expected <span></span> to be null`(提示条仍停在中转文案,即用户报的现象)红。
- 关联文档:[栏目画布底部工具栏入口矩阵](../../technical/【AGC】栏目画布底部工具栏入口矩阵-2026-09-13.md)、[项目开发工作台 PRD](../../prd/【AI游戏创作】项目开发工作台PRD-2026-07-20.md)、[踩坑记录](pitfalls.md)。
## 2026-09-14 放开 AGC 手工图片生成的本地并发:durable 输出槽身份改为「精确动作指纹」
- 背景:AGC 手工图片生成的 durable 输出槽身份是 `run_id = slot-<sha256({outputPath, requireSlices})>`,而工具栏除「图标规范」首次外 `outputPath` 恒为 `null``requireSlices` 恒 false → 同项目所有图片类生成共用一个槽,第二条并发请求在任何远端 POST 之前就被拒(`external_generation_state.rs` 的 singleflight 报「durable 图片生成输出槽已有请求执行中」)。验收反馈里的高优问题(生图期间不能退出、也不能在生成 A 的过程中生成 B)既要前端可退出,也要后端具备并行能力。
@@ -19,6 +30,7 @@
- 背景:2026-09-11 的决策把类型选择器放进「编辑素材标签」面板(与标签同一次保存、同一条写入路径,不在工具条另开入口)。真机使用暴露两个问题:① 那排 chip 在弹窗里没有任何标题,读不出是什么;② 类型改动**没有自己的提交动作** —— 点 chip 只写本地 state,落盘发生在底部「添加」上(那是标签语义的按钮),只选类型后直接关弹窗会静默丢失。
- 决策(入口独立):新增 `ResourceTypePanel`(标题与 `ariaLabel` 均为「设置素材类型」),**选中即落盘**(一次动作一步完成),`category` 传用户选中值、`tags``gameCreationAppAssetTags(asset)` 的落盘原值。入口两处:选中卡浮动工具条「素材类型」按钮 + 信息浮层「分类」行的「设置」。
- 决策(选项形态,真机反馈后修订):选项区**必须是纵向单选列表**,一行一个(容器 `role="radiogroup"`、每项 `role="radio"` + `aria-checked`,选中态与读屏共用 `aria-checked` 并由 CSS 直接驱动;roving tabindex + 方向键移焦点、**Enter/Space 才落盘**)。真机上第一版用了共享 `PlatformSegmentedTabs``columns="threeToSix"`)→ 6 个选项挤成一行互相叠字,验收人原话「做成列表,而不是全都一条」。**有意偏离 APG**:方向键不顺手选中——本面板「选中 = 一次 CAS 写盘 + 宿主收窗」,方向键即选中会让浏览 6 个选项变成连环写盘、第一次按键就关窗。行骨架复用共享 `PlatformNavigableListItem`(未复制共享 UI、未改 `packages/**`);列表 `max-height: min(320px, 40dvh)` + 独立滚动,标题/素材名/错误提示不滚,行高 44px 移动端优先。这一版值得后续抽成 `packages/shared``PlatformRadioList`(或给 `PlatformSegmentedTabs` 加 vertical 档),本批按边界未做。
- 决策(标签面板去掉类型控件):「编辑素材标签」面板删除 chip 与 `categoryChoice` 分叉,保存时 `category: gameCreationAppAssetPersistedCategory(asset)`;「没碰过分类就回传落盘原值」这条不变量改为**结构性保证**(面板里根本没有类型控件),两条对照用例迁到新面板并保留。
- 影响范围:`src/view/project-development/{ResourceTypePanel.tsx,ResourceClassificationPanel.tsx,ResourceInfoPanelView.tsx,resourceCanvasInfoModel.ts,index.tsx}``src/features/project-workspace/resourceTypePanel.css``tests/{resourceTypePanel.test.tsx,resourceClassificationPanel.test.tsx,projectResourceLiveIntegration.test.tsx,appSurface/project-development.suite.ts}`。不改 `update_local_project_resource_classification` 的入参形状与 CAS 口径、不改读时自愈语义、无后端与 schema 变更。
- 验证方式:`resourceTypePanel.test.tsx` 新 12 条 + `resourceClassificationPanel.test.tsx` 19 条 + `appSurface.test.ts` 431 passed。变异验证(已实测):新面板 `category` 改回回传落盘原值 → 「改类型生效」用例红;标签面板改用显示口径 → 对照用例出现 `- "category": "unclassified" / + "category": "ui-interaction"`;去掉浮层判据里的新 state → 点外部串台用例红。
@@ -44,13 +44,13 @@
| 生成音效 | `derive_local_project_resource` | `editKind: 'sound-effect'`、其余同上 |
| 上传 | `upload_local_asset` | `{ projectPath, fileName, mediaType, bytes }` |
`start_local_project_asset_generation` 的完整参数是 `{ projectPath, projectId, taskId, kind, prompt, aspectRatio, imageSize, assetName, outputPath }`Rust `src-tauri/src/asset_generation_tasks.rs`),**提交即返回**一条任务记录(`taskId / status / phaseDetail / assetId / error` 等);生成在 `tauri::async_runtime::spawn` 出来的后台任务里跑,账本落在项目内 `.agent/runtime/asset-generation-tasks/tasks.json`,进度由 `list_local_project_asset_generations` 读回。截图面板不再「等生成结束」,所以提交期间 × / 遮罩 / Esc /「后台运行并关闭」都能关面板(关闭 ≠ 取消)。
`start_local_project_asset_generation` 的完整参数是 `{ projectPath, projectId, taskId, kind, prompt, aspectRatio, imageSize, assetName, outputPath }`Rust `src-tauri/src/asset_generation_tasks.rs`),**提交即返回**一条任务记录(`taskId / status / phaseDetail / assetId / error` 等);生成在 `tauri::async_runtime::spawn` 出来的后台任务里跑,账本落在项目内 `.agent/runtime/asset-generation-tasks/tasks.json`,进度由 `list_local_project_asset_generations` 读回。截图面板不再「等生成结束」,所以**点「生成」即同步关闭面板**(不等 IPC),面板内不出现阶段文案;只有「点击瞬间就失败」才带草稿重开(关闭 ≠ 取消)。
## 4a. 本地排队与「生成任务」面板
## 4a. 本地排队与「生成任务」侧栏
- **本地排队**AGC 本地 durable 输出槽按**精确动作指纹**分槽(`run_id = slot-<sha256(动作材料)>`,材料为 prompt / outputPath / 比例 / 尺寸 / assetKind / assetLabel / replaceExisting / requireSlices;不同 prompt 或素材名各自独立成槽,不再按 `outputPath` 共用;旧 `{outputPath,requireSlices}` 槽账本由同一精确动作懒迁移)。因此后端已具备并行能力,但**本批前端仍按「同一时刻只派发一条」排队**——真并行派发需要并发收口设计(配对读 + manifest CAS + 聚焦意图互不覆盖),留待下一批。所以第二条提交停在**前端本地队列**里(不调用提交 IPC,阶段显示本地排队的「排队中。」),第一条终态后由同一条循环自动补发;判据是「存在 `dispatched && 未终态` 的任务时不派发下一条」。
- **阶段文案只来自后端**:派发之后面板与任务列表渲染的都是后端账本给的 `phaseDetail`(「排队中。」/「正在生成。」/「生成已完成。」/失败原因),前端不拼阶段、不做百分比。
- **「生成任务」面板是非模态浮层**不铺全屏遮罩、不做焦点陷阱**不参与** `isResourceCanvasFloatingPanelOpen` / `resourceCanvasHostGenerationPanelOpen` 的遮挡判据;入口按钮在 `index.tsx` 工具栏区(`aria-label="生成任务"`)。已完成任务可用「定位到素材」复用既有 `pendingResourceFocusRef` 聚焦链
- **阶段文案只来自后端,且只出现在任务侧栏**:派发之后由任务侧栏渲染后端账本给的 `phaseDetail`(「排队中。」/「正在生成。」/「生成已完成。」/失败原因),提交面板已关闭、不渲染任何阶段文案;前端不拼阶段、不做百分比。
- **「生成任务」是常驻画布的可折叠侧栏**(形态对齐网页端美术画布的任务侧栏):折叠时留贴边把手并在把手上显示在途数量;展开是两个分栏「排队/生成中」与「已完成」(各带条数,「已完成」封顶 20 条 + 列表滚动 + 高度有界);位置在画布左侧标题栏之下、**覆盖式**(不 reflow 挤窄画布视口)。侧栏非模态(不铺全屏遮罩、不做焦点陷阱**不参与** `isResourceCanvasFloatingPanelOpen` / `resourceCanvasHostGenerationPanelOpen` 的遮挡判据),提交受理后自动展开;工具栏入口按钮常驻并显示 `生成任务 · N`。每项的「定位到素材」**每次点击都终局化**(含跨栏目先切栏目、不在投影里给结论、3 秒有界兜底),不允许提示条永久停在「正在定位生成的素材…」
## 5. 参数口径与复用程度