Back to home@ManTou-kaya

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

脚本做两件事:

  1. pnpm add -w github:ManTou-kaya/dsh-voice-input(装进 %USERPROFILE%\.dsh\profiles\web
  2. 把 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 APIChrome / Edge;能访问厂商语音服务准确率最好;Chrome 需要能访问 Google 服务
local宿主机 Windows System.SpeechWindows + 已安装听写识别器(如 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