DSH HUB
HomePlugin StorePlugin PacksCommunityRankingsResourcesPublish Guide
Plugin source
Back to catalog

underworld-oddball /

underworld-oddball/sh-volume-shuff

Verified

Read the newest turn aloud from the DSH composer

★ 0 Stars0 Forks0 IssuesN/A Community rating0 Confirmed installs
View on GitHub
READMESource: main@32ef97f5

sh-volume-shuff

朗读 + 音量,一个按钮。从你最新那条提问的开头或者自选位置开始朗读。· Read aloud from the start of your newest question — or from any spot you pick. One button. 作者 / by 江湖老妖

DeepSeek Harness Web GUI 的输入框工具行里、模型选择框左边的一个扬声器按钮:

  • 单击:按下的瞬间图标上的悬浮提示词先消失 → 松开手开始朗读(页面自动翻动到朗读起点,默认=你最新那条提问的第一个字,在起点闪烁一根光标,从那里读到最新回复结尾);再单击停止,标记彻底消失(起点仍在最新那条提问的第一个字,下次点击从那里重读)。
  • 按住图标(≥400ms)并松手 → 松手时图标上方提示词变为「选择开始位置后点击 / After Pick → Tap」(按住期间提示词不变);松手后再移动鼠标,提示词才消失 → 在你想开始读的地方单击 → 光标落在那个字上,立刻从那里开始朗读。
  • 松手后一直不动(不移动鼠标、也不点正文)超过 5 秒 → 自动取消本次操作,提示词收起、标记消失、鼠标指针回到普通状态。想重来把鼠标移回图标再长按一次。
  • 再次单击停止 / 朗读自然结束 → 标记都彻底消失;起点始终是「你最后一次提问的第一个字」,所以下一次点击仍从那里读起。
  • 按住并向上拖拽 → 调出竖式音量混音台:页内音量 + 系统音量,各自可静音。
  • 点击页面任何其他地方 / Esc → 收起混音台 / 取消挑选(挑选中取消会连光标一起收掉)。
  • 挑选起点期间,除了「移动鼠标」,任何其他操作都取消本次挑选:按键、滚轮、拖动、右键、窗口失焦……(详见下表)
[ …其他插件图标 ] [ 🔊 ] [ 模型选择 ▾ ]   ← conversation.input.right 整体排在 .model 左边

它注册在 conversation.input.right(「提交键之前的一排紧凑控件」),整体渲染在模型选择框左边、紧挨着模型选择框——模型选择框自己占 conversation.input.model 这个固定座位,位置不随插件增删而变。同一个槽位里装了多个插件图标时,按槽位规则排队:先比 priority,再比 order,两者相同则按插件加载(安装)顺序——先装的靠左。本插件显式设了 order: 40(其他插件一般用默认 0),所以它总是贴着模型选择框待在这一排的最右边。

安装

# 从 npm 安装(推荐)
dsh plugin --profile web add sh-volume-shuff

也可以直接从 GitHub 装(等价,取的是仓库源码):

dsh plugin --profile web add github:underworld-oddball/sh-volume-shuff

收录进社区列表后,还能在 dsh-market(设置 → 插件市场)里搜到并一键安装。

装完重启一次 dsh web(插件在启动时组入 boot graph),然后刷新页面。

交互

手势 行为
单击图标 按下即收起悬浮提示词 → 松手开始朗读(翻到起点、闪烁光标);再单击 → 停止,标记彻底消失(起点仍是最新提问的第一个字)
按住图标(≥400ms)→ 松手 挑起点:松手时图标上方提示词变为「选择开始位置后点击」(悬停气泡同时退场,两个气泡不重叠)→ 松手后移动鼠标,提示词消失 → 页面上单击 → 光标落在该处并立刻开读
按住并向上拖拽(≥18px) 调出音量混音台(从图标上方弹出)
挑选中点空(空白/代码块外) 取消本次挑选(同「其他操作」),提示词与光标一起收起
挑选中移动鼠标 唯一「不取消」的动作:提示词消失,挑选继续,点正文即开读;每移动一次,下面的 5 秒空闲计时重新开始
挑选全程的鼠标形状 整个页面保持选择(十字)形状——移到图标、链接、按钮上也不会变回手型;选定或取消后立即恢复
挑选中一直不动(≥5 秒) 自动取消本次操作:提示词收起、指针复原、标记消失
挑选中其他任何操作(点空白 / 按键 / 滚轮 / 拖动 / 右键 / 失焦) 立刻取消本次挑选:提示词收起、标记消失,图标恢复为可单击状态
朗读自然结束 标记消失,起点仍是最后一次提问的第一个字
朗读中再次单击(停止) 停止播放,标记消失,起点同上
点击其他地方 / Esc 收起混音台 / 取消挑选
长按(≥400ms) 不朗读;松手即进入挑起点(防误触,也是挑起点手势)
混音台开着时单击图标 只收起混音台
悬停图标 三列双语提示(中文在上、灰色英文在下):
点击朗读 长按选起点 上滑调音量
[Tap: Speak] [Hold: Pick] [Swipe ↑: Volume](一按下就收起)
Enter / Space 朗读 / 停止
Alt + ↑ 开关混音台
← / → 光标左右移 1 字(挑选模式下同样可微调);空闲时挪过的位置不会成为单击的起点
Shift + ← / → 光标左右移 20 字

朗读中图标变蓝并多一道声波弧(不会再变红——静音状态只交给混音台显示)。挑选起点时光标变琥珀色,朗读时光标是主题色。

朗读起点

  • 默认起点:你最新那条提问的第一个字——每次单击/回车都从这里读起(严格规则:即使你之前用 ← / → 把光标挪到别处,单击也不会从那里读)。单击即翻到那里并闪一根光标,读的顺序是「这条提问 → 之后的助手回复(含最新那条)」。
  • 自定义起点:按住图标约 0.4 秒后松手——松手的那一刻图标上方出现「选择开始位置后点击」,然后移动鼠标,提示词消失(此时十字光标仍在,挑选状态没变),再在页面上想开始读的地方单击即可。点空白(代码块外面)算「其他操作」,会直接取消这次挑选;此外按任意键、滚轮、拖动、右键或切走窗口也都取消,图标回到可单击状态、光标停止闪烁。Esc 同样取消。松手后一直没动作(约 5 秒)也会自动取消:空闲计时从松手那刻开始,鼠标每移动一次就重新计满 5 秒。取消后标记彻底消失,起点仍是最后一次提问的第一个字(下次点击从那里读)。取消的原因会写进诊断:gesture:pick-cancel 的 detail 是 idle-timeout / empty-space / key / wheel / context-menu / context-click / drag / blur / hidden / escape。
  • 起点按会话记录保存(localStorage),刷新页面后仍在;出现新回复时自动回到「最新提问开头」。
  • 光标只是标记,不是 MD 编辑器里的选区:它不会选中文字、不影响复制。它只在朗读/挑选期间出现:朗读中闪动(主题色)、挑选中琥珀色;朗读结束(自然读完、手动停止)或取消后一律彻底消失,不残留、不闪烁。

两个音量有什么区别

推子 管什么 不管什么
页内音量 本标签页里所有 <audio>/<video> 元素的 volume:语音朗读、提示音、网页播放器 系统音量、其他 App、其他标签页
系统音量 系统输出音量与静音(macOS osascript,Linux pactl) 单个页面的元素音量上限

两者串联:系统音量是天花板。系统静音时页内开满也没声;页内拉到 0 只静这一页。

界面提示文案

悬停图标与挑选起点时的提示都是中英双标(方便中英混用环境):中文在上(沿用原来的橙色字),英文在下、灰色。空闲气泡是三列手势对齐(每列一个手势,列内中英上下对齐,整块左对齐);挑选/停止气泡只有一列,两行居中互相对齐;两种场景的位置、大小、颜色完全一致——都浮在图标上方。

场景 中文(上) 英文(下,灰)
悬停图标(空闲) 三列,中文在上:点击朗读 / 长按选起点 / 上滑调音量 每列下方灰色英文:[Tap: Speak] / [Hold: Pick] / [Swipe ↑: Volume]
悬停图标(朗读中) 点击停止 Tap: Stop
长按松手后(鼠标未动) 选择开始位置后点击 After Pick → Tap

时序:按住(≥400ms,提示词保持原样)→ 松手(提示词变为「选择开始位置后点击」)→ 移动鼠标(提示词消失)→ 单击正文(开读);这条链上任何一步停住超过 5 秒、或做了别的动作,都会取消(见上手势表)。气泡只负责把操作员送到起点,动鼠标即视为已经在找位置,不该再挡住页面;此后十字光标保持,单击即定起点开读。

反过来,移动鼠标是唯一不会取消挑选的动作:思考、改主意时按个键/滚一下/点右键/点一下空白处即可退出,不会朗读、也不会留下闪烁的光标。取消后提示词收起,想重来把鼠标移回图标再长按一次即可。

朗读实现

  • 文本来源是白名单,不是黑名单。真实 DSH DOM 里每个消息节点都带 [data-chat-flow],用 data-chat-flow-kind 标明类型;实测(dsh 0.1.7-rc.1)出现过这 5 种:

    kind 内容 是否朗读
    user 你的提问 ✅ 默认起点就是最新这条的第一个字
    assistant-step 回复正文(data-chat-group-part="response") ✅
    assistant-step 思考过程(data-chat-group-part="reasoning") ❌ 模型草稿,不念
    turn-process 「已完成工作 用时 4 秒」折叠头 ❌
    turn-tail 用量 301K tok 02:00 ❌
    tool-call 工具调用卡片 ❌

    所有节点都是平铺的兄弟节点(没有嵌套),所以按文档顺序拼接即可。

  • 阅读范围:从起点字符起,沿「可读节点序列」一直读到末尾;更早的轮次不读。

  • 每个字符都有对应的 DOM 位置:插件把节点按 innerText 语义打平成文本串,同时记录「第 n 个字落在哪个文本节点的第几个字符」,所以光标、翻页、按字符微调都精确到字;节点内部的按钮(复制/重试)不计入。

  • 合成:按 ~220 字切句块,逐块 POST /dsh-tts/speak(复用 dsh-tts 已配置的 provider 链,例如 Edge zh-CN-XiaoxiaoNeural),顺序播放;未安装 dsh-tts 或某个分块合成失败时,剩下的部分回退浏览器 speechSynthesis(不会从头重念)。

  • 点按钮时会先暂停页面上正在播放的音频,所以和 dsh-tts 的自动朗读(speakReplies)不会重叠。

早期版本在这里翻过车:按 data-chat-flow-kind="assistant" 找回复(真实值其实是 assistant-step),又用「跳过 button/svg」的黑名单排除杂物,结果把 turn-tail 当成了起点——光标落在时间戳上。现在换成白名单,并补了针对这一条的回归测试。

结构

package.json         dsh.bundle.patch + dsh.client.platform=web
cordis.patch.yml     bundle 层
lib/index.js         host 半:GET/POST /sh-volume-shuff/system、诊断 /sh-volume-shuff/diag
lib/client.js        浏览器半:slot 按钮 + 文本/光标定位 + 朗读器 + 竖式混音台(无需构建步骤)
scripts/test.mjs     jsdom 测试:把上面这个 bundle 原样跑在仿真会话 DOM 上

开发

npm install
npm test        # 33 项:文本定位、光标三态、长按挑选起点、移动只收气泡、5 秒空闲自动取消、点空白/按键/滚轮/拖动/右键/失焦取消、结束/取消后光标归位静止、双语提示、朗读、回退、混音台

测试不引入 React,而是自带一个只实现 createElement + 四个 hook 的微型运行时,并用 jsdom 跑未改动的 lib/client.js;只有 jsdom 缺的东西(布局几何、音频、语音合成、宿主 HTTP 路由)是仿真的。

要求

  • DSH >= 0.1.5-rc.3,Web GUI。适配按你要的通道顺序推进:正式版 → next → latest(alpha 版不在适配范围内)。

    顺序 通道 现在这个 Tag 指向 状态
    1 正式版(无后缀稳定版) 还没有——npm 上 26 个版本全是 -rc/-alpha,GitHub 上 10 个 release 全标着 prerelease(deepseek-harness@0.0.1 只是占位包) 出了即通过,插件无需改动
    2 next 0.1.7-rc.1 已核对(本机 GUI 就是这一版)
    3 latest 0.1.5-rc.3 已核对

    ⚠️ latest 比 next 旧(0.1.5-rc.3 < 0.1.7-rc.1),所以通道顺序 ≠ 版本递增顺序。这正是 engines.dsh 必须写成下限式 >=0.1.5-rc.3 的原因:写成按「最新那版」收窄的范围(比如 >=0.1.7-rc.1)会把 latest 关在门外;写成 ^0.1.5 又会在 0.1.x 上永远不匹配 0.1.7-rc.1,把 next 也踢掉。 正式版发出来之后,位于版本号下方的通道一律被这个范围涵盖,不需要再动。

  • 插件代码里没有任何按版本分支:window.__ModuleLoader__.load({id, factory})、ctx.slots.inject / ctx.slots.register、conversation.input.right 这些接口在 latest(0.1.5-rc.3) 与 next(0.1.7-rc.1) 上逐个比对过,签名一致。 ⚠️ 但消息 DOM 的取值必须实测,不能只看接口名:早期版本就栽在这里——以为 kind 是 user/assistant,实际是 user/assistant-step/turn-process/turn-tail/tool-call。现在的取值是在真实页面上 dump 出来的(见下表)。

  • 系统音量路由:macOS(osascript,内置)/ Linux(pactl)。其他平台该行置灰并显示原因。

  • 可选:dsh-tts —— 没装也能用,只是回退到浏览器语音。

已知边界

  • 页内音量是母音量,会覆盖插件自己设过的 element.volume;默认 100% 时行为不变。
  • 浏览器 speechSynthesis 没有音量 API,只能在静音时 cancel()。
  • 混音台路由只接受回环地址或同源请求,避免任意网页改你机器音量。
  • 「起点」是按字符记的:如果回复在起点之后被重新渲染成不同的文字(例如流式输出还没结束),起点会按同一节点重新夹取;节点整个换掉时退回默认起点(最新提问开头)。

许可

MIT © 江湖老妖

DSH HUB

A community index for DSH plugins. Not an official GitHub or DeepSeek AI product.

CommunityResourcesAPIAbout
—/ 5

No ratings yet

Verified DSH bundle

Commit 32ef97f58625

Community comments

No comments yet. Be the first to write one.