@memef1f1y/dsh-plugin-tts 0.6.4 → 0.6.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.yml +51 -51
  2. package/.github/ISSUE_TEMPLATE/config.yml +2 -2
  3. package/.github/ISSUE_TEMPLATE/feature_request.yml +28 -28
  4. package/.github/pull_request_template.md +7 -7
  5. package/.github/workflows/contrib-triage.yml +66 -66
  6. package/.github/workflows/linked-issue.yml +21 -21
  7. package/.github/workflows/publish.yml +63 -0
  8. package/.github/workflows/stale.yml +30 -30
  9. package/.github/workflows/test.yml +33 -33
  10. package/.github/workflows/welcome.yml +22 -22
  11. package/ANALYSIS.md +205 -205
  12. package/PR-REVIEW.md +74 -74
  13. package/README.md +335 -335
  14. package/TODO.md +34 -23
  15. package/docs/HOST-CONTRACT.md +36 -36
  16. package/docs/RVC-GUIDE.md +390 -390
  17. package/docs/USER-GUIDE.md +389 -389
  18. package/docs/acceptance-checklist.md +147 -147
  19. package/docs/adaptive-chunked-playback.md +194 -194
  20. package/docs/macos-test-prompt.md +230 -230
  21. package/docs/windows-gpu-verify-prompt.md +151 -151
  22. package/lib/client.js +7164 -7164
  23. package/lib/index.mjs +3374 -3371
  24. package/package.json +51 -52
  25. package/rvc-server.py +564 -564
  26. package/tests/README.md +45 -45
  27. package/tests/client-load.mjs +406 -406
  28. package/tests/e2e-compact-index.mjs +86 -86
  29. package/tests/e2e-index-ab.mjs +138 -138
  30. package/tests/e2e-real-rvc.mjs +122 -122
  31. package/tests/i18n-keys.mjs +82 -82
  32. package/tests/package-runtime-patch.mjs +88 -88
  33. package/tests/rvc-server-live.mjs +230 -230
  34. package/tests/smoke.mjs +1284 -1284
  35. package/tools/gen-ui-preview.mjs +174 -174
  36. package/tools/mac-verify.sh +158 -158
  37. package/tools/package-runtime.py +214 -214
package/ANALYSIS.md CHANGED
@@ -1,205 +1,205 @@
1
- # dsh-plugin-tts 优化 · 升级 · 新增 路线图(分析 + 提案)
2
-
3
- > 结论:本插件在"长文分块无缝播放 / RVC 变声 / 音色包分发 / 便携运行时"上已领先同类;
4
- > 真正的差距在 **状态落盘、可发现性、UI/UX 体验护栏(逐句高亮、速度/暂停、选区朗读、快捷键、a11y)**,
5
- > 以及少量**边界健壮性**(显式取消、音色下线自愈、自动播放解锁、网络退避)。
6
-
7
- ## 0. 现状盘点
8
-
9
- - **路由**:`/dsh-tts-api/speak`(合成/分块队列)、`/dsh-tts-audio/<id>`、`rvc-next`(逐块拉取)、
10
- `rvc-files`、`rvc-compact-index`、`rvc-packs`/`-installed`/`-install`/`-uninstall`/`-progress`、`diagnose`。
11
- 零依赖 worker 镜像 node-edge-tts 协议(`Sec-MS-GEC`、`Path:audio` 帧、1006 重试一次)。
12
- - **Client**:`shell.overlay` 隐藏 `<audio>` 宿主 + 4 个 slot(shell.overlay / input.left /
13
- assistant-actions / settings.plugins.tab);Web Audio `start(prevEnd)` 采样级拼接;i18n 双层持久化。
14
- - **自适应分块**:probe→ratio 分档(6–20s/预热2–4)→`calibration.json`(7天+设备指纹);短文本≤12s走单 URL(上传原声模式已下线,见 §14)。
15
- - **音色包生态**:manifest schema 2、sha256 校验、代理、多索引变体、卸载、便携运行时。
16
- - **已知限制(README)**:① 音色/自动朗读开关**内存态、不落盘**;② 音频写 OS 临时目录;③ 中英文视觉只能真机确认。
17
-
18
- ## 1. 同类产品调研(基于既有领域知识)
19
-
20
- | 类别 | 代表 | 本插件差距 / 可借鉴 |
21
- |---|---|---|
22
- | Edge TTS 桌面/CLI | edge-tts、各类 GUI | 只有"选音色→播放";本插件远超。可借鉴**速度控制、导出 MP3、逐句高亮** |
23
- | 浏览器朗读扩展 | Read Aloud、Speechify | 强项:**卡拉OK式逐句高亮 + 语速 + 选区朗读 + 快捷键**——本插件最缺 |
24
- | 桌面朗读工具 | Balabolka、NaturalReader | **格式朗读、批量朗读、波形可视化** → 批量读多消息、导出 |
25
- | 聊天类 App 朗读 | ChatGPT/Claude/豆包/Kimi | **打断/续播策略、播放进度圈** |
26
- | 开源多后端 TTS | Piper/espeak-ng/ChatTTS/CosyVoice/GPT-SoVITS | 本插件只有 Edge;本地高质量 TTS 可作底噪/RVC 补充 |
27
- | RVC 生态 | RVC WebUI、AI Hobbyist、SUPERSING | **批量转换、音色 A/B 对比、成品导出** |
28
-
29
- ## 2. 新增模块(能力扩展)
30
-
31
- - **M1. TTS 后端抽象层(Provider 接口)**:Edge/RVC 迁入可插拔接口,新增本地 Piper/espeak、OpenAI/Azure、离线中文 TTS(ChatTTS/CosyVoice),让 RVC 底噪不再依赖在线。
32
- - **M2. 原生流式合成(SSE/WebSocket)**:Edge 长文"边合成边推",降低 TTFP,复用 Web Audio 拼接管线。
33
- - **M3. 导出/下载/复读缓存**:音频附件下载(原声+变声对);文本 hash+voice 的 LRU 缓存,重播不重合成。
34
- - **M4. 音色包生态增强**:多仓库并存、下载前试听、A/B 对比、离线 zip 导入、断点续传。
35
- - **M5. 多模型库与参数画像**:每个模型记住最优参数(f0/index_rate/原声音色/spk),切换即载入。
36
-
37
- ## 3. 功能升级
38
-
39
- - **F1. 设置落盘(最高优先)**:音色/自动朗读/provider/音量语速持久化(UI→localStorage,Host→`~/.dsh/tts-rvc/settings.json`),消 README Known-limits ①;加"恢复默认"。
40
- - **F2. 自动朗读策略**:每条/仅长消息自动读、新消息打断或排队、不打断手动读。
41
- - **F3. 朗读播放器升级**:暂停/继续、倍速(0.5–2x)、上/下一条、mini 进度条。
42
- - **F4. 选区/从光标朗读**:朗读选中、从第 N 段继续。
43
- - **F5. 诊断增强**:音色表新鲜度自检、calibration 信息、RVC 健康、网络/代理连通。
44
-
45
- ## 4. UI/UX 设计优化(基于真实截图)
46
-
47
- 1. **Auto-read 图标歧义 + 状态不可见**:喇叭易被误读为语音输入(ASR)、开关态只能靠 hover tooltip。→ 换独立图标(耳机/波形+A 徽标)、明显视觉态(填充/斜杠/pill"自动朗读·开")。
48
- 2. **朗读进度显性化**:tooltip 之外,加**逐句/逐段高亮** + 细进度条。
49
- 3. **Tooltip 主题化**:统一 `--dsw-*` token,避免系统默认白底黑字;所有图标补 tooltip。
50
- 4. **设置面板信息密度**:值放把手旁小框、统一缩放/单位、加"恢复默认"、子导航(Edge/RVC/音色包/诊断)。
51
- 5. **首次上手引导**:设置面板内嵌分 OS 启动步骤 + ✓ 就绪清单。
52
- 6. **无障碍(a11y)**:aria-label/aria-pressed、focus 样式、键盘快捷键(Space 暂停/续播、S 停止、R 朗读)。
53
-
54
- ## 5. 边界处理与健壮性
55
-
56
- - **E1. 显式取消/打断释放资源**:停止时立即通知 Host 提前结束 job 串行链并释放 GPU/内存(不必等 10 分钟惰性回收)。
57
- - **E2. 音色下线自愈**:`1007` 时动态剔除 + 自动回退默认音色,而非只报错。
58
- - **E3. 自动播放解锁**:用户首次交互即预热 `AudioContext`。
59
- - **E4. 网络抖动**:Edge 1006 改指数退避重试(≤3)+超时;RVC /convert 超时;`rvc-next` 短暂重试再报错。
60
- - **E5. 服务掉线中播**:RVC 中途断→暂停等待/自动重连,恢复后续播。
61
- - **E6. 消息形态边界**:空白/emoji-only 判定;超长消息限并发、入队。
62
- - **E7. 磁盘与清理**:显式缓存清理入口 + 满盘告警;音频 LRU 上限。
63
- - **E8. 校准健壮性**:`calibration.json` 多机同步/备份、模型更换重校、2 分钟兜底保留。
64
- - **E9. 包安装安全**:下载 size 预检、断点续传、in-flight 恢复。
65
- - **E10. 竞态回归**:换消息/切会话/刷新/新消息 5 态矩阵持续强化。
66
-
67
- ## 6. 测试与验收
68
-
69
- - 沿用现有分层(smoke/live/i18n/client-load/patch/make-pack)。
70
- - 新增:E1 取消、E3 解锁、E4 退避;M1 provider 契约;M3 导出+缓存;F1 落盘往返+恢复默认;UI 新组件 mock 渲染 + a11y 断言;i18n 新键并入回归。
71
- - 验收:`npm run test:all` 全绿;每个新能力有绿色用例;不破坏现有 5 态矩阵;README/zh、RVC-GUIDE、adaptive-chunked-playback 文档同步。
72
-
73
- ## 7. 分期
74
-
75
- - **Phase 1(低风险先做)**:F1 设置落盘+恢复默认 · E1 显式取消 · E3 自动播放解锁 · E2 音色下线自愈 · 部分 a11y(aria/快捷键) · F5 诊断增强的一部分 · 音频 LRU 缓存(F 前置)。
76
- - **Phase 2(UX 体验护栏)**:auto-read 图标+状态重做 · 逐句高亮/进度显性化 · mini 播放器 · tooltip 主题化 · 面板密度整理 · 首次上手引导。
77
- - **Phase 3(能力扩展)**:M1 Provider 抽象+本地 Piper · M2 原生流式 · M3 导出/下载 · F4 选区朗读。
78
- - **Phase 4(规模/生态)**:M4 多仓库+A/B+离线安装+断点续传 · M5 多模型库 · E8 校准多机同步 · 便携运行时 Linux 实测 · E4/E5 网络与服务健壮性。
79
-
80
- ## 8. 假设与前置说明
81
-
82
- - 本文件即分析交付物;实时 web_search 在本环境因无 API Key 不可用,同类调研基于既有领域知识。
83
- - 所有改动沿用现有模式(Host 路由 + `ctx.effect` 清理、零依赖 worker、client sprite+rpc、i18n 双层、dsh bundle/patch 装配),不引入新框架。
84
-
85
- ## 9. Phase 1 落地记录(已完成,test:all 全绿)
86
-
87
- - **E1 显式取消**:`/dsh-tts-api/rvc-next?cancel=1` + Host `cancelJob`,client `stopSpeaking` 即时通知释放本地 GPU/内存。(`tests/smoke.mjs` 取消用例)
88
- - **E3 自动播放解锁**:apply 内首次手势 resume Web Audio 上下文 + 播放静音片段解锁 `<audio>`。
89
- - **E2 音色下线自愈**:`pruneRemovedVoice()` 检测 `1007/unsupported voice`,剔除音色并回退默认;设置面板过滤已移除音色;新增 `voice.removed` i18n 键。
90
- - **F1 设置持久化**:音色/自动朗读/提供者/RVC 配置写入 `localStorage['dsh-tts-settings']`,加载时恢复;面板新增「恢复默认设置」按钮;暴露 `window.__dshTtsSettings` 测试钩子。(`tests/client-load.mjs` round-trip 测试)
91
- - **a11y**:`Esc`/`S`(非输入框内)停止朗读的全局快捷键。
92
- - **音频 LRU 缓存**:确认进程内 `cache`/`files` LRU 缓存(60/300 上限);缓存命中时校验底层文件是否被系统清理,失效则回退重合成(避免 404)。(`tests/smoke.mjs` 重复朗读复用 URL 用例)
93
- - 运行:`smoke 47 · live 18 · patch 4 · i18n 6 · client-load 28`,`npm run test:all` 全绿;README/README.zh「边界行为」「已知限制」已同步。
94
-
95
- > 说明:音频缓存为**进程内**缓存(音频在 OS 临时目录,由系统清理);完全跨重启的磁盘级缓存列为后续项。
96
-
97
- ## 10. Phase 2 落地记录(已完成,test:all 全绿)
98
-
99
- - **P2-1 auto-read 图标+状态重做**:换用独立的 `HeadphonesIcon`(耳机,区别于"语音输入/麦"语义);自动朗读从仅有喇叭图标改为**带文字的 pill**(`自动朗读` + 状态圆点 + 品牌色高亮 + `aria-pressed`);新增 `autoRead.label` i18n 键。`tests/client-load.mjs` 增加"渲染为 labeled pill"结构化断言。
100
- - **P2-2 朗读进度显性化**:分块朗读时在消息操作行显示可见计数 pill「第 x/y 段」(`dsh-tts-chunk-pill`,tabular-nums)。说明:宿主渲染的消息正文 DOM 不在插件控制内,故不做正文内逐句高亮,改用可见段计数(tooltip 仍保留细分)。
101
- - **P2-3 mini 播放器(暂停/继续 + 倍速)**:播放中的消息操作行新增「播放/暂停 + 倍速(1x/1.25x/1.5x)」控制;暂停/继续对 Web Audio 用 `ctx.suspend()/resume()`(无隙安全)、对单 URL 用 `el.pause()/play()`;倍速对单 URL 设 `el.playbackRate`、对 Web Audio 按 `rate` 调度(`nextStart += duration/rate`)并设 `source.playbackRate`;停止/打断时复位。新增 `mini.*` i18n 键。
102
- - **P2-4 tooltip 主题化 + slider 值整理**:为 auto-read / 朗读 / 暂停 / 倍速 / 段计数按钮改用 `data-tts-tip` + CSS `::after/::before` **主题化 tooltip**(`--dsw-*` token,深色友好),保留 `aria-label` 供屏幕阅读器;RVC 高级参数 slider 的当前值从"居中与把手重叠"改为**最右侧独立读数**(`dsh-tts-slider-value` 右对齐)。
103
- - **P2-5 首次上手引导**:RVC 设置面板顶部内嵌「首次使用 RVC?三步上手」引导块(`dsh-tts-onboard`):需求说明 + 分 OS 启动命令(Windows/macOS/Linux + 便携运行时)+ 就绪步骤 + 一键「运行诊断」按钮。新增 `onboard.*` i18n 键。
104
- - 运行:`smoke 47 · live 18 · patch 4 · i18n 6 · client-load 29`,`npm run test:all` 全绿。
105
-
106
- ## 11. Phase 3 落地记录(进行中)
107
-
108
- - **M3 音频导出/下载(已完成)**:
109
- - Host `GET /dsh-tts-audio/<id>?download=1` → 返回 `Content-Disposition: attachment; filename="dsh-tts-<id>.mp3"`,可保存合成音频(Edge 原声或 RVC 变声)。
110
- - 客户端在每条消息操作行新增**下载按钮**(`DownloadIcon`),点按后经 `rpcSpeak` 合成(复用进程内缓存,已读消息秒下),触发浏览器下载。
111
- - **长 RVC 分块朗读**(无单一输出文件)在前置拦截并提示"暂不支持导出为单个文件",避免白白启动探测+预热 job。
112
- - 新增 `download.*` i18n 键;`tests/smoke.mjs` 增加"`?download=1` 设置 Content-Disposition attachment"用例(48/48)。
113
- - **M1 TTS Provider 抽象(基础层,已完成)**:
114
- - Host 引入**提供者注册表**(`registerProvider`/`getProvider`):`edge-tts` 与 `rvc` 各自实现
115
- `synthesizeShort(text, voice, prosody, custom) -> 音频路径`,`/speak` 的短路径统一经注册表分发;
116
- 长 RVC 分块仍是 `rvc` 专属路径。行为零变更,现有 edge / rvc / chunked / cancel 测试全绿(48/48)。
117
- - 收益:后续新增本地后端(如 Piper/CosyVoice)只需 `registerProvider('local-piper', {...})` +
118
- UI 侧 provider 选项,无需改动 `/speak` 主逻辑。
119
- - **M1+ 本地 Piper 提供者(脚手架,已完成)**:注册第三个可插拔 provider `local-piper`,经同一抽象
120
- 在 `/speak` 短路径分发;配置了 `piperBinary`+`piperModel` 时 `spawn piper --model … --output_file …`,
121
- 未配置时返回本地化错误 `host.piperUnconfigured`(不崩溃)。`tests/smoke.mjs` 增加未配置用例(49/49)。
122
- (真实端到端合成需用户放好 Piper 二进制与 .onnx 模型,本环境无二进制无法实跑。)
123
- - **F4 选区朗读(已完成)**:`setupSelectionRead` 监听 `mouseup`,在会话中出现非空文本选区时于选区上方
124
- 显示一个「朗读选中文本」悬浮 chip(`speakText(sel, 'manual')`);自动忽略 input/textarea 内选择,
125
- 点击外部/滚动自动隐藏。新增 `sel.read` i18n 键与 `dsh-tts-sel-*` 主题化 CSS;preview 屏验证 chip 渲染正常。
126
- - **待续**:M2 原生流式合成(SSE/WebSocket + 客户端流式播放适配——较大且需真实宿主交互验证)。
127
- - **M2 原生流式合成的可行子集(已完成)**:把**自适应分块渐进播放**扩展到泛 Edge 长读——长文本不再"整段合成完再播",而是走同一分块队列"边合成边播"。`/speak` 的 `convertChunk` 改为按 `job.sink` 分发(RVC=逐块变声,Edge=逐块 Edge 合成);长 Edge 文本 >12s 时返回 `jobId+chunks`,前端复用现有 `playChunks` 无感续播。`tests/smoke.mjs` 增加"long edge read returns chunked job"用例(50/50)。
128
-
129
- ## 12. 追加落地记录(2026-08-25:toast 报错落地点 · RVC 降级 · 分句硬化)
130
-
131
- 针对四项优化建议的评估与实施(详见方案:toast 必做、降级 opt-in、分句低成本):
132
-
133
- - **F-toast(错误落地点,改自 E2/E4 的"静默失败")**:
134
- - client 新增 `shared.toast` + `showToast/dismissToast` + `shell.overlay` 的 `TtsToastHost`
135
- (主题化 `--dsw-*` token、`role="alert"`、6s 自动消失 + 关闭按钮 + 可选动作按钮)。
136
- - **消息朗读 / 自动朗读失败不再静默**:`speakText` 所有失败路径统一走 `onError`,
137
- 按钮与 auto-read 均接入 toast(沿用 `r.error !== "interrupted"` 打断抑制);
138
- play-failed 路径补 `onError`;预览面板移除 `.then` 重复报错(避免双显)。
139
- - 测试:`tests/client-load.mjs` toast 渲染/关闭断言;`window.__dshTtsToast` 测试钩子。
140
- - **E-RVC 降级(E4 补充)**:
141
- - **一键降级(始终可用)**:RVC 模式下错误 toast 带「改用 Edge TTS 朗读」动作 →
142
- `speakText(..., { provider: 'edge-tts' })` 以 RVC 底噪音色重读全文(长文走 Edge 分块管线)。
143
- - **自动降级(opt-in,默认关)**:设置面板 RVC 区新增 `rvcAutoFallback`(持久化到
144
- `dsh-tts-settings`);开启后 RVC 短/分块发起失败自动以 Edge 重试 + warn toast。
145
- 默认关理由:RVC 宣传"全本地不上传",自动外发 Edge 属隐私语义变化,需用户显式同意。
146
- - `rpcSpeak(text, voice, provider)` 增加 provider 覆盖参数,不翻转持久化 provider。
147
- - 边界(不做):分块**中途**某块失败不自动重启整任务(stop + toast,动作按钮可整篇重读)。
148
- - 测试:smoke「speak rvc unreachable → `host.rvcUnreachable` i18n 错误」;
149
- client-load `rvcAutoFallback` 持久化 round-trip + reset。
150
- - **E-splitter(建议 #4)**:`splitText` 硬化——
151
- - 原子 token(URL / 邮箱 / 小数 / 版本号)placeholders 保护,句子/段落切分不再被
152
- "3.14" 里的 `.` 误拆;硬切滑到空白/标点边界且不切 placeholder(`safeEnd`);
153
- - 末尾 <8 字孤儿块并入前一块(≤ maxChars 时);
154
- - 导出 `__test = { splitText }` 供直测。测试:smoke 增加 6 条 splitText 单测。
155
- - 卫生:清理 zh 字典重复 `voice.*`/`err.*` 键(PR-REVIEW 遗留)。
156
- - 运行:`smoke 60 · live 18 · patch 4 · i18n 6 · client-load 36`,`npm run test:all` 全绿;
157
- README/README.zh「边界行为」「设置持久化」已同步。
158
-
159
- ## 13. 新增候选落地记录(2026-08-25)
160
-
161
- > 用户决定:**做 A1(审批语音提醒),不做 A2(VAD 语音打断)**。
162
-
163
- ### A1. Agent 事件 / 审批语音提醒播报(已完成,test:all 全绿)
164
-
165
- - **事件源(已核实)**:`approval/asked` / `approval/decided` 是 `SessionEventMap`
166
- 的会话事件,经 `session/event` 火线发射(`dsh-session` types:`(session, event)`
167
- 负载、agent 作用域过滤);插件 Host 沙箱显式开放 `ctx.on`(`dsh-cordis-host-runner`)。
168
- - **Host(`lib/index.mjs`)**:
169
- - `ctx.on('session/event', …)` 订阅 → `ingestSessionEvent(event)` 纯函数摄入;
170
- 按审批 id 去重(`seen` 上限 200 粗清空)、队列上限 20、工具名/原因截断;
171
- - 新路由 `GET /dsh-tts-api/notify?s=N`:返回 `{ items, latest }` 增量队列;
172
- - `__test = { splitText, ingestSessionEvent, notify }` 测试钩子。
173
- - **Client(`lib/client.js`)**:
174
- - 设置面板新增「事件语音提醒」区:总开关(默认关)+ 事件类型勾选(审批请求 /
175
- 审批结果)+ 提醒音色(复用 Edge 音色表);持久化到 `dsh-tts-settings` 的 `notify` 对象;
176
- - `shell.overlay` 注册隐形 `TtsNotifyPoller`:每 4s 轮询 `/notify?s=N`;**首次轮询只做
177
- 基线同步**(不播历史,刷新不重播);后续按事件类型播报;
178
- - 播报:审批请求 = `speakText(text,'notify', 静默, {provider:'edge-tts', voice: 提醒音色})`
179
- **打断**当前朗读;审批结果仅在 `!shared.speaking` 时播报;播报失败静默(无错误 toast);
180
- - `speakText` opts 新增 `voice` 覆盖(提醒音色优先于 RVC 底噪回退)。
181
- - **测试**:smoke 68(fakeCtx 增加 `effect(fn)` 回调执行 + `on/emitFake` 模拟火线:
182
- 去重、增量游标、空 id/非事件防护);client-load 43(notify 持久化 round-trip +
183
- 设置面板渲染断言);i18n 6(新增 12 个 zh/en 键对等、无死键)。
184
- - 运行:`smoke 68 · live 18 · patch 4 · i18n 6 · client-load 43` 全绿。
185
- - **范围边界**:任务完成播报二期——dsh-jobs 无本插件可观察的会话事件通道(已核查
186
- dsh-jobs 无 `SessionEventMap` 增补),需后续事件接线。
187
- - 文档:README/zh 功能 #11 + 边界行为 + 设置持久化;`docs/acceptance-checklist.md` §2.5。
188
-
189
- ### A2. VAD 语音打断开关(用户决定:不做)
190
-
191
- - 判定不做:回声硬伤(TTS 外放会被麦克风自采导致自打断)、mic 权限 UX 成本、仅免手场景
192
- 有价值;键鼠场景已有打断替代(按钮/Esc/S/迷你暂停)。详见上一版评估。
193
-
194
- ## 14. 原声来源(上传底噪)下线记录(2026-09-11)
195
-
196
- - 判定:伪需求——RVC 链路本质是"Edge TTS 按消息文本合成底噪 → 本地转换",
197
- 对话内容时刻在变,静态上传音频与文本对不上,无意义。
198
- - 删除:Client `baseSource/baseAudioName/baseAudioData`(设置项、上传框、
199
- `onUploadAudio`、相关 i18n×16、CSS×4)+ Host `baseSource/baseAudioName/
200
- baseAudioBase64`(`rvcConfig`、指纹、`synthesizeRvc` 上传分支、分块守卫)+
201
- smoke 上传用例;`baseVoice/语速/音调/音量`(作用于 Edge 底噪)保留。
202
- - 老用户残留的 `localStorage.rvc.baseSource` 由 `loadSettings` 的白名单循环
203
- 自动忽略(`for k in RVC_DEFAULTS`),Host 对残留 `custom.baseSource` 同理
204
- 忽略;`rvc-config-save` 的 `{version:1}` 文件不受影响。
205
- - 运行:`smoke 74 · patch 4 · i18n 6 · client-load 49` 全绿。
1
+ # dsh-plugin-tts 优化 · 升级 · 新增 路线图(分析 + 提案)
2
+
3
+ > 结论:本插件在"长文分块无缝播放 / RVC 变声 / 音色包分发 / 便携运行时"上已领先同类;
4
+ > 真正的差距在 **状态落盘、可发现性、UI/UX 体验护栏(逐句高亮、速度/暂停、选区朗读、快捷键、a11y)**,
5
+ > 以及少量**边界健壮性**(显式取消、音色下线自愈、自动播放解锁、网络退避)。
6
+
7
+ ## 0. 现状盘点
8
+
9
+ - **路由**:`/dsh-tts-api/speak`(合成/分块队列)、`/dsh-tts-audio/<id>`、`rvc-next`(逐块拉取)、
10
+ `rvc-files`、`rvc-compact-index`、`rvc-packs`/`-installed`/`-install`/`-uninstall`/`-progress`、`diagnose`。
11
+ 零依赖 worker 镜像 node-edge-tts 协议(`Sec-MS-GEC`、`Path:audio` 帧、1006 重试一次)。
12
+ - **Client**:`shell.overlay` 隐藏 `<audio>` 宿主 + 4 个 slot(shell.overlay / input.left /
13
+ assistant-actions / settings.plugins.tab);Web Audio `start(prevEnd)` 采样级拼接;i18n 双层持久化。
14
+ - **自适应分块**:probe→ratio 分档(6–20s/预热2–4)→`calibration.json`(7天+设备指纹);短文本≤12s走单 URL(上传原声模式已下线,见 §14)。
15
+ - **音色包生态**:manifest schema 2、sha256 校验、代理、多索引变体、卸载、便携运行时。
16
+ - **已知限制(README)**:① 音色/自动朗读开关**内存态、不落盘**;② 音频写 OS 临时目录;③ 中英文视觉只能真机确认。
17
+
18
+ ## 1. 同类产品调研(基于既有领域知识)
19
+
20
+ | 类别 | 代表 | 本插件差距 / 可借鉴 |
21
+ |---|---|---|
22
+ | Edge TTS 桌面/CLI | edge-tts、各类 GUI | 只有"选音色→播放";本插件远超。可借鉴**速度控制、导出 MP3、逐句高亮** |
23
+ | 浏览器朗读扩展 | Read Aloud、Speechify | 强项:**卡拉OK式逐句高亮 + 语速 + 选区朗读 + 快捷键**——本插件最缺 |
24
+ | 桌面朗读工具 | Balabolka、NaturalReader | **格式朗读、批量朗读、波形可视化** → 批量读多消息、导出 |
25
+ | 聊天类 App 朗读 | ChatGPT/Claude/豆包/Kimi | **打断/续播策略、播放进度圈** |
26
+ | 开源多后端 TTS | Piper/espeak-ng/ChatTTS/CosyVoice/GPT-SoVITS | 本插件只有 Edge;本地高质量 TTS 可作底噪/RVC 补充 |
27
+ | RVC 生态 | RVC WebUI、AI Hobbyist、SUPERSING | **批量转换、音色 A/B 对比、成品导出** |
28
+
29
+ ## 2. 新增模块(能力扩展)
30
+
31
+ - **M1. TTS 后端抽象层(Provider 接口)**:Edge/RVC 迁入可插拔接口,新增本地 Piper/espeak、OpenAI/Azure、离线中文 TTS(ChatTTS/CosyVoice),让 RVC 底噪不再依赖在线。
32
+ - **M2. 原生流式合成(SSE/WebSocket)**:Edge 长文"边合成边推",降低 TTFP,复用 Web Audio 拼接管线。
33
+ - **M3. 导出/下载/复读缓存**:音频附件下载(原声+变声对);文本 hash+voice 的 LRU 缓存,重播不重合成。
34
+ - **M4. 音色包生态增强**:多仓库并存、下载前试听、A/B 对比、离线 zip 导入、断点续传。
35
+ - **M5. 多模型库与参数画像**:每个模型记住最优参数(f0/index_rate/原声音色/spk),切换即载入。
36
+
37
+ ## 3. 功能升级
38
+
39
+ - **F1. 设置落盘(最高优先)**:音色/自动朗读/provider/音量语速持久化(UI→localStorage,Host→`~/.dsh/tts-rvc/settings.json`),消 README Known-limits ①;加"恢复默认"。
40
+ - **F2. 自动朗读策略**:每条/仅长消息自动读、新消息打断或排队、不打断手动读。
41
+ - **F3. 朗读播放器升级**:暂停/继续、倍速(0.5–2x)、上/下一条、mini 进度条。
42
+ - **F4. 选区/从光标朗读**:朗读选中、从第 N 段继续。
43
+ - **F5. 诊断增强**:音色表新鲜度自检、calibration 信息、RVC 健康、网络/代理连通。
44
+
45
+ ## 4. UI/UX 设计优化(基于真实截图)
46
+
47
+ 1. **Auto-read 图标歧义 + 状态不可见**:喇叭易被误读为语音输入(ASR)、开关态只能靠 hover tooltip。→ 换独立图标(耳机/波形+A 徽标)、明显视觉态(填充/斜杠/pill"自动朗读·开")。
48
+ 2. **朗读进度显性化**:tooltip 之外,加**逐句/逐段高亮** + 细进度条。
49
+ 3. **Tooltip 主题化**:统一 `--dsw-*` token,避免系统默认白底黑字;所有图标补 tooltip。
50
+ 4. **设置面板信息密度**:值放把手旁小框、统一缩放/单位、加"恢复默认"、子导航(Edge/RVC/音色包/诊断)。
51
+ 5. **首次上手引导**:设置面板内嵌分 OS 启动步骤 + ✓ 就绪清单。
52
+ 6. **无障碍(a11y)**:aria-label/aria-pressed、focus 样式、键盘快捷键(Space 暂停/续播、S 停止、R 朗读)。
53
+
54
+ ## 5. 边界处理与健壮性
55
+
56
+ - **E1. 显式取消/打断释放资源**:停止时立即通知 Host 提前结束 job 串行链并释放 GPU/内存(不必等 10 分钟惰性回收)。
57
+ - **E2. 音色下线自愈**:`1007` 时动态剔除 + 自动回退默认音色,而非只报错。
58
+ - **E3. 自动播放解锁**:用户首次交互即预热 `AudioContext`。
59
+ - **E4. 网络抖动**:Edge 1006 改指数退避重试(≤3)+超时;RVC /convert 超时;`rvc-next` 短暂重试再报错。
60
+ - **E5. 服务掉线中播**:RVC 中途断→暂停等待/自动重连,恢复后续播。
61
+ - **E6. 消息形态边界**:空白/emoji-only 判定;超长消息限并发、入队。
62
+ - **E7. 磁盘与清理**:显式缓存清理入口 + 满盘告警;音频 LRU 上限。
63
+ - **E8. 校准健壮性**:`calibration.json` 多机同步/备份、模型更换重校、2 分钟兜底保留。
64
+ - **E9. 包安装安全**:下载 size 预检、断点续传、in-flight 恢复。
65
+ - **E10. 竞态回归**:换消息/切会话/刷新/新消息 5 态矩阵持续强化。
66
+
67
+ ## 6. 测试与验收
68
+
69
+ - 沿用现有分层(smoke/live/i18n/client-load/patch/make-pack)。
70
+ - 新增:E1 取消、E3 解锁、E4 退避;M1 provider 契约;M3 导出+缓存;F1 落盘往返+恢复默认;UI 新组件 mock 渲染 + a11y 断言;i18n 新键并入回归。
71
+ - 验收:`npm run test:all` 全绿;每个新能力有绿色用例;不破坏现有 5 态矩阵;README/zh、RVC-GUIDE、adaptive-chunked-playback 文档同步。
72
+
73
+ ## 7. 分期
74
+
75
+ - **Phase 1(低风险先做)**:F1 设置落盘+恢复默认 · E1 显式取消 · E3 自动播放解锁 · E2 音色下线自愈 · 部分 a11y(aria/快捷键) · F5 诊断增强的一部分 · 音频 LRU 缓存(F 前置)。
76
+ - **Phase 2(UX 体验护栏)**:auto-read 图标+状态重做 · 逐句高亮/进度显性化 · mini 播放器 · tooltip 主题化 · 面板密度整理 · 首次上手引导。
77
+ - **Phase 3(能力扩展)**:M1 Provider 抽象+本地 Piper · M2 原生流式 · M3 导出/下载 · F4 选区朗读。
78
+ - **Phase 4(规模/生态)**:M4 多仓库+A/B+离线安装+断点续传 · M5 多模型库 · E8 校准多机同步 · 便携运行时 Linux 实测 · E4/E5 网络与服务健壮性。
79
+
80
+ ## 8. 假设与前置说明
81
+
82
+ - 本文件即分析交付物;实时 web_search 在本环境因无 API Key 不可用,同类调研基于既有领域知识。
83
+ - 所有改动沿用现有模式(Host 路由 + `ctx.effect` 清理、零依赖 worker、client sprite+rpc、i18n 双层、dsh bundle/patch 装配),不引入新框架。
84
+
85
+ ## 9. Phase 1 落地记录(已完成,test:all 全绿)
86
+
87
+ - **E1 显式取消**:`/dsh-tts-api/rvc-next?cancel=1` + Host `cancelJob`,client `stopSpeaking` 即时通知释放本地 GPU/内存。(`tests/smoke.mjs` 取消用例)
88
+ - **E3 自动播放解锁**:apply 内首次手势 resume Web Audio 上下文 + 播放静音片段解锁 `<audio>`。
89
+ - **E2 音色下线自愈**:`pruneRemovedVoice()` 检测 `1007/unsupported voice`,剔除音色并回退默认;设置面板过滤已移除音色;新增 `voice.removed` i18n 键。
90
+ - **F1 设置持久化**:音色/自动朗读/提供者/RVC 配置写入 `localStorage['dsh-tts-settings']`,加载时恢复;面板新增「恢复默认设置」按钮;暴露 `window.__dshTtsSettings` 测试钩子。(`tests/client-load.mjs` round-trip 测试)
91
+ - **a11y**:`Esc`/`S`(非输入框内)停止朗读的全局快捷键。
92
+ - **音频 LRU 缓存**:确认进程内 `cache`/`files` LRU 缓存(60/300 上限);缓存命中时校验底层文件是否被系统清理,失效则回退重合成(避免 404)。(`tests/smoke.mjs` 重复朗读复用 URL 用例)
93
+ - 运行:`smoke 47 · live 18 · patch 4 · i18n 6 · client-load 28`,`npm run test:all` 全绿;README/README.zh「边界行为」「已知限制」已同步。
94
+
95
+ > 说明:音频缓存为**进程内**缓存(音频在 OS 临时目录,由系统清理);完全跨重启的磁盘级缓存列为后续项。
96
+
97
+ ## 10. Phase 2 落地记录(已完成,test:all 全绿)
98
+
99
+ - **P2-1 auto-read 图标+状态重做**:换用独立的 `HeadphonesIcon`(耳机,区别于"语音输入/麦"语义);自动朗读从仅有喇叭图标改为**带文字的 pill**(`自动朗读` + 状态圆点 + 品牌色高亮 + `aria-pressed`);新增 `autoRead.label` i18n 键。`tests/client-load.mjs` 增加"渲染为 labeled pill"结构化断言。
100
+ - **P2-2 朗读进度显性化**:分块朗读时在消息操作行显示可见计数 pill「第 x/y 段」(`dsh-tts-chunk-pill`,tabular-nums)。说明:宿主渲染的消息正文 DOM 不在插件控制内,故不做正文内逐句高亮,改用可见段计数(tooltip 仍保留细分)。
101
+ - **P2-3 mini 播放器(暂停/继续 + 倍速)**:播放中的消息操作行新增「播放/暂停 + 倍速(1x/1.25x/1.5x)」控制;暂停/继续对 Web Audio 用 `ctx.suspend()/resume()`(无隙安全)、对单 URL 用 `el.pause()/play()`;倍速对单 URL 设 `el.playbackRate`、对 Web Audio 按 `rate` 调度(`nextStart += duration/rate`)并设 `source.playbackRate`;停止/打断时复位。新增 `mini.*` i18n 键。
102
+ - **P2-4 tooltip 主题化 + slider 值整理**:为 auto-read / 朗读 / 暂停 / 倍速 / 段计数按钮改用 `data-tts-tip` + CSS `::after/::before` **主题化 tooltip**(`--dsw-*` token,深色友好),保留 `aria-label` 供屏幕阅读器;RVC 高级参数 slider 的当前值从"居中与把手重叠"改为**最右侧独立读数**(`dsh-tts-slider-value` 右对齐)。
103
+ - **P2-5 首次上手引导**:RVC 设置面板顶部内嵌「首次使用 RVC?三步上手」引导块(`dsh-tts-onboard`):需求说明 + 分 OS 启动命令(Windows/macOS/Linux + 便携运行时)+ 就绪步骤 + 一键「运行诊断」按钮。新增 `onboard.*` i18n 键。
104
+ - 运行:`smoke 47 · live 18 · patch 4 · i18n 6 · client-load 29`,`npm run test:all` 全绿。
105
+
106
+ ## 11. Phase 3 落地记录(进行中)
107
+
108
+ - **M3 音频导出/下载(已完成)**:
109
+ - Host `GET /dsh-tts-audio/<id>?download=1` → 返回 `Content-Disposition: attachment; filename="dsh-tts-<id>.mp3"`,可保存合成音频(Edge 原声或 RVC 变声)。
110
+ - 客户端在每条消息操作行新增**下载按钮**(`DownloadIcon`),点按后经 `rpcSpeak` 合成(复用进程内缓存,已读消息秒下),触发浏览器下载。
111
+ - **长 RVC 分块朗读**(无单一输出文件)在前置拦截并提示"暂不支持导出为单个文件",避免白白启动探测+预热 job。
112
+ - 新增 `download.*` i18n 键;`tests/smoke.mjs` 增加"`?download=1` 设置 Content-Disposition attachment"用例(48/48)。
113
+ - **M1 TTS Provider 抽象(基础层,已完成)**:
114
+ - Host 引入**提供者注册表**(`registerProvider`/`getProvider`):`edge-tts` 与 `rvc` 各自实现
115
+ `synthesizeShort(text, voice, prosody, custom) -> 音频路径`,`/speak` 的短路径统一经注册表分发;
116
+ 长 RVC 分块仍是 `rvc` 专属路径。行为零变更,现有 edge / rvc / chunked / cancel 测试全绿(48/48)。
117
+ - 收益:后续新增本地后端(如 Piper/CosyVoice)只需 `registerProvider('local-piper', {...})` +
118
+ UI 侧 provider 选项,无需改动 `/speak` 主逻辑。
119
+ - **M1+ 本地 Piper 提供者(脚手架,已完成)**:注册第三个可插拔 provider `local-piper`,经同一抽象
120
+ 在 `/speak` 短路径分发;配置了 `piperBinary`+`piperModel` 时 `spawn piper --model … --output_file …`,
121
+ 未配置时返回本地化错误 `host.piperUnconfigured`(不崩溃)。`tests/smoke.mjs` 增加未配置用例(49/49)。
122
+ (真实端到端合成需用户放好 Piper 二进制与 .onnx 模型,本环境无二进制无法实跑。)
123
+ - **F4 选区朗读(已完成)**:`setupSelectionRead` 监听 `mouseup`,在会话中出现非空文本选区时于选区上方
124
+ 显示一个「朗读选中文本」悬浮 chip(`speakText(sel, 'manual')`);自动忽略 input/textarea 内选择,
125
+ 点击外部/滚动自动隐藏。新增 `sel.read` i18n 键与 `dsh-tts-sel-*` 主题化 CSS;preview 屏验证 chip 渲染正常。
126
+ - **待续**:M2 原生流式合成(SSE/WebSocket + 客户端流式播放适配——较大且需真实宿主交互验证)。
127
+ - **M2 原生流式合成的可行子集(已完成)**:把**自适应分块渐进播放**扩展到泛 Edge 长读——长文本不再"整段合成完再播",而是走同一分块队列"边合成边播"。`/speak` 的 `convertChunk` 改为按 `job.sink` 分发(RVC=逐块变声,Edge=逐块 Edge 合成);长 Edge 文本 >12s 时返回 `jobId+chunks`,前端复用现有 `playChunks` 无感续播。`tests/smoke.mjs` 增加"long edge read returns chunked job"用例(50/50)。
128
+
129
+ ## 12. 追加落地记录(2026-08-25:toast 报错落地点 · RVC 降级 · 分句硬化)
130
+
131
+ 针对四项优化建议的评估与实施(详见方案:toast 必做、降级 opt-in、分句低成本):
132
+
133
+ - **F-toast(错误落地点,改自 E2/E4 的"静默失败")**:
134
+ - client 新增 `shared.toast` + `showToast/dismissToast` + `shell.overlay` 的 `TtsToastHost`
135
+ (主题化 `--dsw-*` token、`role="alert"`、6s 自动消失 + 关闭按钮 + 可选动作按钮)。
136
+ - **消息朗读 / 自动朗读失败不再静默**:`speakText` 所有失败路径统一走 `onError`,
137
+ 按钮与 auto-read 均接入 toast(沿用 `r.error !== "interrupted"` 打断抑制);
138
+ play-failed 路径补 `onError`;预览面板移除 `.then` 重复报错(避免双显)。
139
+ - 测试:`tests/client-load.mjs` toast 渲染/关闭断言;`window.__dshTtsToast` 测试钩子。
140
+ - **E-RVC 降级(E4 补充)**:
141
+ - **一键降级(始终可用)**:RVC 模式下错误 toast 带「改用 Edge TTS 朗读」动作 →
142
+ `speakText(..., { provider: 'edge-tts' })` 以 RVC 底噪音色重读全文(长文走 Edge 分块管线)。
143
+ - **自动降级(opt-in,默认关)**:设置面板 RVC 区新增 `rvcAutoFallback`(持久化到
144
+ `dsh-tts-settings`);开启后 RVC 短/分块发起失败自动以 Edge 重试 + warn toast。
145
+ 默认关理由:RVC 宣传"全本地不上传",自动外发 Edge 属隐私语义变化,需用户显式同意。
146
+ - `rpcSpeak(text, voice, provider)` 增加 provider 覆盖参数,不翻转持久化 provider。
147
+ - 边界(不做):分块**中途**某块失败不自动重启整任务(stop + toast,动作按钮可整篇重读)。
148
+ - 测试:smoke「speak rvc unreachable → `host.rvcUnreachable` i18n 错误」;
149
+ client-load `rvcAutoFallback` 持久化 round-trip + reset。
150
+ - **E-splitter(建议 #4)**:`splitText` 硬化——
151
+ - 原子 token(URL / 邮箱 / 小数 / 版本号)placeholders 保护,句子/段落切分不再被
152
+ "3.14" 里的 `.` 误拆;硬切滑到空白/标点边界且不切 placeholder(`safeEnd`);
153
+ - 末尾 <8 字孤儿块并入前一块(≤ maxChars 时);
154
+ - 导出 `__test = { splitText }` 供直测。测试:smoke 增加 6 条 splitText 单测。
155
+ - 卫生:清理 zh 字典重复 `voice.*`/`err.*` 键(PR-REVIEW 遗留)。
156
+ - 运行:`smoke 60 · live 18 · patch 4 · i18n 6 · client-load 36`,`npm run test:all` 全绿;
157
+ README/README.zh「边界行为」「设置持久化」已同步。
158
+
159
+ ## 13. 新增候选落地记录(2026-08-25)
160
+
161
+ > 用户决定:**做 A1(审批语音提醒),不做 A2(VAD 语音打断)**。
162
+
163
+ ### A1. Agent 事件 / 审批语音提醒播报(已完成,test:all 全绿)
164
+
165
+ - **事件源(已核实)**:`approval/asked` / `approval/decided` 是 `SessionEventMap`
166
+ 的会话事件,经 `session/event` 火线发射(`dsh-session` types:`(session, event)`
167
+ 负载、agent 作用域过滤);插件 Host 沙箱显式开放 `ctx.on`(`dsh-cordis-host-runner`)。
168
+ - **Host(`lib/index.mjs`)**:
169
+ - `ctx.on('session/event', …)` 订阅 → `ingestSessionEvent(event)` 纯函数摄入;
170
+ 按审批 id 去重(`seen` 上限 200 粗清空)、队列上限 20、工具名/原因截断;
171
+ - 新路由 `GET /dsh-tts-api/notify?s=N`:返回 `{ items, latest }` 增量队列;
172
+ - `__test = { splitText, ingestSessionEvent, notify }` 测试钩子。
173
+ - **Client(`lib/client.js`)**:
174
+ - 设置面板新增「事件语音提醒」区:总开关(默认关)+ 事件类型勾选(审批请求 /
175
+ 审批结果)+ 提醒音色(复用 Edge 音色表);持久化到 `dsh-tts-settings` 的 `notify` 对象;
176
+ - `shell.overlay` 注册隐形 `TtsNotifyPoller`:每 4s 轮询 `/notify?s=N`;**首次轮询只做
177
+ 基线同步**(不播历史,刷新不重播);后续按事件类型播报;
178
+ - 播报:审批请求 = `speakText(text,'notify', 静默, {provider:'edge-tts', voice: 提醒音色})`
179
+ **打断**当前朗读;审批结果仅在 `!shared.speaking` 时播报;播报失败静默(无错误 toast);
180
+ - `speakText` opts 新增 `voice` 覆盖(提醒音色优先于 RVC 底噪回退)。
181
+ - **测试**:smoke 68(fakeCtx 增加 `effect(fn)` 回调执行 + `on/emitFake` 模拟火线:
182
+ 去重、增量游标、空 id/非事件防护);client-load 43(notify 持久化 round-trip +
183
+ 设置面板渲染断言);i18n 6(新增 12 个 zh/en 键对等、无死键)。
184
+ - 运行:`smoke 68 · live 18 · patch 4 · i18n 6 · client-load 43` 全绿。
185
+ - **范围边界**:任务完成播报二期——dsh-jobs 无本插件可观察的会话事件通道(已核查
186
+ dsh-jobs 无 `SessionEventMap` 增补),需后续事件接线。
187
+ - 文档:README/zh 功能 #11 + 边界行为 + 设置持久化;`docs/acceptance-checklist.md` §2.5。
188
+
189
+ ### A2. VAD 语音打断开关(用户决定:不做)
190
+
191
+ - 判定不做:回声硬伤(TTS 外放会被麦克风自采导致自打断)、mic 权限 UX 成本、仅免手场景
192
+ 有价值;键鼠场景已有打断替代(按钮/Esc/S/迷你暂停)。详见上一版评估。
193
+
194
+ ## 14. 原声来源(上传底噪)下线记录(2026-09-11)
195
+
196
+ - 判定:伪需求——RVC 链路本质是"Edge TTS 按消息文本合成底噪 → 本地转换",
197
+ 对话内容时刻在变,静态上传音频与文本对不上,无意义。
198
+ - 删除:Client `baseSource/baseAudioName/baseAudioData`(设置项、上传框、
199
+ `onUploadAudio`、相关 i18n×16、CSS×4)+ Host `baseSource/baseAudioName/
200
+ baseAudioBase64`(`rvcConfig`、指纹、`synthesizeRvc` 上传分支、分块守卫)+
201
+ smoke 上传用例;`baseVoice/语速/音调/音量`(作用于 Edge 底噪)保留。
202
+ - 老用户残留的 `localStorage.rvc.baseSource` 由 `loadSettings` 的白名单循环
203
+ 自动忽略(`for k in RVC_DEFAULTS`),Host 对残留 `custom.baseSource` 同理
204
+ 忽略;`rvc-config-save` 的 `{version:1}` 文件不受影响。
205
+ - 运行:`smoke 74 · patch 4 · i18n 6 · client-load 49` 全绿。
package/PR-REVIEW.md CHANGED
@@ -1,75 +1,75 @@
1
- # PR #2 审查记录 — dsh-plugin-tts
2
-
3
- > ✅ **已完成合入**(2026-08-25):CI 批准后 Node 22/24 双 job success → squash 合入 main
4
- > (merge commit `9dcd7c7`,PR 状态 closed/merged)。本地已验证测试全绿。
5
- >
6
- > 目标:https://github.com/1624318455/dsh-plugin-tts/pull/2
7
- > 交付:这个 PR 是做什么的 · 是否需要合入 · 是否可以合入
8
- > 证据状态:GitHub 公开 API(pulls/2、commits、diff、actions/runs、check-runs、status)已全部直取核实 ✅
9
- > (本文档取代 2026-08-25 旧版,旧版在 API 不可达时误判 PR #2 = Phase 1-3 批次,已被 API 事实推翻。)
10
-
11
- ## 0. 事实速览(API 核实,2026-08-24 创建)
12
-
13
- | 项 | 值 |
14
- |---|---|
15
- | 标题 | `feat(tts): add Russian voices (ru-RU-Svetlana/Dmitry) to voice picker` |
16
- | 作者 | drfenixion(外部贡献者,fork:drfenixion/dsh-plugin-tts,author_association=NONE) |
17
- | 状态 | open,未合入,无评论 / 无 review |
18
- | 规模 | 1 commit · +6 / -0 · 1 文件(`lib/client.js`) |
19
- | 基础 | base `1624318455:main` @ `136ed935`(= 本地 HEAD,无冲突:`mergeable: true`、`rebaseable: true`) |
20
- | CI | head sha 有 1 条 workflow run(test),结论 **action_required**(首次贡献者需管理员批准才执行);base 分支自身最近一次 run 为 success |
21
- | 合并状态 | `mergeable_state: unstable`(= 无冲突但存在"未通过/待定"的 check 状态;个人仓库无强制保护,不影响点 Merge) |
22
-
23
- ## 1. 这个 PR 是做什么的
24
-
25
- 给设置面板的 **Edge TTS 音色选择列表**(`lib/client.js` 的 `VOICES` 数组)新增两个俄语音色:
26
-
27
- - `ru-RU-SvetlanaNeural`(斯维特拉娜,女声)
28
- - `ru-RU-DmitryNeural`(德米特里,男声)
29
-
30
- 并同步补充 i18n 标签:zh 字典(简体音译「斯韦特兰娜 / 德米特里」)与 en 字典(`Svetlana / Dmitry`)。
31
- 纯增量:+6 行、零删除、零新增依赖、不改任何现有行为。两个音色名均为微软 Edge TTS 长期在线的合法俄语 neural 音色。
32
-
33
- > ⚠️ 与旧版 PR-REVIEW.md 的结论完全不同:PR #2 **不是** Phase 1-3 批次(那是直接 push 上 main 的),
34
- > 而是一个小功能增强 PR。
35
-
36
- ## 2. 是否需要合入 —— 建议:合入 ✅
37
-
38
- - **有真实功能价值**:Edge 语音列表是唯一的 Edge 音色选择入口("自定义"仅指 RVC provider,与 Edge 列表无关),
39
- 新增俄语音色填补了列表里 zh / en / ja / ko / fr 之外的空白,让俄语用户开箱即用。
40
- - **改动极小且风格一致**:音色条目格式(`[voiceId, t(key)]`)、i18n 键命名(`voice.svetlana`)、
41
- 字典摆放位置都与现有条目完全一致;俄语名音译正确。
42
- - **零风险**:无运行时逻辑变化、无依赖、无公共接口变更,现有用户零影响。
43
- - 小瑕疵(非阻塞,可合入后顺手处理):
44
- - zh 字典中存在 Phase 1-3 遗留的 `voice.*` 键**重复定义**(L63-78 与 L277-285 两处,值相同、后者覆盖前者),
45
- 与本次 PR 无关,但建议单独清理;
46
- - 中文标签用简体音译,与 zh-TW/HK 条目用繁体不冲突(zh-CN 条目本就简体),风格上没问题。
47
-
48
- ## 3. 是否可以合入 —— 可以 ✅(已验证)
49
-
50
- - **无冲突**:`mergeable: true`,base = 本地 HEAD `136ed935`,patch 可干净应用(`git apply --check` OK)。
51
- - **本地实测全绿**(将 PR diff 应用后运行,跑完已回退,工作区恢复原状):
52
- - `test:i18n`:6/6(zh=en 各 233 键、键集一致、无死键、无 CJK 直出)
53
- - `test:client`:29/29(含设置面板渲染)
54
- - `test`(smoke):50/50;`test:patch`:4/4
55
- - **CI 侧面**:head 的 workflow run 处于 `action_required`——GitHub 对外部首次贡献者的 Actions 默认需维护者批准。
56
- 合入前建议在 PR 页点 "Approve and run",等 Node 22/24 矩阵绿了再合;若不想等,个人仓库无强制 check 保护,技术上不阻塞。
57
-
58
- **合入动作建议**:approve CI → 等 test 跑绿 → Merge(squash)→ 顺手清理 zh 字典重复 voice.* 键(可选)。
59
-
60
- ## 4. 结论
61
-
62
- 1. **做什么**:给 Edge TTS 音色列表新增 2 个俄语音色(Svetlana / Dmitry)+ zh/en 标签,+6/-0 的纯增量小功能。
63
- 2. **是否需要合入**:需要,小而有价值、零风险、风格一致。
64
- 3. **是否可以合入**:可以——无冲突、本地 4 套测试全绿(i18n 6/6 · client 29/29 · smoke 50/50 · patch 4/4);
65
- 注意首次贡献者的 CI 需管理员先批准才会跑。
66
-
67
- ## 5. 证据命令(可复现)
68
-
69
- ```bash
70
- curl -s https://api.github.com/repos/1624318455/dsh-plugin-tts/pulls/2
71
- curl -s https://github.com/1624318455/dsh-plugin-tts/pull/2.diff
72
- curl -s "https://api.github.com/repos/1624318455/dsh-plugin-tts/actions/runs?head_sha=62bae53bf6685d29a70fd13e030655b80f66f1cc"
73
- curl -sL https://github.com/1624318455/dsh-plugin-tts/pull/2.diff -o /tmp/pr2.diff && git apply --check /tmp/pr2.diff
74
- npm run test:i18n && npm run test:client && npm run test && npm run test:patch
1
+ # PR #2 审查记录 — dsh-plugin-tts
2
+
3
+ > ✅ **已完成合入**(2026-08-25):CI 批准后 Node 22/24 双 job success → squash 合入 main
4
+ > (merge commit `9dcd7c7`,PR 状态 closed/merged)。本地已验证测试全绿。
5
+ >
6
+ > 目标:https://github.com/1624318455/dsh-plugin-tts/pull/2
7
+ > 交付:这个 PR 是做什么的 · 是否需要合入 · 是否可以合入
8
+ > 证据状态:GitHub 公开 API(pulls/2、commits、diff、actions/runs、check-runs、status)已全部直取核实 ✅
9
+ > (本文档取代 2026-08-25 旧版,旧版在 API 不可达时误判 PR #2 = Phase 1-3 批次,已被 API 事实推翻。)
10
+
11
+ ## 0. 事实速览(API 核实,2026-08-24 创建)
12
+
13
+ | 项 | 值 |
14
+ |---|---|
15
+ | 标题 | `feat(tts): add Russian voices (ru-RU-Svetlana/Dmitry) to voice picker` |
16
+ | 作者 | drfenixion(外部贡献者,fork:drfenixion/dsh-plugin-tts,author_association=NONE) |
17
+ | 状态 | open,未合入,无评论 / 无 review |
18
+ | 规模 | 1 commit · +6 / -0 · 1 文件(`lib/client.js`) |
19
+ | 基础 | base `1624318455:main` @ `136ed935`(= 本地 HEAD,无冲突:`mergeable: true`、`rebaseable: true`) |
20
+ | CI | head sha 有 1 条 workflow run(test),结论 **action_required**(首次贡献者需管理员批准才执行);base 分支自身最近一次 run 为 success |
21
+ | 合并状态 | `mergeable_state: unstable`(= 无冲突但存在"未通过/待定"的 check 状态;个人仓库无强制保护,不影响点 Merge) |
22
+
23
+ ## 1. 这个 PR 是做什么的
24
+
25
+ 给设置面板的 **Edge TTS 音色选择列表**(`lib/client.js` 的 `VOICES` 数组)新增两个俄语音色:
26
+
27
+ - `ru-RU-SvetlanaNeural`(斯维特拉娜,女声)
28
+ - `ru-RU-DmitryNeural`(德米特里,男声)
29
+
30
+ 并同步补充 i18n 标签:zh 字典(简体音译「斯韦特兰娜 / 德米特里」)与 en 字典(`Svetlana / Dmitry`)。
31
+ 纯增量:+6 行、零删除、零新增依赖、不改任何现有行为。两个音色名均为微软 Edge TTS 长期在线的合法俄语 neural 音色。
32
+
33
+ > ⚠️ 与旧版 PR-REVIEW.md 的结论完全不同:PR #2 **不是** Phase 1-3 批次(那是直接 push 上 main 的),
34
+ > 而是一个小功能增强 PR。
35
+
36
+ ## 2. 是否需要合入 —— 建议:合入 ✅
37
+
38
+ - **有真实功能价值**:Edge 语音列表是唯一的 Edge 音色选择入口("自定义"仅指 RVC provider,与 Edge 列表无关),
39
+ 新增俄语音色填补了列表里 zh / en / ja / ko / fr 之外的空白,让俄语用户开箱即用。
40
+ - **改动极小且风格一致**:音色条目格式(`[voiceId, t(key)]`)、i18n 键命名(`voice.svetlana`)、
41
+ 字典摆放位置都与现有条目完全一致;俄语名音译正确。
42
+ - **零风险**:无运行时逻辑变化、无依赖、无公共接口变更,现有用户零影响。
43
+ - 小瑕疵(非阻塞,可合入后顺手处理):
44
+ - zh 字典中存在 Phase 1-3 遗留的 `voice.*` 键**重复定义**(L63-78 与 L277-285 两处,值相同、后者覆盖前者),
45
+ 与本次 PR 无关,但建议单独清理;
46
+ - 中文标签用简体音译,与 zh-TW/HK 条目用繁体不冲突(zh-CN 条目本就简体),风格上没问题。
47
+
48
+ ## 3. 是否可以合入 —— 可以 ✅(已验证)
49
+
50
+ - **无冲突**:`mergeable: true`,base = 本地 HEAD `136ed935`,patch 可干净应用(`git apply --check` OK)。
51
+ - **本地实测全绿**(将 PR diff 应用后运行,跑完已回退,工作区恢复原状):
52
+ - `test:i18n`:6/6(zh=en 各 233 键、键集一致、无死键、无 CJK 直出)
53
+ - `test:client`:29/29(含设置面板渲染)
54
+ - `test`(smoke):50/50;`test:patch`:4/4
55
+ - **CI 侧面**:head 的 workflow run 处于 `action_required`——GitHub 对外部首次贡献者的 Actions 默认需维护者批准。
56
+ 合入前建议在 PR 页点 "Approve and run",等 Node 22/24 矩阵绿了再合;若不想等,个人仓库无强制 check 保护,技术上不阻塞。
57
+
58
+ **合入动作建议**:approve CI → 等 test 跑绿 → Merge(squash)→ 顺手清理 zh 字典重复 voice.* 键(可选)。
59
+
60
+ ## 4. 结论
61
+
62
+ 1. **做什么**:给 Edge TTS 音色列表新增 2 个俄语音色(Svetlana / Dmitry)+ zh/en 标签,+6/-0 的纯增量小功能。
63
+ 2. **是否需要合入**:需要,小而有价值、零风险、风格一致。
64
+ 3. **是否可以合入**:可以——无冲突、本地 4 套测试全绿(i18n 6/6 · client 29/29 · smoke 50/50 · patch 4/4);
65
+ 注意首次贡献者的 CI 需管理员先批准才会跑。
66
+
67
+ ## 5. 证据命令(可复现)
68
+
69
+ ```bash
70
+ curl -s https://api.github.com/repos/1624318455/dsh-plugin-tts/pulls/2
71
+ curl -s https://github.com/1624318455/dsh-plugin-tts/pull/2.diff
72
+ curl -s "https://api.github.com/repos/1624318455/dsh-plugin-tts/actions/runs?head_sha=62bae53bf6685d29a70fd13e030655b80f66f1cc"
73
+ curl -sL https://github.com/1624318455/dsh-plugin-tts/pull/2.diff -o /tmp/pr2.diff && git apply --check /tmp/pr2.diff
74
+ npm run test:i18n && npm run test:client && npm run test && npm run test:patch
75
75
  ```