现象
CLAUDE.md 明确"不要在 dictation 路径上 `NSApp.activate` / `activateIgnoringOtherApps`,会破坏 insertion"——但当用户麦克风权限缺失时,dictation 路径反而主动抢前台焦点:
```
用户在 Notes 写字 → 按 hotkey 录音 → 麦克风权限恰好缺
→ OpenLess 主窗口跳到前台抢走 Notes 的焦点(弹"请授权"对话框)
→ 用户骂街
```
虽然首次缺权限场景才触发,但跳焦的动作很扎眼。
复现
- macOS 第一次安装 OpenLess(或手动撤掉 mic 权限)
- 在 Notes 编辑文档
- 按 rightOption 录音
- 观察:OpenLess 主窗口跳到前台、Notes 失焦
根因
coordinator.rs:1472 的 `ensure_microphone_permission`:
```rust
fn ensure_microphone_permission(inner: &Arc) -> Result<(), String> {
// ... 缺权限路径调用:
show_main_window(&app); // ← 这里调 set_dock_visibility(true) + activateIgnoringOtherApps
return Err("缺少麦克风权限".to_string());
}
```
`show_main_window` 在 macOS 上会:
```rust
app.set_activation_policy(Regular); // dock 显
ns_app.activateIgnoringOtherApps(true); // 抢焦点
window.show();
```
CLAUDE.md 明确:
Don't NSApp.activate on the dictation path — it steals focus and breaks insertion.
`ensure_microphone_permission` 是在 `begin_session` 早期被调,属于 dictation path。
影响
- macOS 第一次缺权限场景(用户首次按 hotkey)
- 也可能在已撤回权限的场景(用户改隐私设置)
- 体感:突然跳焦、原 app 状态丢失(当前光标位置 / 输入法状态)
建议 fix
把"缺权限提示" 通路从"抢焦点 show 主窗口"改成"通知中心 + 胶囊":
```rust
// 缺 mic 权限时:
emit_capsule(inner, CapsuleState::Error, 0.0, 0,
Some("需要麦克风权限".into()), None);
// 用户点胶囊 / 等下次按时再去主窗口请求权限(用户主动唤起,不是被强夺)
```
或最低限度:去掉 `activateIgnoringOtherApps(true)`,只 `window.show()` 不抢焦点。
现象
CLAUDE.md 明确"不要在 dictation 路径上 `NSApp.activate` / `activateIgnoringOtherApps`,会破坏 insertion"——但当用户麦克风权限缺失时,dictation 路径反而主动抢前台焦点:
```
用户在 Notes 写字 → 按 hotkey 录音 → 麦克风权限恰好缺
→ OpenLess 主窗口跳到前台抢走 Notes 的焦点(弹"请授权"对话框)
→ 用户骂街
```
虽然首次缺权限场景才触发,但跳焦的动作很扎眼。
复现
根因
coordinator.rs:1472的 `ensure_microphone_permission`:```rust
fn ensure_microphone_permission(inner: &Arc) -> Result<(), String> {
// ... 缺权限路径调用:
show_main_window(&app); // ← 这里调 set_dock_visibility(true) + activateIgnoringOtherApps
return Err("缺少麦克风权限".to_string());
}
```
`show_main_window` 在 macOS 上会:
```rust
app.set_activation_policy(Regular); // dock 显
ns_app.activateIgnoringOtherApps(true); // 抢焦点
window.show();
```
CLAUDE.md 明确:
`ensure_microphone_permission` 是在 `begin_session` 早期被调,属于 dictation path。
影响
建议 fix
把"缺权限提示" 通路从"抢焦点 show 主窗口"改成"通知中心 + 胶囊":
```rust
// 缺 mic 权限时:
emit_capsule(inner, CapsuleState::Error, 0.0, 0,
Some("需要麦克风权限".into()), None);
// 用户点胶囊 / 等下次按时再去主窗口请求权限(用户主动唤起,不是被强夺)
```
或最低限度:去掉 `activateIgnoringOtherApps(true)`,只 `window.show()` 不抢焦点。