fix(overlay): keep Yomitan popup interactive on macOS/Windows (#177)

This commit is contained in:
2026-07-30 19:47:08 -07:00
committed by GitHub
parent 1995200e76
commit 89ed675935
5 changed files with 39 additions and 74 deletions
@@ -336,8 +336,8 @@ test('tick only writes interaction state on change', () => {
state.active = active;
},
});
tickLinuxOverlayPointerInteraction(deps); // off→on
tickLinuxOverlayPointerInteraction(deps); // no change
tickLinuxOverlayPointerInteraction(deps, 'linux'); // off→on
tickLinuxOverlayPointerInteraction(deps, 'linux'); // no change
assert.deepEqual(calls, [true]);
});
@@ -352,7 +352,7 @@ test('tick reapplies an unchanged inactive state when the window passthrough sta
},
});
tickLinuxOverlayPointerInteraction(deps);
tickLinuxOverlayPointerInteraction(deps, 'linux');
assert.deepEqual(calls, [false]);
});
@@ -363,7 +363,7 @@ test('tick does not flip state when suspended (returns null)', () => {
shouldSuspend: () => true,
setInteractionActive: (active) => calls.push(active),
});
tickLinuxOverlayPointerInteraction(deps);
tickLinuxOverlayPointerInteraction(deps, 'linux');
assert.deepEqual(calls, []);
});
@@ -379,10 +379,26 @@ test('tick clears active hover while a separate SubMiner window suppresses overl
});
state.active = true;
tickLinuxOverlayPointerInteraction(deps);
tickLinuxOverlayPointerInteraction(deps, 'linux');
assert.deepEqual(calls, [false]);
});
test('tick never clears interaction state on macOS/Windows, where renderer hover owns it', () => {
for (const platform of ['darwin', 'win32'] as const) {
const calls: boolean[] = [];
// Pointer is off the measured subtitle rect (e.g. sitting on a Yomitan popup) while the
// renderer has marked the overlay interactive.
const { deps } = makeDeps({
getCursorScreenPoint: () => ({ x: 200, y: 200 }),
getInteractionActive: () => true,
setInteractionActive: (active) => calls.push(active),
});
tickLinuxOverlayPointerInteraction(deps, platform);
assert.deepEqual(calls, [], `expected no interaction writes on ${platform}`);
}
});
test('tick skips cursor-driven mouse-ignore toggles when Linux input shape owns hit rects', () => {
const calls: boolean[] = [];
const { deps } = makeDeps({
@@ -391,7 +407,7 @@ test('tick skips cursor-driven mouse-ignore toggles when Linux input shape owns
setInteractionActive: (active) => calls.push(active),
});
tickLinuxOverlayPointerInteraction(deps);
tickLinuxOverlayPointerInteraction(deps, 'linux');
assert.deepEqual(calls, []);
});
@@ -270,7 +270,16 @@ export function resolveDesiredOverlayInteractive(
);
}
export function tickLinuxOverlayPointerInteraction(deps: LinuxOverlayPointerInteractionDeps): void {
export function tickLinuxOverlayPointerInteraction(
deps: LinuxOverlayPointerInteractionDeps,
platform: NodeJS.Platform = process.platform,
): void {
// Linux-only. Windows/macOS drive interaction state from renderer hover (setIgnoreMouseEvents),
// which knows about Yomitan popups and modals that sit off the measured subtitle rects. This
// cursor poll only hit-tests those rects, so running it elsewhere would clear interaction state
// (and re-enable window passthrough) whenever a measurement lands while the pointer is on a
// popup, swallowing popup clicks and scroll.
if (platform !== 'linux') return;
if (deps.shouldUseInputShape?.()) return;
const desired = resolveDesiredOverlayInteractive(deps);
if (desired === null) return;
@@ -827,6 +827,9 @@ export function createVisibleOverlayInteractionRuntime(deps: VisibleOverlayInter
};
function tickLinuxOverlayPointerInteractionNow(): void {
if (process.platform !== 'linux') {
return;
}
if (applyLinuxOverlayInputShapeFromLatestMeasurement()) {
return;
}