Skip to content

[windows] 划词追问设置改键后 QA hotkey 监听器不刷新 #147

@Cooper-X-Oak

Description

@Cooper-X-Oak

现象

  • 在 Windows 运行态里,Selection Ask / 划词追问 的 QA hotkey 如果通过设置页修改或禁用,前端 prefs.qaHotkey 会更新,但后端运行中的 QaHotkeyMonitor 不一定同步刷新。
  • 结果是旧热键可能继续生效,或者 UI 显示已禁用 / 已改键,但用户按旧组合键仍能打开 QA panel。
  • 这次本地调查时,代码路径显示 openless-all/app/src-tauri/src/commands.rsset_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

  1. 为什么用户改了 QA hotkey 后旧热键还可能生效?
    • 因为运行中的 QaHotkeyMonitor 没有被重新注册。
  2. 为什么 QaHotkeyMonitor 没有重注册?
    • 因为通用设置入口 set_settings 没有调用 update_qa_hotkey_binding()
  3. 为什么只有 set_qa_hotkey 调用了 QA 刷新?
    • 因为实现上把 QA hotkey 视为“专门命令负责”的特例,但设置页实际又通过通用 set_settings 持久化 qaHotkey
  4. 为什么会出现两条写回路径并且语义不一致?
    • 因为 Tauri/React 层把偏好持久化与运行态监听器刷新拆开了,但没有建立统一 contract。
  5. 为什么这个 contract 在 Windows 上更容易暴露?
    • 因为 Windows 下全局热键注册是显式生命周期对象;一旦注册成功,就会持续存活直到主动更新或卸载。macOS 原始设计更偏向“设置即生效”的产品语义,但跨平台移植时没有把这层生命周期一致性补齐。

影响

  • 划词追问 / selection ask / QA panel 的入口行为变得不可预测,用户会遇到 “UI state != runtime state”。
  • 测试与回归也会被误导:看起来设置已经保存,但真实触发仍旧跟旧配置走。
  • 对后续 Windows 行为对齐 macOS 的工作有干扰,因为我们无法信任偏好状态是否真实落到监听器。

建议接受标准

  • set_settings 写入 qaHotkey 变更时,运行态 QA 监听器与主听写监听器一样立即刷新。
  • 禁用 QA hotkey 后,旧组合键不再触发 QA panel。
  • 改成新组合键后,只有新组合键生效,旧组合键失效。
  • 至少补一条最小回归验证(test / script / documented manual check),覆盖“设置写回 -> 监听器刷新”这一条链。
  • 明确记录该问题属于“全平台逻辑层”,Windows 只是当前首个证据面。

TODO / 不确定项

  • TODO: 需要确认是否还有其他入口会绕过 set_qa_hotkey 直接写 prefs.qaHotkey
  • TODO: 需要确认 macOS 当前是否也能通过相同路径复现,还是仅在 Windows 先暴露。
  • TODO: 需要确认 release build 与 debug build 在监听器更新时的行为是否完全一致。

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingwindowsWindows-specific issue

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions