Skip to content

feat: polish hotword overrides, audio archive, auto-update check#436

Merged
appergb merged 4 commits into
betafrom
feat/voice-quality-audio-archive
May 15, 2026
Merged

feat: polish hotword overrides, audio archive, auto-update check#436
appergb merged 4 commits into
betafrom
feat/voice-quality-audio-archive

Conversation

@appergb
Copy link
Copy Markdown
Collaborator

@appergb appergb commented May 14, 2026

User description

Summary

围绕「ZIP 误识别 + AI 日报反馈」一波 7 项改动:

  • polish 提示词:COMMON_RULES 规则 2 加热词例外、规则 5 加英文短词同音示例(ZIP/VIP)、规则 2 显式举例带次版本号产品名(GPT-5.6 / Claude 4.7 / iOS 26.1)不省略小数;Structured mode 主题数上限放宽给「多条独立新闻 / 日报」场景 + 主题标题保留实体名;热词块尾部加「优先于规则 2」强约束
  • History 详情面板:修复复制按钮无反馈 bug(1.5s "已复制");加 AudioRecordingPlayer + 导出录音按钮
  • Settings 新增:自动检查更新(默认 on,启动延 4s + 60min interval)/ 历史条数上限(5–200)/ 保留原始录音 + 录音条数上限(1–200)
  • 录音归档(debug 开关版):recorder.rs WavArchiver(Drop 回填 RIFF/data header)、DictationSession.has_audio_recording 字段、history.id 与 SessionId 对齐、prune_recordings 双重 cap、IPC 改 async + UUID-v4 白名单校验防路径越界

Test plan

代码层:

  • cargo test --manifest-path src-tauri/Cargo.toml --lib → 258 全过(+2 个 is_valid_session_id path-traversal 单测)
  • npx tsc --noEmit → 0 error
  • 5 语言 i18n(zh-CN / en / ja / ko / zh-TW)补全所有新 key

手工验证(待 reviewer 跑):

  • 录一段说「这个 ZIP 文件解压一下」→ raw 仍可能是 VIP,polished 应该是 ZIP
  • 录一段说「GPT-5.6 出来了」→ polished 不应省略成 GPT-5
  • 录一段 AI 日报式播报(多条独立新闻)→ Structured mode 输出 ≥4 个主题,标题带实体名
  • History 点复制 → 看到 1.5s "已复制" 反馈
  • Settings 开「保留原始录音」→ 录一条 → History 看到「播放录音」+「导出录音」按钮 → 播放正常、导出落到 Downloads
  • Settings 关「自动检查更新」→ 主窗口不再后台弹更新对话框
  • Settings 历史条数填 5 → 录几条验证旧条目被裁掉

已知后续待优化(review 已标记,本 PR 不修)

  • H2:prune 在录音前调用,cap 边界可能多出 1 条文件(不爆盘)
  • M1:WavArchiver Drop unwinding 时 header 长度兜底(QuickTime 严格播放器可能拒播)
  • M3:has_audio_recording 跟磁盘脱钩(前端 catch 'recording not found' 已兜住)

文件改动

20 modified + 1 new (AutoUpdateGate.tsx) / +707 -32 lines


PR Type

Enhancement, Bug fix, Tests


Description

  • Archive dictation audio as WAV

    • Playback and export from History
    • Hide missing files after pruning
  • Add configurable history and audio caps

    • Persist has_audio_recording per session
  • Enable automatic update checks on startup

  • Tighten hotword and transcript prompts

    • Validate recording IPC session IDs
    • Fix clipboard copy feedback
    • Add path-traversal unit tests

Diagram Walkthrough

flowchart LR
  S["Settings"] -- "writes prefs" --> P["User preferences"]
  P -- "configures" --> R["Recorder"]
  R -- "archives WAV" --> W["recordings/<session_id>.wav"]
  R -- "flags session" --> H["History entry"]
  H -- "play / export" --> UI["History page"]
  G["AutoUpdateGate"] -- "checks releases" --> U["Update dialog"]
  PR["Prompt rules"] -- "hotword / structure polish" --> T["Transcript cleanup"]
Loading

File Walkthrough

Relevant files
Bug fix
1 files
commands.rs
Add secure audio-recording read command                                   
+86/-5   
Enhancement
15 files
coordinator.rs
Persist archive state with history entries                             
+23/-6   
lib.rs
Register new recording read IPC command                                   
+1/-0     
polish.rs
Give hotword overrides priority over defaults                       
+2/-2     
recorder.rs
Archive microphone PCM into WAV files                                       
+103/-1 
App.tsx
Mount auto-update gate in main app                                             
+2/-0     
AutoUpdateGate.tsx
Schedule background update checks and dialog                         
+58/-0   
Icon.tsx
Add play and download icons                                                           
+4/-0     
en.ts
Add history and settings translation keys                               
+13/-0   
ja.ts
Add history and settings translation keys                               
+13/-0   
ko.ts
Add history and settings translation keys                               
+13/-0   
zh-CN.ts
Add history and settings translation keys                               
+13/-0   
zh-TW.ts
Add history and settings translation keys                               
+13/-0   
ipc.ts
Expose audio-recording IPC and mock data                                 
+21/-0   
History.tsx
Add recording playback and export actions                               
+143/-4 
Settings.tsx
Add recording, history, and update controls                           
+67/-0   
Configuration changes
3 files
persistence.rs
Add recording cleanup and capped history                                 
+94/-4   
types.rs
Extend session schema, prefs, and prompts                               
+57/-3   
types.ts
Add recording flags and preference fields                               
+14/-0   
Tests
1 files
stylePrefs.test.ts
Update preference fixtures for new fields                               
+4/-0     
Additional files
1 files
dictation.rs +47/-11 

围绕"ZIP 误识别 + AI 日报反馈"一波集中改动,共 7 项:

polish 提示词
- COMMON_RULES 规则 2 加热词例外(热词列表正确写法优先于"原样保留")
- 规则 5 自动纠错补英文短词同音示例(ZIP/VIP)
- 规则 2 显式举例带次版本号产品名(GPT-5.6 / Claude 4.7 / iOS 26.1)不省略小数
- Structured mode 主题数上限放宽给"多条独立新闻/日报"场景
- Structured mode 要求主题标题保留关键实体名
- 热词块尾部加「优先于规则 2」强约束

History 详情面板
- 修复复制按钮没 await 没反馈的 bug(1.5s 显示"已复制")
- 加 AudioRecordingPlayer(按需加载 wav → Blob → 原生 audio controls)
- 加导出录音按钮(anchor.download 触发浏览器下载)

Settings 新增
- 自动检查更新开关(默认 on,启动延 4s + 60min interval)
- 历史条数上限输入框(5–200,留空 = 200)
- 保留原始录音开关 + 录音条数上限输入框

录音归档(debug 开关版)
- recorder.rs WavArchiver(Drop 时回填 RIFF/data header)
- DictationSession.has_audio_recording 字段;history.id 与 SessionId 对齐
- persistence.rs 加 recordings_root / recording_path_for_session
- prune_recordings 双重 cap(days + count),仅扫 .wav,metadata 失败按过期处理

IPC 安全 + 性能
- read_audio_recording 改 async + tokio::fs::read 防阻塞主循环
- session_id 白名单 UUID-v4 字面校验 + 2 个单测覆盖路径越界

后续待优化(review 标记 H2 / M1 / M3):
- prune 在录音前调用,cap 边界少裁 1 条(最多多 1 个文件,不爆盘)
- WavArchiver Drop unwinding 时 header 长度兜底(QuickTime 可能拒播)
- has_audio_recording 跟磁盘脱钩(前端 catch 'recording not found' 已兜住)

cargo test 258 全过;tsc 0 error;5 语言 i18n 完整。
@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 14, 2026

PR Reviewer Guide 🔍

(Review updated until commit 3125de8)

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 PR contains tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Compile Error

Mutex::lock() returns a LockResult, so chaining .append(...) directly on it will not compile. The recorder path will fail to build unless the guard is unwrapped before calling append.

if let Some(arch) = archiver {
    arch.lock().append(&pcm_bytes);
Audio Glitch

The WAV archive now performs mutex locking and file writes directly inside the CPAL audio callback. On slower disks or when the filesystem stalls, this can block the real-time capture thread long enough to cause underruns or corrupted recordings.

if let Some(arch) = archiver {
    arch.lock().append(&pcm_bytes);
}

pr_agent review #436 提了两个跟 has_audio_recording 字段相关的 issue,本 commit
全部修复:

Wrong Flag (dictation.rs:1551 polish 收尾路径)
- 原实现:has_audio_recording: Some(prefs.record_audio_for_debug)
  以「开关是否打开」当真值。但 Recorder::start 内部 WavArchiver::create 可能失败
  (recordings/ 创建不出 / 权限不足 / 磁盘满),开关 on 但 wav 没真写盘时,前端会
  渲染播放按钮、点击得到 'recording not found'。
- 修法:Recorder::start 返回值加第三个 bool = archive_active。Inner 加
  audio_archive_active: AtomicBool 字段,begin_session 时写入。history 写入路径
  读这个字段而不是 prefs,反映真实写盘状态。

Missing Audio (dictation.rs:1233 empty-transcript 失败路径)
- 原实现:has_audio_recording: None,即使开关 on 也强行 None。但磁盘上 wav 仍存在
  (recorder 已走完整段录音)。讽刺的是:ASR 没识别到文字恰好是用户最想听原始录音
  诊断的场景。
- 修法:empty-transcript 路径同样用 inner.audio_archive_active.load(...)。
  与 polish 路径一致。

副作用最小:
- SessionState 不动(不污染 10 个构造点)
- 改 Inner 加一个 AtomicBool(2 个构造点已补)
- Recorder::start 返回元组扩第 3 项;3 个调用点(dictation/QA/preview)都已更新

cargo test 258 全过。
@github-actions
Copy link
Copy Markdown

Persistent review updated to latest commit 13839a2

pr_agent 二轮 review 又提了 2 个 issue,本 commit 解决:

Stale closure (AutoUpdateGate.tsx)
- 原 useEffect 依赖 [enabled],tick 闭包捕获渲染时的 u;u.status 变化时 tick
  读不到新值,极端时序下 60min interval 触发的 tick 可能在用户已经手动打开
  UpdateDialog 的情况下错过 busy 检查,并发触发第二次 checkForUpdates。
- 修:useRef(u) + 每次渲染同步 uRef.current = u;tick 读 uRef.current 始终拿
  最新 useAutoUpdate 返回值,避免 stale 状态判断。

Missing file check (History.tsx)
- hasAudioRecording 只代表「录音时 wav 写盘成功」,但 audioRecordingMaxEntries
  / historyRetentionDays 之后 prune 可能已删 wav。前端无条件渲染按钮 → click
  → 后端返回 'recording not found'。属于 UX 不优雅(功能有 error UI 兜住)。
- 修:父组件加 audioMissingIds: Set<string>;AudioRecordingPlayer 加 onMissing
  回调,遇 'not found' 错误时调用父让 id 加入 set;导出按钮 catch 同样错误
  也 mark missing;按钮组渲染条件改为 hasAudioRecording && !audioMissingIds.
  has(id),一次点击后永久隐藏,不再让用户撞同样的 error。

cargo test 258 / tsc 0 error。
@github-actions
Copy link
Copy Markdown

Persistent review updated to latest commit 67d1277

pr_agent round 3 提的 Missing-file handling 兜底:

read_audio_recording 在 exists() 检查后到 tokio::fs::read 之间,文件可能因
prune(条数 cap / retention 清理 / 用户手动删)而消失。原实现把 NotFound 错
格式化成 'read wav failed: No such file...' 这种 OS 原文,前端字符串匹配
'recording not found' 接不住,UI 显示一次 generic error 而不是隐藏按钮。

修法:read 失败时显式判 ErrorKind::NotFound → 返回跟 exists() 失败同样的
'recording not found' 字符串。前端单条 catch 就稳,不依赖本地化 OS 错误。

注:pr_agent 同轮提的 "Build break"(arch.lock() 返回 Result)是 false positive
—— recorder.rs 用 parking_lot::Mutex 而非 std::sync::Mutex,.lock() 直接返回
MutexGuard 无 Result。本地 cargo test 258 全过、3 平台 CI build 都绿可证。

cargo test 258 全过。
@appergb
Copy link
Copy Markdown
Collaborator Author

appergb commented May 14, 2026

关于 pr_agent round-3 提的 Build break (arch.lock returns Result):这是 false positive。

recorder.rs:20 用的是 use parking_lot::Mutex(不是 std::sync::Mutex),parking_lot 的 .lock() 直接返回 MutexGuard,没有 Result 包装。本地 cargo test --lib 258 全过 + 3 平台 CI build 都绿可证。

pr_agent 没有区分 parking_lot 和 std::sync 两套 API,这里按 false positive 处理,不修。

round-3 commit 3125de8 解决了同轮的 Missing-file handling:把 read_audio_recordingtokio::fs::readErrorKind::NotFound 标准化成跟 exists() 失败同样的 "recording not found" 字符串,前端 catch 不依赖 OS 原文。

@github-actions
Copy link
Copy Markdown

Persistent review updated to latest commit 3125de8

@appergb appergb merged commit a0c239a into beta May 15, 2026
4 checks passed
@appergb appergb deleted the feat/voice-quality-audio-archive branch May 15, 2026 00:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant