fix(macos): keep overlay interactive when mpv loses foreground

- Track overlay mouse interaction state via IPC setIgnoreMouseEvents hook
- Skip macOS hide/passthrough when overlayInteractionActive is set
- Focus overlay window so lookup keys reach it during interaction
- Record mpv duration events into AniList media state for threshold checks
This commit is contained in:
2026-05-16 18:48:45 -07:00
parent 215e0f804b
commit fe201a2d2f
18 changed files with 425 additions and 4 deletions
+27
View File
@@ -2069,6 +2069,7 @@ const overlayVisibilityRuntime = createOverlayVisibilityRuntimeService(
getModalActive: () => overlayModalInputState.getModalInputExclusive(),
getVisibleOverlayVisible: () => overlayManager.getVisibleOverlayVisible(),
getForceMousePassthrough: () => appState.statsOverlayVisible,
getOverlayInteractionActive: () => visibleOverlayInteractionActive,
getWindowTracker: () => appState.windowTracker,
getLastKnownWindowsForegroundProcessName: () => lastWindowsVisibleOverlayForegroundProcessName,
getWindowsOverlayProcessName: () => path.parse(process.execPath).name.toLowerCase(),
@@ -2123,6 +2124,7 @@ let windowsVisibleOverlayZOrderSyncQueued = false;
let windowsVisibleOverlayForegroundPollInterval: ReturnType<typeof setInterval> | null = null;
let lastWindowsVisibleOverlayForegroundProcessName: string | null = null;
let lastWindowsVisibleOverlayBlurredAtMs = 0;
let visibleOverlayInteractionActive = false;
function clearVisibleOverlayBlurRefreshTimeouts(): void {
for (const timeout of visibleOverlayBlurRefreshTimeouts) {
@@ -3045,6 +3047,7 @@ const {
resetAnilistMediaTracking,
getAnilistMediaGuessRuntimeState,
setAnilistMediaGuessRuntimeState,
recordAnilistMediaDuration,
resetAnilistMediaGuessState,
maybeProbeAnilistDuration,
ensureAnilistMediaGuess,
@@ -3148,6 +3151,13 @@ const {
);
},
},
recordMediaDurationMainDeps: {
getCurrentMediaKey: () => getCurrentAnilistMediaKey(),
getState: () => getAnilistMediaGuessRuntimeState(),
setState: (state) => {
setAnilistMediaGuessRuntimeState(state);
},
},
resetMediaGuessStateMainDeps: {
setMediaGuess: (value) => {
anilistMediaGuessRuntimeState = transitionAnilistMediaGuessRuntimeState(
@@ -3987,6 +3997,9 @@ const {
void reportJellyfinRemoteStopped();
},
maybeRunAnilistPostWatchUpdate: (options) => maybeRunAnilistPostWatchUpdate(options),
recordAnilistMediaDuration: (durationSec) => {
recordAnilistMediaDuration(durationSec);
},
logSubtitleTimingError: (message, error) => logger.error(message, error),
broadcastToOverlayWindows: (channel, payload) => {
broadcastToOverlayWindows(channel, payload);
@@ -5128,6 +5141,20 @@ const { registerIpcRuntimeHandlers } = composeIpcRuntimeHandlers({
onOverlayModalOpened: (modal) => {
overlayModalRuntime.notifyOverlayModalOpened(modal);
},
onOverlayMouseInteractionChanged: (active, senderWindow) => {
const mainWindow = overlayManager.getMainWindow();
if (!mainWindow || senderWindow !== mainWindow) {
return;
}
if (visibleOverlayInteractionActive === active) {
if (active && process.platform === 'darwin' && !mainWindow.isFocused()) {
overlayVisibilityRuntime.updateVisibleOverlayVisibility();
}
return;
}
visibleOverlayInteractionActive = active;
overlayVisibilityRuntime.updateVisibleOverlayVisibility();
},
onYoutubePickerResolve: (request) => youtubeFlowRuntime.resolveActivePicker(request),
openYomitanSettings: () => openYomitanSettings(),
quitApp: () => requestAppQuit(),