# dsh-read-aloud

[English](README.md) | 中文

在点赞按钮旁边加一个小喇叭：朗读 DeepSeek Harness 的任意一条回复。

每一条定稿的助手回复，都会在它本来就有的那行图标里多出一个动作，位置就在 👍👎 的右边：

```
复制 · 👍 👎 · 🔊 · 分支
```

点一下，就用**你浏览器自带的语音引擎**把这条回复念出来。不上传任何内容、不需要 API 密钥、主机端一行代码都不跑。

## 安装

```sh
dsh plugin --profile web add dsh-read-aloud
```

也可以在 **设置 → 插件市场** 里一键安装。多数情况下装完刷新页面就生效。

## 使用

| 操作 | 效果 |
| --- | --- |
| 点小喇叭 | 从开头朗读这条回复 |
| 朗读中再点一下 | 暂停，图标变成 ▶ |
| 暂停时再点一下 | 从同一句继续 |
| 点另一条消息的小喇叭 | 停掉当前的，开始读那一条 |
| 按 `Esc` | 立刻停止，不管鼠标在哪 |
| 鼠标移到小喇叭上 | 弹出倍速与音色面板 |

按钮有三种状态：🔈 待机、⏸ 朗读中、▶ 已暂停。读完会自己结束，图标回到 🔈。

### 倍速与音色

鼠标移到按钮上会弹出一个小面板，它会跟着你正在听的那条消息：

```
倍速   [0.5×] [0.75×] [1×] [1.25×] [1.5×] [1.75×] [2×]
音色   [ 跟随系统 ▾ ]
```

两个选择都**立刻对正在念的这句话生效**，并记在浏览器本地。音色来自你的操作系统；列表按中文语音优先排序，选「跟随系统」时会自动挑一个跟界面语言匹配的音色。

## 朗读内容

助手回复是 Markdown，而照着 Markdown 原文念会非常难听——URL 会被逐字符拼读，代码块会变成天书。所以正文会先做清洗：

| 保留 | 丢弃 |
| --- | --- |
| 正文段落与标题 | 围栏代码块（静默跳过） |
| 列表条目（去掉 `-` `1.` 等符号） | 表格行 |
| 行内代码的内容（去掉反引号） | 图片语法 |
| 链接文字 | 链接地址与裸 URL |
| 强调文字（去掉 `**` `*`） | HTML 标签、文件路径、emoji |

长回复**全文读完**，不做任何截断。正文会按句子切成小段排队朗读，而不是整段一次性丢给引擎——因为不少语音引擎会把过长的单次朗读悄悄截断或直接丢弃。

## 环境要求

- DeepSeek Harness **0.1.2-rc.1** 或更高版本
- 一个支持 Web Speech API 的浏览器。语音来自你操作系统里已安装的音色，所以如果系统里没有对应语言的音色，就会没有声音——Windows 和 macOS 都自带可用音色，Edge 还会额外提供更自然的在线音色。
- 如果浏览器完全没有语音引擎，按钮会直接告诉你不支持，而不是默默失败。

## 隐私

- **不发起任何网络请求**，插件从不联系任何服务器。
- 没有 API 密钥、没有账号、没有埋点。
- **没有主机端代码**：`lib/index.js` 是一个空的 `apply`，存在的唯一目的是让插件出现在 profile 的加载器里。
- 倍速和音色保存在浏览器本地存储的 `dsh-read-aloud/settings` 键下。清除站点数据只会把它们重置回 `1×` 和「跟随系统」，不影响任何其他东西。

## 兼容性

插件声明 `engines.dsh >= 0.1.2-rc.1`，并在 `conversation.chat.assistant-actions` 插槽里以 `order: 20` 注册一个条目，因此它紧挨着官方反馈按钮（`order: 10`）而**不会替换它**。正文通过插槽自带的 `useChat` 标准 props 读取，所以既不需要主机端 RPC，也不需要抓取 DOM。

## 开发

```sh
npm test
```

客户端 bundle 是按 Web 外壳加载的模块格式**手写**的，因此没有构建步骤、不需要打包器——`lib/client.js` 就是最终产物。测试会用一个桩化模块图加载这个真实文件，覆盖文本清洗、分段、消息查找和音色列举这几组纯函数。

## 许可

MIT
