dsh-voice-input
Voice input for the DSH Web composer: browser Web Speech with a Windows offline (System.Speech) fallback.
- Stars
- 1
- Language
- JavaScript
- Created
- Sep 9, 2026
- Updated
- Sep 9, 2026
Introduction
dsh-voice-input
给 DSH Web GUI 的输入框加语音输入:点麦克风说话,识别文字自动追加到草稿末尾。
- 双引擎:默认用浏览器内置的 Web Speech(Chrome / Edge);不可用或报网络错误时自动回退到本机 Windows 离线识别(System.Speech,零密钥、可离线)
- 零构建安装:
lib/client.js已提交,克隆或pnpm add github:...之后直接可用,不需要装 esbuild - 浮层面板可切换语言与引擎,
完成/撤销/Esc,选择持久化 - 不改动任何已发布的
@deepseek-ai/*包,可单独卸载
安装(另一台电脑)
让 AI 助手装:把仓库地址丢给那台机器上的 Agent,加一句「按
AGENTS.md安装」即可。AGENTS.md是给模型读的逐步安装 / 验证 / 排错说明(含每条命令的期望输出)。
前提:那台机器已经装好 DSH 与 pnpm(dsh web 至少跑过一次,profile 才会存在)。
git clone https://github.com/ManTou-kaya/dsh-voice-input.git
cd dsh-voice-input
powershell -ExecutionPolicy Bypass -File .\install.ps1
脚本做两件事:
pnpm add -w github:ManTou-kaya/dsh-voice-input(装进%USERPROFILE%\.dsh\profiles\web)- 把 loader 行写进该 profile 的
cordis.patch.yml
然后刷新浏览器页面(F5),输入框工具行就会出现麦克风按钮。若没出现,重启一次 dsh web。
手动安装(不用脚本)
cd $env:USERPROFILE\.dsh\profiles\web
pnpm add -w github:ManTou-kaya/dsh-voice-input
再编辑 cordis.patch.yml(该文件是 YAML 数组,热生效):
- insert:
- id: dsh-voice-input
name: 'dsh-voice-input'
包故意不声明
dsh.bundle:这样dsh plugin add不会把包名同时写进dsh.profile.bundles,避免同一个 id 被插入两次。dsh plugin --profile web add github:ManTou-kaya/dsh-voice-input也能装,只是会打印一条 “declares no dsh.bundle” 的提示,属正常。
卸载
# 1) 删掉 cordis.patch.yml 里的 - insert: 那一行(热生效)
# 2) 删依赖
cd $env:USERPROFILE\.dsh\profiles\web
pnpm remove -w dsh-voice-input
用法
输入框工具行(模型选择器左侧、发送按钮之前)的麦克风按钮:
| 操作 | 效果 |
|---|---|
| 点麦克风 | 开始识别;再次点击 = 停止并保留文字 |
浮层 完成 | 同上 |
浮层 撤销 | 只删除本次语音追加的文字(录音期间手打的字不动) |
Esc | 停止并保留 |
| 语言下拉 | zh-CN / en-US(本机离线引擎只支持已安装的语言) |
| 引擎下拉 | 自动 / 浏览器 / 本机离线 |
识别文字按段追加到草稿末尾;浮层实时显示临时结果。录音期间手动输入的内容不会被覆盖,但可能与识别结果交错。
设置持久化在 localStorage['dsh-voice-input'] = { engine, lang }。
引擎
| 引擎 | 位置 | 依赖 | 说明 |
|---|---|---|---|
browser | 浏览器内 Web Speech API | Chrome / Edge;能访问厂商语音服务 | 准确率最好;Chrome 需要能访问 Google 服务 |
local | 宿主机 Windows System.Speech | Windows + 已安装听写识别器(如 zh-CN) | 完全离线、无密钥;听写准确率一般 |
auto(默认):有 Web Speech 就用它;API 不存在或报 network / language-not-supported 时自动切到本机离线引擎,面板会提示已切换。
实现
lib/index.js 宿主端:/voice-input/transcribe、/voice-input/status、启动全局、常驻 PowerShell 子进程
lib/recognize.ps1 System.Speech 常驻 worker(stdin/stdout 行式 JSON、UTF-8、空闲回收)
lib/client.js 浏览器端构建产物(__ModuleLoader__ 工厂信封,外部依赖只有 react / react-jsx-runtime / dsh-client-ui-primitives)
src/client/ 源码:slot 注册、麦克风按钮、浮层面板、引擎编排、Web Speech、本机录音+WAV、草稿追加/撤销
build.mjs esbuild 打包成宿主可服务的工厂信封
数据流:
[麦克风按钮 conversation.input.right] ─┐
[浮层面板 conversation.input.overlay] ─┤ 模块级 store
├─ browser: SpeechRecognition onresult → 追加草稿
└─ local: getUserMedia → AudioWorklet(16k)
→ VAD 分段 → WAV → POST /voice-input/transcribe
→ 宿主 powershell.exe (System.Speech) → 文本
→ inputActions.setDraft(实时草稿 + 新段)
- 宿主路由只接受 loopback 对端 + 每进程随机 token(
x-dsh-voice-token),body ≤ 4 MiB,20s 超时,临时文件用后即删。 - 草稿写入只能走宿主公开的
InputActions.setDraft()(整体替换),所以采用「追加到实时草稿」而不是「基准草稿 + 累计」,这样录音期间手打的字不会被抹掉。 System.Speech的同步Recognize()在第一句之后会清空音频输入(多句音频会丢后半段),因此 worker 用RecognizeAsync(Multiple)+SpeechRecognized/RecognizeCompleted事件收集全部句子。
开发
pnpm install # 装 esbuild
node build.mjs # 重写 lib/client.js
只改 src/client/** 时,改完跑 node build.mjs 即可;client-hmr 会轮询该文件并在浏览器里热重载插件行,不用重启 dsh web。改 lib/index.js / lib/recognize.ps1 需要重启 dsh web。
调试本机识别 worker:
$env:DSH_VOICE_DEBUG='1' # worker 把请求/结果写到 stderr
限制
- 麦克风需要安全上下文:
http://127.0.0.1/localhost或 HTTPS。用局域网 IP 打开时浏览器会禁用麦克风,面板会明确提示。 - 本机离线引擎仅 Windows,且只能用系统已安装的识别器语言;听写准确率一般(实测示例句会有错字),面板上有引擎徽标可手动切回浏览器引擎。
setDraft()是整体替换,识别结果与录音期间的手动输入可能交错。
License
MIT