文档:恢复音频生成共享分流并停止跟踪本地方案
新增共享音频 Composer 重构方案并收口权威设计 更新实施记录、决策日志、踩坑记录与文档索引 停止跟踪 local-docs 中的 T5 实施方案并保留本地文件
This commit is contained in:
@@ -0,0 +1,171 @@
|
||||
# 音频生成 Composer 恢复共享分流方案
|
||||
|
||||
日期:`2026-08-06`
|
||||
|
||||
状态:`已规划,尚未实施`
|
||||
|
||||
## 一、目标
|
||||
|
||||
图片画布的音效与背景音乐恢复使用同一个音频 composer。`ImageCanvasGenerationComposerView.tsx` 内只保留一个 `ImageCanvasAudioGenerationComposerView`,并在组件内定义:
|
||||
|
||||
```ts
|
||||
const isSoundEffect = dialog.mode === 'audio-sound-effect';
|
||||
```
|
||||
|
||||
`isSoundEffect === true` 渲染现有 SFX 分支,`isSoundEffect === false` 渲染现有 BGM V1 分支。删除完整的独立 BGM composer 文件,但保留确有独立职责的 BGM 纯模型、助手 controller 和预设跑马灯组件。
|
||||
|
||||
这次只调整前端视图组织方式,不改变任何生成契约、业务规则、请求时序、计费或持久化语义。
|
||||
|
||||
## 二、范围与非目标
|
||||
|
||||
### 2.1 必须保留的 BGM 能力
|
||||
|
||||
- `gpt_description_prompt` 可见原文、Unicode `White_Space` canonicalization 和 200 字生成限制。
|
||||
- 30 个预设、字符计数、AI 补全、一键简化和单层交换式撤销。
|
||||
- AI 处理锁、同步正式提交锁、`generating` 锁和迟到响应隔离。
|
||||
- 稳定 dialog ID、账号 / 项目 scope 校验和按 dialog ID 写回。
|
||||
- 固定 `Suno`、当前动态泥点价格和隐藏 `make_instrumental` 的现状。
|
||||
|
||||
### 2.2 必须保持不变的现有 SFX 能力
|
||||
|
||||
- 固定 Vidu `audio1.0`,Prompt 同值映射到现有 `prompt + sound` 请求字段。
|
||||
- `2–10` 秒、步长 `1` 秒、默认 `5` 秒。
|
||||
- 当前 Prompt 规范化、全空白时回退“游戏音效”、1500 字限制、价格和失败态。
|
||||
- 现有提交 callback、生成占位和完成链路。
|
||||
- SFX 中不出现 Suno、BGM 预设、AI 补全、一键简化、BGM 字符计数或撤销。
|
||||
|
||||
### 2.3 本次明确不做
|
||||
|
||||
- 不实现 SFX V2 独有的一键优化、自动中译英、ElevenLabs、自动时长、30 秒、Loop 或 SFX 预设。
|
||||
- 不修改 BGM 或 SFX 需求原文。
|
||||
- 不修改 BFF、队列、`platform-audio`、External v1、OpenAPI、SpacetimeDB schema、计费或重试。
|
||||
- 不把 BGM Prompt controller 泛化为音频通用 controller。
|
||||
- 不新建配置驱动的 composer 框架、第二套音频组件或其它顺带重构。
|
||||
|
||||
## 三、当前问题
|
||||
|
||||
当前 `ImageCanvasGenerationComposerView.tsx` 分别渲染 SFX 内部组件和 `ImageCanvasBackgroundMusicGenerationComposerView.tsx`。这层完整视图拆分让同一个音频入口形成两套 composer 边界,而 SFX V2 需求已经表明预设、AI 写回、撤销和锁定等交互并非 BGM 永久独占,继续用“BGM 专属交互较多”作为完整组件分叉理由不再成立。
|
||||
|
||||
同时,最近一次合并把共享架构中的 `isSoundEffect` 条件表达式带回了当前 SFX-only 组件,却没有带回变量定义,形成 `isSoundEffect is not defined`。该问题不是增加一个局部常量后就可以收口的长期架构问题;本次重构应恢复单一音频组件,使变量与它控制的两个分支重新处于同一组件边界。
|
||||
|
||||
## 四、目标结构
|
||||
|
||||
```text
|
||||
ImageCanvasEditorView
|
||||
└─ useImageCanvasGenerationSurface
|
||||
└─ ImageCanvasGenerationComposerView
|
||||
└─ ImageCanvasAudioGenerationComposerView
|
||||
├─ isSoundEffect === true → 现有 SFX UI 与行为
|
||||
└─ isSoundEffect === false → 现有 BGM V1 UI 与行为
|
||||
└─ ImageCanvasBackgroundMusicPresetMarquee
|
||||
```
|
||||
|
||||
继续保留的 BGM 专项模块:
|
||||
|
||||
- `ImageCanvasBackgroundMusicPromptModel.ts`:canonicalization、计数、动作资格和 dialog 类型收窄。
|
||||
- `useImageCanvasBackgroundMusicPromptAssist.ts`:按 dialog ID 隔离的异步助手状态与提交锁。
|
||||
- `ImageCanvasBackgroundMusicPresetModel.ts`:30 个预设与追加规则。
|
||||
- `ImageCanvasBackgroundMusicPresetMarquee.tsx`:展开、滚动、hover、触摸和 reduced-motion。
|
||||
|
||||
删除的完整视图模块:
|
||||
|
||||
- `ImageCanvasBackgroundMusicGenerationComposerView.tsx`
|
||||
- `ImageCanvasBackgroundMusicGenerationComposerView.test.tsx`
|
||||
|
||||
删除测试文件不等于删除覆盖;其中全部用例必须迁入总 composer 测试。
|
||||
|
||||
## 五、实现边界
|
||||
|
||||
### 5.1 单一渲染入口
|
||||
|
||||
`ImageCanvasGenerationComposerView` 对 `audio-sound-effect` 与 `audio-background-music` 只保留一个渲染条件和一个 `ImageCanvasAudioGenerationComposerView` 调用。组件内部以 `isSoundEffect` 选择两棵现有表单子树,不为本次重构新造配置层。
|
||||
|
||||
共享组件使用基于 dialog ID 与 mode 的稳定 `key`。连续切换 BGM dialog 时,预设展开、滚动、hover 和触摸状态不得继承;从 SFX 切到 BGM 时,音效时长菜单状态也不得泄漏。
|
||||
|
||||
### 5.2 Hook 与类型安全
|
||||
|
||||
- React Hook 不得放进 `isSoundEffect` 条件分支。`useState`、`useRef`、`useId` 和 `useImageCanvasFloatingOptionDismiss` 保持固定调用顺序。
|
||||
- `GenerateDialogState` 不是可判别联合。BGM 分支继续通过 `toBackgroundMusicGenerationDialog` 取得带稳定 ID 的 `BackgroundMusicGenerationDialogState`。
|
||||
- BGM 缺少稳定 ID 或 `backgroundMusicPromptAssist` 时失败关闭,不渲染不完整的 BGM 表单;SFX 不依赖这两个条件。
|
||||
|
||||
### 5.3 两套状态写回不得合并
|
||||
|
||||
- SFX 继续走现有 `setGenerateDialog` 路径,保持按 mode 更新、失败态复位和时长写回语义。
|
||||
- BGM 继续走 `updateCanvasGenerationDialogById(dialog.id, updater)`,不能降级成只比较 mode。助手响应、预设、撤销和提交锁仍按稳定 dialog ID 隔离。
|
||||
- `backgroundMusicPromptAssist` 可以继续由 surface 传给共享 composer,但只允许 BGM 分支读取或调用。
|
||||
|
||||
### 5.4 锁定与提交资格不得串用
|
||||
|
||||
|分支|锁定条件|生成资格|
|
||||
|---|---|---|
|
||||
|SFX|沿用现有 `dialog.status === 'generating'`|沿用现有 SFX 提交入口和默认 Prompt 规则|
|
||||
|BGM|AI processing、`submitting` 或 `generating` 任一成立|沿用 canonical Prompt 的有效字符与 `1–200` code point 规则|
|
||||
|
||||
BGM 使用 `PlatformTextField + readOnly + aria-invalid`;SFX 继续使用 `AutoGrowTextArea + disabled`。本次不统一这两个输入控件,也不改错误、可访问名称或按钮文案。
|
||||
|
||||
## 六、预计代码改动
|
||||
|
||||
|文件|最小改动|
|
||||
|---|---|
|
||||
|`ImageCanvasGenerationComposerView.tsx`|恢复共享 `ImageCanvasAudioGenerationComposerView` 与 `isSoundEffect`;移入现有 BGM JSX、常量和依赖;删除独立 BGM import 与双渲染入口|
|
||||
|`ImageCanvasGenerationComposerView.test.tsx`|迁入独立 BGM composer 的全部测试,并增加双 mode 隔离与切换回归|
|
||||
|`ImageCanvasBackgroundMusicGenerationComposerView.tsx`|删除|
|
||||
|`ImageCanvasBackgroundMusicGenerationComposerView.test.tsx`|覆盖迁完后删除|
|
||||
|`useImageCanvasBackgroundMusicPromptAssist.ts`|只修正指向独立 composer 的历史注释;不改 controller 行为|
|
||||
|
||||
以下生产文件预计不改:`useImageCanvasGenerationSurface.tsx` 的现有 props 接线、BGM Prompt / 预设模型、预设跑马灯、submission workflow、submission model、dialog model、`src/index.css` 的现有 BGM 样式,以及全部后端代码。
|
||||
|
||||
如实施时发现必须超出该清单才能保持现有行为,应先停下并重新确认边界,不能借本次组件归并顺带重构。
|
||||
|
||||
## 七、测试迁移与回归矩阵
|
||||
|
||||
### 7.1 BGM 原覆盖完整迁移
|
||||
|
||||
独立组件测试中的下列覆盖必须逐项迁入 `ImageCanvasGenerationComposerView.test.tsx`:
|
||||
|
||||
- canonical preview 计数、超限展示和不改写输入框。
|
||||
- 0 / 1 / 2 个有效字符及 200 / 201 / 2000 / 2001 边界。
|
||||
- 补全、简化、撤销和预设按正确 dialog ID 路由。
|
||||
- `preparePreset` 拒绝时不写回,预设成功时沿用追加和清快照语义。
|
||||
- 助手错误与生成错误的展示顺序。
|
||||
- Suno、泥点价格、生成允许 / 拒绝。
|
||||
- `completing`、`simplifying`、`submitting`、`generating` 锁定。
|
||||
- 完整单层撤销按钮矩阵和现有可访问属性。
|
||||
|
||||
### 7.2 mode 隔离与切换
|
||||
|
||||
- SFX 渲染时不存在 BGM 控件,也不存在本次明确排除的 SFX V2 控件。
|
||||
- BGM 渲染时不存在 Vidu 和音效时长控件。
|
||||
- SFX 仍保持 `audio1.0`、2–10 秒、默认 5 秒、当前价格和既有提交参数。
|
||||
- BGM dialog A 展开预设后切到 dialog B,局部展开与滚动状态不继承。
|
||||
- SFX 与 BGM 相互切换时,菜单、锁和 Prompt 助手状态不跨 mode 泄漏。
|
||||
- 缺少稳定 ID 或 controller 的 BGM 失败关闭;同样条件不影响 SFX 正常渲染。
|
||||
|
||||
### 7.3 最小验证命令
|
||||
|
||||
```powershell
|
||||
npm run test -- src/components/image-editor/ImageCanvasGenerationComposerView.test.tsx src/components/image-editor/ImageCanvasBackgroundMusicPromptModel.test.ts src/components/image-editor/ImageCanvasBackgroundMusicPresetModel.test.ts src/components/image-editor/ImageCanvasBackgroundMusicPresetMarquee.test.tsx src/components/image-editor/useImageCanvasBackgroundMusicPromptAssist.test.tsx
|
||||
npm run test -- src/components/image-editor/useImageCanvasGenerationSurface.test.tsx src/components/image-editor/useImageCanvasGenerationSubmissionWorkflow.test.tsx
|
||||
npm run typecheck
|
||||
npm run check:encoding
|
||||
git diff --check
|
||||
```
|
||||
|
||||
删除旧测试文件后,还要确认测试收集清单中不再引用它。若本次改动触发其它现有图片画布测试失败,只修复由共享 composer 归并直接造成的回归,不扩大到无关模块。
|
||||
|
||||
## 八、实施顺序
|
||||
|
||||
1. 在总 composer 内恢复共享音频组件、`isSoundEffect` 和单一音频渲染入口。
|
||||
2. 原样迁入 BGM 分支,保留按 ID 写回、锁定、错误顺序、Suno 与预设行为。
|
||||
3. 迁移独立 BGM 组件测试,并补齐 SFX/BGM 隔离与切换用例。
|
||||
4. 删除独立 BGM composer 及其测试文件,修正相关注释与文档引用。
|
||||
5. 运行定向测试、typecheck、编码检查和差异检查;实现完成后再把实际结果补入 T6 后续记录。
|
||||
|
||||
## 九、完成定义
|
||||
|
||||
- 两个音频 mode 均从同一个 `ImageCanvasAudioGenerationComposerView` 渲染,且组件内存在唯一的 `isSoundEffect` 分流。
|
||||
- 独立完整 BGM composer 文件和独立测试文件已删除,原测试覆盖无遗漏地迁入总 composer。
|
||||
- BGM V1 所有已交付能力与正式提交语义不变。
|
||||
- 现有 SFX UI、Prompt、Vidu、时长、价格、校验和提交链路无回归。
|
||||
- 没有实现任何 SFX V2 独有功能,没有修改需求原文或后端契约。
|
||||
- 规定的定向测试、typecheck、编码检查和 `git diff --check` 全部通过。
|
||||
@@ -6614,3 +6614,10 @@
|
||||
- 遗留(建议单开,不在本次范围):`loadProjectCoverImage` 里无 timeout / 无 AbortSignal 的 `new Image()` 本身仍是隐患,自动保存路径一样会踩。本次只是把它移出生成链的关键路径,没有消除它。
|
||||
- 影响范围:`useImageCanvasProjectPersistence.ts` 的 `flushProjectPersistence`。不改服务端、不改契约。
|
||||
- 验证方式:既有用例「flush 等待封面缓存」翻转为「flush 不等封面、但封面链照常跑完并完成上传与资源登记」;新增「封面永不 settle 时 flush 仍返回」——用永不 resolve 的 blob 模拟 `new Image()` 不 settle,并断言 `createProjectCoverSnapshotBlob` 确实被调用过以防用例空过。已实证:回退修复后新用例报 `expected 'false' to be 'true'`。运行 `npx vitest run src/components/image-editor src/components/platform-entry src/services`(101 文件 / 1241 项)、`npm run typecheck`、`npm run lint:eslint`、`npm run check:encoding`。
|
||||
|
||||
## 2026-08-06 音效与背景音乐恢复共享音频 Composer
|
||||
|
||||
- 决策:图片画布的 `audio-sound-effect` 与 `audio-background-music` 只保留一个 `ImageCanvasAudioGenerationComposerView`,组件内以 `isSoundEffect = dialog.mode === 'audio-sound-effect'` 分流。撤销的是完整 `ImageCanvasBackgroundMusicGenerationComposerView` 这一层视图拆分,不撤销 BGM Prompt 纯模型、助手 controller、预设模型或预设跑马灯的独立职责。
|
||||
- 业务隔离:共享组件不等于共享规则。SFX 继续使用 Vidu `audio1.0`、2–10 秒、默认 5 秒、现有 Prompt 回退、1500 字限制、价格和提交链路;BGM 继续使用 canonical Prompt、200 字生成限制、30 个预设、AI 补全 / 简化、单层撤销、提交锁和 Suno。BGM 按 dialog ID 写回,SFX 继续走现有 `setGenerateDialog`,两条路径不得互换。
|
||||
- 非目标:本次只规划视图归并,不实现 SFX V2 的 ElevenLabs、中译英、自动时长、30 秒、Loop、一键优化或预设,不修改任何后端、External v1、Schema、计费或需求原文,也不新建配置驱动的 composer 框架。
|
||||
- 实施状态:当前代码仍保留独立 BGM composer,待按 `docs/project-memory/plans/【前端重构】音频生成面板恢复共享分流方案-2026-08-06.md` 落地。完成前不得把本决策写成已通过的实现结论;完成时必须迁移全部独立组件测试并同时证明 SFX 与 BGM 两个分支无状态和控件泄漏。
|
||||
|
||||
@@ -4201,3 +4201,10 @@
|
||||
- 原因:两个跨模块测试读写同一进程全局状态,却没有共用隔离边界;只给 accept 后取得的 stream 设置 read timeout 无法约束 accept 本身,payload 读取也缺少总 deadline。
|
||||
- 处理:全部全局 sink 测试共用一把 test-only 串行锁,并由 RAII guard 在 `Drop` 中无条件清空;测试统一使用 `manifest_invalidation_sink_isolation_` 前缀。relay fixture 对 accept 和 payload 分别使用非阻塞轮询与总 deadline,不使用固定 sleep;生产 loopback、token、连接 / 写入超时和 payload 大小校验保持不变。
|
||||
- 验证:用 `--test-threads=2` 重复运行统一 filter,覆盖正常 relay、无事件 accept 超时、不完整 payload 超时、panic 展开清理,以及 GUI owner attach 配置与 guard 清理。
|
||||
|
||||
## 共享音频 Composer 架构冲突不能按单行选边(2026-08-06)
|
||||
|
||||
- 现象:master 的音频 composer 同时承载 SFX 与 BGM,并在组件内定义 `isSoundEffect`;功能分支把 BGM 拆成独立组件后,原组件变成 SFX-only。合并时只把 master 的条件占位表达式带回 SFX-only 组件,没有带回变量定义,最终在测试渲染阶段报 `isSoundEffect is not defined`。
|
||||
- 原因:冲突两侧代表不同组件架构,逐行保留看似有用的 JSX 会把一个架构中的局部条件拼进另一个架构。import 排序、格式检查和只覆盖单一 mode 的测试都不能证明这种组合成立。
|
||||
- 处理:先确定权威组件边界,再按完整调用链解决冲突。图片画布音频入口当前决策是恢复一个共享 `ImageCanvasAudioGenerationComposerView`,由组件内 `isSoundEffect` 分流;BGM/SFX 的 validator、写回、锁和提交契约仍分别保持。不要只补一个常量后继续维持已经废弃的双 composer 边界。
|
||||
- 验证:同时渲染 `audio-sound-effect` 与 `audio-background-music`,覆盖两个 mode 的正向控件和互斥负向断言、dialog / mode 切换、BGM 稳定 ID 与 controller 缺失的失败关闭,并运行 `ImageCanvasGenerationComposerView.test.tsx` 与 typecheck。
|
||||
|
||||
Reference in New Issue
Block a user