fix(island): 命中改为"光标位置兜底轮询",修复"点开再收起就再也点不开"

用户复现路径:点开灵动岛(展开)→ 收起为胶囊 → 再点就毫无反应。
根因:固定包围盒窗口默认整块穿透,命中只在渲染进程收到 mousemove 时才打开。
Windows 下 setIgnoreMouseEvents(true, { forward: true }) 的 mousemove 转发并不总是可靠,
一旦"展开 → 收起"后转发丢失,窗口就永远停在穿透状态 —— 看得到胶囊,却怎么点都没反应。

修法(双保险,命中不再依赖"必须先收到一次 mousemove"):
- 渲染进程把**岛体矩形**上报主进程(新 IPC island:set-hit-rect,随状态/外观/形变更新,
  彻底隐藏时上报 null);
- 主进程每 120ms 用 screen.getCursorScreenPoint() 与矩形比对校正命中(窗口不可见或无矩形
  则强制穿透),并在收到矩形时立即校正一次;
- 保留渲染进程 mousemove 的快速路径(即时响应),两者使用同一矩形,结论一致;
- 渲染进程仍记住最近指针位置,状态变化时按新几何重算命中。

冒烟新增/改造(新增可注入光标位置 setHitTestCursor,避免去挪用户物理鼠标):
- 「展开 → 收起 → 再次展开(用户复现路径)」:真实点击往返,并断言每个形态都上报了命中框;
- 「命中兜底:岛体矩形随形态更新」(展开 424x230 → 收起 268x44);
- 「彻底隐藏时撤销命中框(不吞桌面点击)」;
- 「空闲细缝态命中跟随光标」(细缝上可点、移开即穿透);
- 「岛外鼠标穿透」与「收起态点击可再次展开」改为注入光标位置做确定性断言。

验证:verify:desktop 65/65 + 离线复跑 59/59;打包便携版 65/65(packaged=true);
typecheck / lint / format 全绿。
This commit is contained in:
LoveChengke committed 2026-09-12 21:49:14 +08:00
1 parent 80a0efbcfe
commit d8b0eb05c2
6 files changed
+270 -49

No files matched your search

@@ -301,6 +301,19 @@ const islandClass = computed(() => ({
let lastInteractive: boolean | null = null;
/** 最近一次已知的指针位置:岛收起/变大后用它重算命中,避免"鼠标没动就点不到" */
let lastPointer: { x: number; y: number } | null = null;
/** 最近一次上报给主进程的岛体矩形(去重,避免形变期间刷屏 IPC) */
let lastHitRectKey: string | null = null;
/** 把岛体矩形上报给主进程:主进程按光标位置兜底校正命中(不依赖 mousemove 转发) */
function reportHitRect(rect: DOMRect | null): void {
const payload = rect ? { x: rect.left, y: rect.top, width: rect.width, height: rect.height } : null;
const key = payload
? `${Math.round(payload.x)},${Math.round(payload.y)},${Math.round(payload.width)},${Math.round(payload.height)}`
: 'none';
if (key === lastHitRectKey) return;
lastHitRectKey = key;
bridge?.setHitRect?.(payload);
}
function updateInteractive(clientX: number, clientY: number): void {
lastPointer = { x: clientX, y: clientY };
@@ -327,9 +340,14 @@ function updateInteractive(clientX: number, clientY: number): void {
* 只有在收到 mousemove 时才会打开命中。岛"收起 / 展开 / 换形态"时几何变了,
* 但指针可能一动没动(没有新的 mousemove)—— 此时窗口会一直保持"穿透",
* 表现就是**收起后怎么点都打不开**(用户反馈)。所以每次状态变化都用缓存的指针位置重算一次。
*
* 另外这里会把当前岛体矩形上报主进程(`setHitRect`):即使 Windows 下 mousemove 转发丢失,
* 主进程也能按光标位置把命中校正回来 —— 这是"点开→收起→再也点不开"的兜底修复。
*/
function refreshInteractive(): void {
const node = document.querySelector('.island-card') as HTMLElement | null;
const fullyHidden = state.value?.mode === 'hidden' && !appearance.value.idleSliver;
reportHitRect(node && !fullyHidden ? node.getBoundingClientRect() : null);
if (fullyHidden) {
if (lastInteractive !== false) {
lastInteractive = false;