feat: polish hotword overrides, audio archive, auto-update check#436
Conversation
围绕"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 完整。
PR Reviewer Guide 🔍(Review updated until commit 3125de8)Here are some key observations to aid the review process:
|
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 全过。
|
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。
|
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 全过。
|
关于 pr_agent round-3 提的 Build break (arch.lock returns Result):这是 false positive。
pr_agent 没有区分 parking_lot 和 std::sync 两套 API,这里按 false positive 处理,不修。 round-3 commit 3125de8 解决了同轮的 Missing-file handling:把 |
|
Persistent review updated to latest commit 3125de8 |
User description
Summary
围绕「ZIP 误识别 + AI 日报反馈」一波 7 项改动:
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_idpath-traversal 单测)npx tsc --noEmit→ 0 error手工验证(待 reviewer 跑):
已知后续待优化(review 已标记,本 PR 不修)
文件改动
20 modified + 1 new (
AutoUpdateGate.tsx) / +707 -32 linesPR Type
Enhancement, Bug fix, Tests
Description
Archive dictation audio as WAV
Add configurable history and audio caps
has_audio_recordingper sessionEnable automatic update checks on startup
Tighten hotword and transcript prompts
Diagram Walkthrough
File Walkthrough
1 files
Add secure audio-recording read command15 files
Persist archive state with history entriesRegister new recording read IPC commandGive hotword overrides priority over defaultsArchive microphone PCM into WAV filesMount auto-update gate in main appSchedule background update checks and dialogAdd play and download iconsAdd history and settings translation keysAdd history and settings translation keysAdd history and settings translation keysAdd history and settings translation keysAdd history and settings translation keysExpose audio-recording IPC and mock dataAdd recording playback and export actionsAdd recording, history, and update controls3 files
Add recording cleanup and capped historyExtend session schema, prefs, and promptsAdd recording flags and preference fields1 files
Update preference fixtures for new fields1 files