Skip to content

fix(hotkey): Esc 取消改走独立通道——修复转写/润色期间按 Esc 停不下来#853

Open
bigsongeth wants to merge 1 commit into
Open-Less:betafrom
bigsongeth:fix/esc-cancel-bypass-blocked-bridge
Open

fix(hotkey): Esc 取消改走独立通道——修复转写/润色期间按 Esc 停不下来#853
bigsongeth wants to merge 1 commit into
Open-Less:betafrom
bigsongeth:fix/esc-cancel-bypass-blocked-bridge

Conversation

@bigsongeth

@bigsongeth bigsongeth commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

User description

问题

转写(Transcribing)和 LLM 润色(Polishing)期间按 Esc 完全没有反应,必须干等整段流程跑完。#798 已经给 end_session 加了「转写 vs 取消」的 tokio::select! 赛跑、润色流也有 should_cancel 检查——但取消信号本身送不进来。

根因

Esc 的 HotkeyEvent::Cancelled 与 Pressed/Released 共用同一条 mpsc channel + 同一条 bridge 线程。bridge 为修 #468/#475 的 latch 竞态改成了串行 block_on#489),Hold 松手后 end_session 就在 bridge 线程上同步跑完整段转写 + 润色:

  1. 用户松开听写键 → bridge block_on(end_session),线程被占死;
  2. 用户按 Esc → Cancelled 进 channel 排队,没有人能 recv
  3. wait_for_processing_cancel 每 75ms 轮询的 cancelled 旗标只有 cancel_session 会设置,而 cancel_session 正等着这条被堵死的线程来调——fix(dictation): 转写阶段可中断——按 Esc 立即取消在途 ASR 请求 #798 的 select! 赛跑永远等不到信号;
  4. 流程跑完、bridge 空出来才取出 Cancelled,此时 phase 已回 Idle,cancel 变 no-op。

听写键为修饰键(默认 fn 等)时必现;自定义组合键用户不受影响(按键事件走 ComboHotkeyMonitor 的独立线程,主 monitor 的 channel 空闲)——这也是 #798 验证时没暴露的原因。

修法

CancelledHotkeyEvent 枚举中拆出(编译器强制所有平台改到位),Esc 在 tap/hook 回调里改发独立的 Sender<()>,由专用 esc-cancel-bridge 线程消费并调 cancel_session(纯同步快路径,不 await):

  • hotkey.rsHotkeyMonitor::start 新增 cancel_tx: Sender<()> 参数;macOS CGEventTap 与 Windows WH_KEYBOARD_LL 的 Esc 分支改发 cancel 通道;Linux 占位 adapter / mobile stub 同步签名。
  • hotkey_loops.rs:新增 esc_cancel_bridge_loop + spawn_esc_cancel_bridge;两个 bridge loop 删除 Cancelled 臂;Less Computer 修饰键 monitor 的 Esc 转发同样迁移(cancel_session 幂等,双 tap 重复触发无害)。

Pressed/Released 的串行化语义(#468/#475)完全不动。

验证

🤖 Generated with Claude Code


PR Type

Bug fix


Description

  • Esc 取消改走独立通道,避免被 bridge 线程阻塞

  • 新增 esc-cancel-bridge 专用线程,即时处理 Esc 事件

  • macOS/Windows Esc 回调发送至独立 Sender<()>

  • 更新各 hotkey bridge 循环,移除 Cancelled 枚举


Diagram Walkthrough

flowchart LR
  ESC["用户按 Esc"] --> cancel_tx["独立 Sender<()>"]
  cancel_tx --> esc_cancel_bridge["esc-cancel-bridge 线程"]
  esc_cancel_bridge --> cancel_session["cancel_session (立即执行)"]
  cancel_session --> set_cancelled["置 cancelled 旗标"]
  set_cancelled --> select_cancel["end_session 的 select! 赛跑命中"]
Loading

File Walkthrough

Relevant files
Enhancement
hotkey_loops.rs
Add esc-cancel-bridge loop and remove Cancelled handling 

openless-all/app/src-tauri/src/coordinator/hotkey_loops.rs

  • 新增 esc_cancel_bridge_loopspawn_esc_cancel_bridge
  • hotkey_bridge_loopless_computer_modifier_bridge_loop 移除 Cancelled
    处理
+34/-6   
coordinator.rs
Spawn esc-cancel-bridge on hotkey start                                   

openless-all/app/src-tauri/src/coordinator.rs

  • 在启动 hotkey monitor 时调用 spawn_esc_cancel_bridge
  • 传递 cancel_txHotkeyMonitor::start
+2/-1     
Bug fix
hotkey.rs
Move Esc cancel to separate channel in hotkey module         

openless-all/app/src-tauri/src/hotkey.rs

  • HotkeyEvent 枚举移除 Cancelled
  • 添加 cancel_tx: Sender<()> 参数到 start 和相关函数
  • 添加 send_cancel_or_log 函数
  • macOS/Win 平台回调使用 cancel_tx 发送取消事件
+50/-13 
hotkey.rs
Update mobile stub for HotkeyEvent changes                             

openless-all/app/src-tauri/src/mobile_stubs/hotkey.rs

  • HotkeyEvent 移除 Cancelled
  • 更新 start 函数签名
+1/-1     

Open-Less#798 给 end_session 加了「转写 vs 取消」的 select! 赛跑,但取消信号本身
送不进来:hotkey bridge 线程为修 Open-Less#468/Open-Less#475 的 latch 竞态改成了串行
block_on,Hold 松手后 end_session 在 bridge 线程上同步跑完整段转写 +
润色,期间 Esc 的 HotkeyEvent::Cancelled 只能在同一条 channel 里排队。
流程跑完后才被取出,此时 phase 已回 Idle,cancel 变 no-op —— 用户按
Esc 毫无反应,必须干等转写完成。听写键为修饰键(默认 fn 等)时必现;
组合键用户不受影响(按键事件走 ComboHotkeyMonitor 的独立线程),这也是
Open-Less#798 验证时没暴露的原因。

修法:把 Cancelled 从 HotkeyEvent 枚举拆出,Esc 在 tap/hook 回调里改发
独立的 Sender<()>,由专用 esc-cancel-bridge 线程消费并调 cancel_session
(纯同步快路径,不 await)。cancelled 旗标即时置位,end_session 的
select! 赛跑与润色流的 should_cancel 立即命中。macOS CGEventTap 与
Windows WH_KEYBOARD_LL 同步修改;Less Computer 修饰键 monitor 的 Esc
转发同样迁移。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@bigsongeth
bigsongeth marked this pull request as draft July 23, 2026 07:49
@github-actions

github-actions Bot commented Jul 23, 2026

Copy link
Copy Markdown
Contributor

PR Reviewer Guide 🔍

(Review updated until commit edcdda7)

Here are some key observations to aid the review process:

🎫 Ticket compliance analysis ❌

475 - Not compliant

Non-compliant requirements:

  • Windows 下打开 OpenLess 悬窗后,主体内容区不再异常透出底层应用内容。
  • 默认提示页、录音中状态和结果展示状态的背景透明度符合设计预期,正文内容清晰可读。
  • 悬窗圆角、边框、阴影和底部状态栏在截图复现场景中没有异常透明层叠。

489 - Not compliant

Non-compliant requirements:

  • Windows 实机:按快捷键开 capsule → 再按一次关 capsule
  • Windows 实机:按快捷键开始录音 → 再按一次停止录音
  • Windows Hold 模式短按松手 stop 行为
  • macOS 回归测试 toggle / hold 双模式
  • Linux fcitx5 路径 hotkey 行为

798 - Partially compliant

Compliant requirements:

  • 取消信号不再被桥接线程阻塞,能够被即时处理(独立通道 + 专用线程)

Non-compliant requirements:

Requires further human verification:

  • 真机复验:Windows 下按快捷键开始转写后按 Esc,观察胶囊是否立即变“已取消”
⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

录制快捷键期间按 Esc 被静默忽略

esc_cancel_bridge_loopshortcut_recording_active 为 true 时直接 continue,不再调用 cancel_session。此前 HotkeyEvent::Cancelledhotkey_bridge_loop 时没有此过滤,因此该修改改变了快捷键录制过程中按 Esc 的行为——Esc 现在完全无效。如果快捷键录制流程依赖 Esc 来退出或放弃录制,则此改动会破坏该功能。建议确认录制 UI 是否有独立的 Esc 处理路径,若无则不应跳过。

if inner.shortcut_recording_active.load(Ordering::SeqCst) {
    continue;
}
线程创建失败导致取消功能静默失效

spawn_esc_cancel_bridgestd::thread::Builder::spawn 失败时直接 .ok() 忽略错误,此时 cancel_tx 发出的 () 信号永远无法被消费,用户将无法通过 Esc 取消转写/润色。虽然实际中线程创建失败极为罕见,但一旦发生,由于日志中的 warn 级别不对用户可见,问题容易被忽略。建议至少记录一次 error 级别的日志,或考虑在 spawn 失败时回退到轮询替代方案。

pub(super) fn spawn_esc_cancel_bridge(inner: &Arc<Inner>) -> mpsc::Sender<()> {
    let (cancel_tx, cancel_rx) = mpsc::channel::<()>();
    let bridge_inner = Arc::clone(inner);
    std::thread::Builder::new()
        .name("openless-esc-cancel-bridge".into())
        .spawn(move || esc_cancel_bridge_loop(bridge_inner, cancel_rx))
        .ok();

@bigsongeth
bigsongeth marked this pull request as ready for review July 23, 2026 07:53
@github-actions

Copy link
Copy Markdown
Contributor

Persistent review updated to latest commit edcdda7

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant