fix(insertion): macOS 让「恢复剪贴板」开关真正生效,修复被回退的 #525#683
Conversation
#525 的 macOS 剪贴板恢复(commit 4b4d219)被 Android 移植 commit 1ea467a 连同 cfg 门控一起回退:整套恢复机制被重新 #[cfg] 排除出 macOS,macOS insert() 退回「只写剪贴板 + Cmd+V」,于是设置里的「恢复剪贴板」开关在 macOS 上完全无效 ——无论开关开还是关,剪贴板都被留成转写文字。 - 把共享的剪贴板恢复机制(ClipboardRestorePlan / schedule_clipboard_restore / restore_clipboard_after_delay / should_restore_clipboard 等)的 cfg 从 not(any(macos,android,ios)) 放宽为 not(any(android,ios)),重新纳入 macOS (移动端仍排除;Windows/Linux 不变)。 - macOS insert() 改为:保存原剪贴板 → 写转写文字 → Cmd+V → 仅当「恢复剪贴板」 开关开启且粘贴成功时按 CLIPBOARD_RESTORE_DELAY 延迟恢复;粘贴失败则保留转写 文字供手动粘贴、不恢复。开 = 恢复,关 = 保持现状,开关说了算。 - 删除随之失效的 macos_insert_status_after_paste;恢复相关单测在 macOS 也运行。 Windows/Linux 专用项(insert_with_clipboard_restore / paste_keys / simulate_paste(shortcut) / 其 insertion_success_status 变体)的 macOS 排除保持不变。
|
@claude 请审核:让 macOS 的「恢复剪贴板」开关真正生效(#525 被 1ea467a 回退后修复)。重点看 (1) 把恢复机制 cfg 从 not(any(macos,android,ios)) 放宽到 not(any(android,ios)) 是否准确——macOS/Windows/Linux 启用、Android/iOS 排除,没有把 enigo/PasteShortcut 相关 winlinux 专用项误带进 macOS(应无重复定义);(2) macOS insert 的开=恢复/关=保持/粘贴失败=保留 三态逻辑;(3) schedule_clipboard_restore 用 std::thread + arboard 在 macOS 是否妥当。 |
|
Claude finished @appergb's task in 2m 46s —— View job 审核结果
1. cfg 门控正确性 ✅放宽范围的函数/类型(全部改为
保持
结论:cfg 放宽精准,共享基础设施对 macOS 开放,enigo/PasteShortcut 专属逻辑保持排除,macOS 编译无重复定义,符合改动目标。 2. macOS insert 三态逻辑 ✅// 开关开 (restore_clipboard_after_paste = true)
copy_to_clipboard_with_restore_plan(text) // 保存原剪贴板 + 写转写文字
→ simulate_paste() 成功
→ schedule_clipboard_restore(restore_plan) // 750ms 后恢复
→ InsertStatus::Inserted
// 开关关 (restore_clipboard_after_paste = false)
copy_to_clipboard_with_restore_plan(text)
→ simulate_paste() 成功
→ schedule_clipboard_restore 不调用 // 转写文字留在剪贴板
→ InsertStatus::Inserted
// 粘贴失败
copy_to_clipboard_with_restore_plan(text)
→ simulate_paste() 返回 Err
→ 不恢复(转写文字留剪贴板供手动粘贴)
→ InsertStatus::CopiedFallback三态逻辑正确。 一处轻微效率问题(非阻塞):当 // 可选优化(非必须)
if restore_clipboard_after_paste {
let plan = copy_to_clipboard_with_restore_plan(text)?;
// ...
schedule_clipboard_restore(plan);
} else {
copy_to_clipboard(text); // 不读原剪贴板
}3. std::thread + arboard 在 macOS 是否妥当
|
| 方面 | 评估 |
|---|---|
NSPasteboard 线程安全性 |
Apple 文档明确 NSPasteboard 可从任意线程访问,无需主线程 ✅ |
| Autorelease Pool | arboard 在 macOS 实现内部自行管理 @autoreleasepool,后台线程无需额外处理 ✅ |
| JoinHandle 丢弃 | spawn 返回的 handle 被丢弃 = detach,750ms 后台恢复正常完成,不会被提前终止 ✅ |
| 竞态防护 | NEXT_CLIPBOARD_RESTORE_ID(SeqCst AtomicU64)+ PENDING_CLIPBOARD_RESTORE(parking_lot::Mutex)确保多次快速听写时只有最新一次恢复执行 ✅ |
| 沙盒限制 | 此应用依赖辅助功能权限,非 App Store 沙盒限制环境,跨线程剪贴板访问无额外限制 ✅ |
主要风险:arboard 在 macOS 后台线程调用的行为在极端条件下(如系统剪贴板被第三方剪贴板管理器大量监听时)可能有细微延迟差异。PR 描述中"需真机验证"是正确判断——建议在「恢复剪贴板」开关两态下各测试 ≥10 次,覆盖快速连续听写场景。
测试覆盖评估
| 测试 | cfg | 说明 |
|---|---|---|
macos_paste_success_reports_inserted_and_guards_restore(新增) |
macOS | 测试 insertion_success_status() 和 should_restore_clipboard()——单元层面正确;因 CGEvent 需系统权限,insert() 集成路径需真机验证 |
restore_only_when_clipboard_still_holds_inserted_text |
现在 macOS 也跑 | ✅ |
pending_clipboard_restore_keeps_first_original_until_latest_restore |
现在 macOS 也跑 | ✅ |
clipboard_restore_skips_when_clipboard_no_longer_matches_inserted_text |
现在 macOS 也跑 | ✅ |
总结
可合并,无阻塞性问题。三处需关注:
- 可选优化(关闭状态下的多余剪贴板读取)——不影响正确性,可后续迭代
- 真机验证(arboard 后台线程在 macOS 的实际行为)——PR 描述中已有说明,合并前请完成
- 测试覆盖——
insert()端到端开关行为依赖真机测试,单元测试覆盖了核心辅助函数
cfg 边界清晰、三态逻辑正确、删除失效的 macos_insert_status_after_paste 属于正确清理。
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
User description
关联 issue
Closes #525(修复其在 macOS 上的回退)
问题
#525 的 macOS 剪贴板恢复(commit
4b4d219)被 Android 移植 commit1ea467a连同 cfg 门控一起回退:#[cfg(not(any(macos, android, ios)))]排除出 macOS;insert()退回「只写剪贴板 + Cmd+V」,参数restore_clipboard_after_paste(即设置里的开关)被忽略。结果:设置里的「恢复剪贴板」开关在 macOS 上完全无效——无论开还是关,听写/插入后剪贴板都被留成转写文字,覆盖用户手动复制的内容。(Windows/Linux 不受影响。)
改动(单一职责)
ClipboardRestorePlan/PendingClipboardRestore/copy_to_clipboard_with_restore_plan/schedule_clipboard_restore/remember_pending_clipboard_restore/restore_clipboard_after_delay/is_latest_clipboard_restore/clear_pending_clipboard_restore/should_restore_clipboard/CLIPBOARD_RESTORE_DELAY+ 相关 imports)的 cfg 从not(any(macos,android,ios))放宽为not(any(android,ios)),重新纳入 macOS(移动端仍排除)。insert()改为:保存原剪贴板 → 写转写文字 → Cmd+V → 仅当开关开启且粘贴成功时延迟恢复;粘贴失败保留文字供手动粘贴。开 = 恢复,关 = 保持现状。macos_insert_status_after_paste。未改动的 Windows/Linux 专用项(
insert_with_clipboard_restore/paste_keys/simulate_paste(shortcut)及其insertion_success_status变体)保持 macOS 排除——它们依赖 enigo /PasteShortcut,macOS 走固定 Cmd+V。测试
macOS 下 lib 干净编译(无重复定义/缺符号);
restore_clipboard_after_paste不再是 unused 警告。需真机验证
macOS 出测试版号 dmg,验证「恢复剪贴板」开关两态:开 → 听写后剪贴板恢复为原内容;关 → 保持转写文字。
PR Type
Bug fix
Description
Restore macOS clipboard restore toggle functionality (regression from 建议:插入语音转写文字后恢复用户剪贴板 #525)
Re-enable shared clipboard restore mechanism for macOS (relaxed cfg gates)
Modify macOS insert() to save original clipboard and conditionally restore based on toggle
Remove obsolete macos_insert_status_after_paste function
Diagram Walkthrough
flowchart LR A["Save original clipboard"] --> B["Write transcribed text"] B --> C["Cmd+V paste"] C --> D{"Restore toggle ON\?"} D -- Yes --> E["Schedule delayed restore"] D -- No --> F["Keep transcribed text"] E --> G["Restore original after delay"]File Walkthrough
insertion.rs
Fix macOS clipboard restore toggle in insertionopenless-all/app/src-tauri/src/insertion.rs
cfgfromnot(any(macos, android, ios))tonot(any(android,ios))for clipboard restore mechanisminsert()to save original clipboard and conditionallyrestore after paste
macos_insert_status_after_pastehelper function