# dsh-alert-sound — Roadmap / 功能计划书

> **核心定位**：让用户在**不盯着 dsh 画面**（在干别的事、切走窗口、全屏等）时，也能通过**声音 / 语音**及时知道 dsh 需要自己——尤其是不想工作被“卡住”。因此本插件的差异化是 **“免注视的声音/语音提醒”**，而非单纯“完成提示音”。

## 现状

- 5 类提醒：需要审批 / 需要回答 / 输出完成 / 发生错误 + **「卡住」**（实验性，需先开「停滞检测」）。
- 每类：独立启停 + 音色（叮咚/低沉/轻点/警醒/语音/自定义/静音）+ 音量（0–200%）+ 试听。
- 可选**语音朗读**（中/英文随界面语言）；**语音失败自动改播提示音**，不会静音。
- **「朗读输出」的语义**：开启后「输出完成」会念出最后一条助手回复（上限 400 字）；关闭时只念类型名。**审批 / 提问不受该开关影响**（详情始终会念）。
- 默认对所有会话提醒（可切成「仅当前会话」）；后台标签页也能响。
- 其余开关：重复提醒、系统通知、停滞检测、悬浮提示、语音语速、勿扰时段、自定义音色、界面语言、恢复默认设置。
- 设置页在 **侧栏 → 提醒音**；设置存 localStorage（`dsh-alert-sound.v1`，自定义音色 `dsh-alert-sound.custom.v1`）。
- **（main 上已就绪、未发布）UI 改造**：设置页与悬浮提示改用宿主 `ui-primitives`（`Switch` / `Button` / `Toast`）与 `--dsw-*` 主题变量，深浅色 / 高对比主题自动跟随；悬浮提示改为**顶部宿主 Toast**（滑入 + 固定 1 秒淡出，总 3.6 秒）；宿主缺该模块时回退为原生控件 + 底部自绘横幅。

## 已实现（A–I）

- **A 播报具体内容**：审批念工具名 + 原因，提问念问题原文，错误念原因；「完成」的正文另需「朗读输出」。
- **B 重复提醒**：审批 / 提问按可配间隔（关 / 10 / 20 / 30 秒）重复到处理为止；错误重复 3 次。
- **C 系统通知**：浏览器 Notification，标签页在后台也会弹。
- **D 朗读输出**：浏览器 `speechSynthesis`；朗读没起来时自动改播该类提示音，保证一定有声音。
- **E 停滞检测**（实验性、默认关）：目前按会话 `updatedAt` 判定，**会误报**（见「E+」）。
- **F 自定义音色**：上传音频转 data URL 存 localStorage；每类上限 2MB，**超出配额时写入失败会被静默吞掉**，刷新后该音色消失。
- **G 语速 / 勿扰**：慢 / 标准 / 快；勿扰时段按整点，期间完全不响。
- **H i18n**：界面与语音 zh/en，跟随「界面语言」设置。
- **I 悬浮提示**：默认关（audio-first）；现在渲染为宿主 Toast。

## 未排期

### K. 语音更自然 / 换声音

- **问题**：浏览器 `speechSynthesis` 只能按语言选，不能选具体音色，中文偏机械。
- **轻（推荐先做）**：`getVoices()` 选音色 + 音调 pitch；纯客户端、成本低。
- **❌ 中方案（Edge TTS）已否决（2026-10-06）**：外部贡献者照它做了一份完整实现（[PR #1](https://github.com/Machine-126/dsh-alert-sound/pull/1)），评审后未合并，三条理由：
  1. **职责边界** —— 本插件的差异化是"免注视的提醒声音"；TTS 引擎选择、音色管理、预取缓存、内部协议维护是另一件事，生态已有专门的 `dsh-plugin-tts`。
  2. **隐私承诺** —— 插件与 `SECURITY.md` 都写着"不发起任何网络请求"；默认走 Edge 即推翻它。
  3. **维护面** —— `Sec-MS-GEC` / UA 版本等是跟着 `edge-tts` 对齐的**未公开内部协议**，上游随时可改，会**无声失灵**。
- **重（不建议放插件）**：本地嗓音克隆需装本地模型，超出浏览器插件范畴。
- **另通路（零开发）**：预录"你的嗓音/角色音"短音频，走**自定义音色**（每次固定句，读不了动态详情）。

### L. 朗读"挑内容"，去掉 markdown 符号

- **问题**：现在把 `assistant-step` 的 text 块原样拼接，`#`、代码、表格、链接、emoji 都会被念出来。
- **轻（推荐先做）**：**去 markdown 符号 + 规则式挑重点**（跳过代码块与元信息，优先读结尾结论）；纯客户端、离线、立即见效。
- **中（可选，后期）**：让模型生成一句高信息量摘要再读。

### E+. 停滞检测换判定信号

- **问题**：现在只看会话的 `updatedAt`，用户在后台 / 离开时会被误判成"卡住"，因此只能默认关闭。
- **参考**：社区的 [dsh-stall-guard](https://dshfind.com/zh/plugins/akira399/dsh-stall-guard) 用"**无活动事件 + 无在飞操作**"判定真卡死（`in-flight operations are exempt`），正好补上缺的那半个信号。
- **做法**：改为观察事件流（有没有新事件、有没有 running 的工具调用），而不是只比时间戳；先在真实卡住场景验证，再考虑默认开启。

## 下一步

按"改动小、收益直接"排序：

1. **K 的轻方案**：加音色选择 + 音调调节（纯客户端）。
2. **L 的轻方案**：朗读前去掉 markdown 符号、跳过代码块。
3. **E+ 的判定改进**：借鉴 stall-guard 的信号，把误报压下去。
