Skip to content

[windows] 划词追问第二次按 Option 抓不到选区 — show_qa_window 抢前台焦点 #164

@appergb

Description

@appergb

现象

Windows 上划词追问浮窗的多轮提问场景:

  1. 选段文字 → Cmd+Shift+; 弹浮窗(OK,选区被抓到)
  2. 按 Option 录音 → 说话 → 按 Option 提交 → LLM 答案出来(OK)
  3. 再按 Option 第二次提问选区抓不到,LLM 收到的 "选区原文" 段是空的,或是 OpenLess 浮窗内容

macOS 不复现(macOS 走 `orderFrontRegardless` 不抢前台焦点)。Linux 受同样影响。

复现

  1. 在备忘录 / 浏览器选段 200 字
  2. Cmd+Shift+; → Option → 说"什么意思" → Option → 第一轮答案 OK
  3. 不动选区,按 Option 开始第二轮 → 说"用大白话" → Option
  4. 观察:LLM 答 "您没提供选中内容...",或者基于浮窗自身的 markdown 答的(可笑)

根因

lib.rs:517 的 `show_qa_window` 在 macOS 用 `orderFrontRegardless`(不抢 key window),但 Win/Linux 走的是普通 `window.show()`:

```rust
#[cfg(target_os = "macos")]
{
// ... orderFrontRegardless 不抢焦点
}
#[cfg(not(target_os = "macos"))]
if let Err(e) = window.show() {
log::warn!("[qa] show failed: {e}");
}
```

Windows 上 `window.show()` 调 `ShowWindow(SW_SHOW)`,OpenLess 浮窗成为前台。后续 `capture_selection` 在浮窗 Cmd+C fallback 路径上跑——拿到的是浮窗自己的 webview 内容(空 / 浮窗里能选中的字)。第一轮能拿到是因为浮窗刚 show 还在过渡(焦点切换有延迟),第二轮浮窗已稳定为前台 → 必然失败。

参考 `show_capsule_window_no_activate`(coordinator.rs:2415)的实现,用 `SetWindowPos` + `SWP_NOACTIVATE` 是 Windows 上"显示但不抢焦点"的标准手法。

影响

  • 平台:Windows + Linux(Linux 也走 `window.show()` 分支)
  • 频率:每次多轮提问的第二轮+ 必现
  • 用户损失:划词追问 v2 在 Win/Linux 的多轮场景实质不可用

建议 fix

把 macOS 的 `orderFrontRegardless` 思路在 Windows 上对应到 `SetWindowPos + SWP_NOACTIVATE + HWND_TOPMOST`,跟 `show_capsule_window_no_activate` 一样:

```rust
#[cfg(target_os = "windows")]
fn show_qa_window_no_activate<R: Runtime>(window: &tauri::WebviewWindow) -> bool {
use raw_window_handle::{HasWindowHandle, RawWindowHandle};
use windows::Win32::Foundation::HWND;
use windows::Win32::UI::WindowsAndMessaging::{
SetWindowPos, HWND_TOPMOST, SWP_NOACTIVATE, SWP_SHOWWINDOW, SWP_NOMOVE, SWP_NOSIZE,
};
let Ok(handle) = window.window_handle() else { return false; };
let RawWindowHandle::Win32(h) = handle.as_raw() else { return false; };
let hwnd = HWND(h.hwnd.get() as *mut _);
unsafe {
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_SHOWWINDOW | SWP_NOACTIVATE).is_ok()
}
}
```

然后 `show_qa_window` 在 Windows 分支调它代替 `window.show()`。Linux 暂保 `window.show()`(没等价 X11 API),但在 Linux 平台契约 issue 里另议。

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingpriority: highHigh prioritywindowsWindows-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