fix(commands): wrap tray refresh in run_on_main_thread to avoid macOS UI freeze#380
Merged
Conversation
… UI freeze set_settings 和 set_default_polish_mode 是同步 Tauri commands,跑在 IPC handler 线程。它们直接调 refresh_tray_microphone_menu,里面 tray.set_menu(...) 改 macOS NSStatusItem 必须在主线程上做。从 IPC 线程直调会触发 macOS dispatch queue 死锁,导致用户改任何偏好开关后整个 UI 永久卡死、所有按键无响应。 修复:把两处 tray 刷新都包到 app.run_on_main_thread(move || ...) dispatch 到主线程, IPC 线程立即返回不阻塞。这跟 lib.rs:558 里 start_tray_microphone_watcher 已经 在用的 pattern 完全一致(issue #169 stop_qa_hotkey_listener 同样用法)。 防御性扫描结论:整个项目里 tray.set_menu() 只在 refresh_tray_microphone_menu (lib.rs:518)一处出现,所有 5 个调用点已确认安全: - lib.rs:207 tray hover event:tray 事件本身就在主线程 - lib.rs:559 麦克风设备 watcher:已有 run_on_main_thread - lib.rs:605 tray 菜单事件:本身在主线程 - commands.rs:160 set_settings:本提交修复 - commands.rs:944 set_default_polish_mode:本提交修复 其它 sync IPC commands(set_dictation/qa/translation/switch_style/open_app_hotkey) 通过 coord.refresh_*_hotkey() 间接刷新,那些函数内部已包了 run_on_main_thread; 不碰 AppKit 的 commands 安全。
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
PR Reviewer Guide 🔍Here are some key observations to aid the review process:
|
appergb
pushed a commit
that referenced
this pull request
May 9, 2026
Beta-6 包含的合并: - #379 fix(recorder): watchdog 在 sleep 醒来后重检 stop_flag,消除停采误报 - #380 fix(commands): wrap tray refresh in run_on_main_thread 修主线程死锁 - #381 feat(ui): consolidate footer/nav, sliding indicator, hover cues, top-right saved toast - #378 拆分 coordinator 子状态机模块(间接合入) Tag: v1.2.24-6-beta-tauri 推到 main 后触发 release-tauri.yml Beta 流水线。
appergb
pushed a commit
that referenced
this pull request
May 9, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
User description
Summary
set_settings和set_default_polish_mode是同步 Tauri commands,跑在 IPC handler 线程。它们直接调refresh_tray_microphone_menu,里面tray.set_menu(...)改 macOS NSStatusItem 必须主线程;从 IPC 线程直调会触发 macOS dispatch queue 死锁,导致用户改任何偏好开关(包括 ASR 配置、划词追问历史保存、默认风格切换)后整个 UI 永久卡死、所有按键无响应。修复:把两处 tray 刷新都包到
app.run_on_main_thread(move || ...)dispatch 到主线程,IPC 线程立即返回不阻塞。这跟lib.rs:558里start_tray_microphone_watcher已经在用的 pattern 完全一致(参考 issue #169)。Root cause
PR #306(feat: microphone device selection)第一次在
set_settings加这个调用,没意识到主线程隔离;set_default_polish_mode后续也有同样模式。两处都修。Defensive sweep
整个项目里
tray.set_menu()只在refresh_tray_microphone_menu(lib.rs:518)一处出现,所有 5 个调用点已确认安全:其它 sync IPC commands(set_dictation/qa/translation/switch_style/open_app_hotkey)通过
coord.refresh_*_hotkey()间接刷新,那些函数内部已包了 run_on_main_thread;不碰 AppKit 的 commands 安全。Rust 后端无更多隐患。Test plan
PR Type
Bug fix
Description
Wrap tray refresh in
run_on_main_threadinset_settingsSame fix in
set_default_polish_modePrevent UI freeze from IPC threads calling NSStatusItem
Diagram Walkthrough
File Walkthrough
commands.rs
Dispatch tray menu refresh to main thread in settings commandsopenless-all/app/src-tauri/src/commands.rs
refresh_tray_microphone_menucall inset_settingswithapp.run_on_main_threadto dispatch to main thread.set_default_polish_mode.tray_microphonesstate parameter; now accessedvia
app.stateinside the closure.