Files
kangkang/docs/superpowers/specs/2026-06-10-voice-export-composer-design.md
2026-06-10 08:23:36 +08:00

2.2 KiB

「身体档案」输入框语音输入 设计

2026-06-10 · 在「身体档案」(HealthExportSheet)底部聊天输入框加端侧语音听写,复用 SpeechDictationService,识别文字实时流进输入框。

背景

「身体档案」composer 是聊天式输入(提问/诉求 → 发送 → LLM 对话/生成报告)。与日记不同,这里输入的内容马上交给 LLM,不需要"整理"加工;口述原话直接进输入框即正确行为(类似系统键盘听写)。

决策(已与用户确认)

维度 决定
交互 听写直接流进输入框:点 mic 开始,实时上屏;再点停止;用户自查后手动发送
LLM 不调用(无整理步骤、不自动发送)
复用 SpeechDictationService(@State 持有,防视图重建丢实例)、权限 alert 文案、3 分钟看门狗、onDisappear abort
UI mic 按钮放 TextField 与发送键之间;isAvailable == false 隐藏;录音中变红色停止态(脉冲动画)

组件

1. SpeechDictationService.merge(prefix:partial:)(新,static 纯函数)

听写文本拼接规则,唯一可单测的逻辑:

  • prefix 为空 → 返回 partial
  • prefix 以空白/换行结尾 → prefix + partial
  • 其余 → prefix + " " + partial

2. HealthExportSheet 改动

  • @State dictation + isDictating + dictationPrefix + 看门狗 Task
  • 点 mic:申请权限(拒绝 → alert 跳设置,与日记同文案)→ 记录 dictationPrefix = draftQuestion → start,每个 partial:draftQuestion = merge(prefix:partial:)
  • 再点:stop(),最终稿同 merge 落定;stop 返回空时保留输入框现状(partial 已实时在框里,天然兜底,不提示「没听清」)
  • 3 分钟看门狗自动停(防麦克风悬挂)

冲突防护

  • 录音中:TextField 与发送按钮、「生成整理报告」按钮禁用(防手输与 partial 互相覆盖、防录音中发送)
  • isAnswering / isGeneratingReport 时 mic 禁用
  • onDisappear abort

测试

  • merge(prefix:partial:) 3 个单测(空前缀 / 空白结尾前缀 / 普通前缀)
  • 真机手测:听写上屏、停止落定、已有文字保留、权限拒绝、3 分钟自动停

不做(YAGNI)

快捷问答弹窗 / 个人资料 Form 等其他输入处的语音;自动发送;录音面板;LLM 整理。