Description 现象
在 Windows 运行态里,Selection Ask / 划词追问 的 QA hotkey 如果通过设置页修改或禁用,前端 prefs.qaHotkey 会更新,但后端运行中的 QaHotkeyMonitor 不一定同步刷新。
结果是旧热键可能继续生效,或者 UI 显示已禁用 / 已改键,但用户按旧组合键仍能打开 QA panel。
这次本地调查时,代码路径显示 openless-all/app/src-tauri/src/commands.rs 的 set_settings 只调用了 update_hotkey_binding(),没有调用 update_qa_hotkey_binding()。
运行态证据:最新 debug build 启动后,日志可以看到 QA hotkey listener installed on main thread;手工发送热键时,日志命中 QA hotkey edge (panel_visible=false) 与 QA panel opened,说明监听器本身工作,但设置写回链路与监听器生命周期脱节。
问题层面 / Problem layer
层面判定:应用逻辑层 / cross-platform settings-to-runtime contract bug 。
当前证据来自 Windows,但根因不是 Windows API 特有逻辑,而是通用设置写回链路漏掉了 QA 监听器刷新。
更准确的判断是:全平台逻辑问题,当前先在 Windows 运行态被观测到 ;是否在 macOS 复现,需要再补运行态验证,但 issue 目标不应收窄成纯 Windows-only bug。
证据
代码证据:openless-all/app/src-tauri/src/commands.rs
set_settings(...) 走统一偏好写回,但之前只刷新主听写 hotkey。
QA 专用刷新逻辑只存在于 set_qa_hotkey(...)。
日志证据(Windows, latest debug run)
[coord] QA hotkey listener installed on main thread (after 1 attempt(s))
[coord] QA hotkey edge (panel_visible=false)
[coord] QA panel opened (awaiting Option to record)
这说明“监听器能工作”与“设置变更能否更新监听器”是两条不同生命周期;当前问题出在后者。
与 macOS 原始设计意图的偏差
macOS 上原始设计意图是:用户改掉 QA shortcut 后,运行态监听器应立刻切到新绑定,行为与 UI 保持 single source of truth。
当前 Tauri 实现把 QA hotkey 拆成了单独 monitor,但通用设置写回没有承担这条 monitor 的刷新责任,导致“偏好已变更”与“运行态仍持有旧注册”分离。
这不是单纯的 API 漏调,更是生命周期边界没有从 macOS 语义完整移植到跨平台实现。
生命周期差异 / Platform lifecycle notes
Windows 上全局热键注册是显式 runtime object;一旦注册成功,就会持续存活直到主动更新或卸载,所以更容易把这个 contract 缺口暴露出来。
macOS 即使最终表现相同,它暴露问题的节奏也可能不同,因为前台 app、global monitor、window activation 的系统路径不同。
这条 issue 的重点不是“哪个平台更容易坏”,而是设置生命周期与监听器生命周期没有被统一建模 。
5 Whys
为什么用户改了 QA hotkey 后旧热键还可能生效?
因为运行中的 QaHotkeyMonitor 没有被重新注册。
为什么 QaHotkeyMonitor 没有重注册?
因为通用设置入口 set_settings 没有调用 update_qa_hotkey_binding()。
为什么只有 set_qa_hotkey 调用了 QA 刷新?
因为实现上把 QA hotkey 视为“专门命令负责”的特例,但设置页实际又通过通用 set_settings 持久化 qaHotkey。
为什么会出现两条写回路径并且语义不一致?
因为 Tauri/React 层把偏好持久化与运行态监听器刷新拆开了,但没有建立统一 contract。
为什么这个 contract 在 Windows 上更容易暴露?
因为 Windows 下全局热键注册是显式生命周期对象;一旦注册成功,就会持续存活直到主动更新或卸载。macOS 原始设计更偏向“设置即生效”的产品语义,但跨平台移植时没有把这层生命周期一致性补齐。
影响
划词追问 / selection ask / QA panel 的入口行为变得不可预测,用户会遇到 “UI state != runtime state”。
测试与回归也会被误导:看起来设置已经保存,但真实触发仍旧跟旧配置走。
对后续 Windows 行为对齐 macOS 的工作有干扰,因为我们无法信任偏好状态是否真实落到监听器。
建议接受标准
TODO / 不确定项
TODO: 需要确认是否还有其他入口会绕过 set_qa_hotkey 直接写 prefs.qaHotkey。
TODO: 需要确认 macOS 当前是否也能通过相同路径复现,还是仅在 Windows 先暴露。
TODO: 需要确认 release build 与 debug build 在监听器更新时的行为是否完全一致。
Reactions are currently unavailable
You can’t perform that action at this time.
现象
Selection Ask/划词追问的 QA hotkey 如果通过设置页修改或禁用,前端prefs.qaHotkey会更新,但后端运行中的QaHotkeyMonitor不一定同步刷新。openless-all/app/src-tauri/src/commands.rs的set_settings只调用了update_hotkey_binding(),没有调用update_qa_hotkey_binding()。QA hotkey listener installed on main thread;手工发送热键时,日志命中QA hotkey edge (panel_visible=false)与QA panel opened,说明监听器本身工作,但设置写回链路与监听器生命周期脱节。问题层面 / Problem layer
证据
openless-all/app/src-tauri/src/commands.rsset_settings(...)走统一偏好写回,但之前只刷新主听写 hotkey。set_qa_hotkey(...)。[coord] QA hotkey listener installed on main thread (after 1 attempt(s))[coord] QA hotkey edge (panel_visible=false)[coord] QA panel opened (awaiting Option to record)与 macOS 原始设计意图的偏差
生命周期差异 / Platform lifecycle notes
5 Whys
QaHotkeyMonitor没有被重新注册。QaHotkeyMonitor没有重注册?set_settings没有调用update_qa_hotkey_binding()。set_qa_hotkey调用了 QA 刷新?set_settings持久化qaHotkey。影响
划词追问 / selection ask / QA panel的入口行为变得不可预测,用户会遇到 “UI state != runtime state”。建议接受标准
set_settings写入qaHotkey变更时,运行态 QA 监听器与主听写监听器一样立即刷新。TODO / 不确定项
set_qa_hotkey直接写prefs.qaHotkey。