fix: address CodeRabbit review comments

This commit is contained in:
2026-04-27 20:10:33 -07:00
parent bdbacb1304
commit 35ba8778f3
13 changed files with 226 additions and 54 deletions

View File

@@ -18,6 +18,8 @@ test('on will quit cleanup handler runs all cleanup steps', () => {
stopTexthookerService: () => calls.push('stop-texthooker'),
clearWindowsVisibleOverlayForegroundPollLoop: () =>
calls.push('clear-windows-visible-overlay-poll'),
clearLinuxMpvFullscreenOverlayRefreshTimeouts: () =>
calls.push('clear-linux-mpv-fullscreen-overlay-refresh-timeouts'),
destroyMainOverlayWindow: () => calls.push('destroy-main-overlay-window'),
destroyModalOverlayWindow: () => calls.push('destroy-modal-overlay-window'),
destroyYomitanParserWindow: () => calls.push('destroy-yomitan-window'),
@@ -42,10 +44,11 @@ test('on will quit cleanup handler runs all cleanup steps', () => {
});
cleanup();
assert.equal(calls.length, 29);
assert.equal(calls.length, 30);
assert.equal(calls[0], 'destroy-tray');
assert.equal(calls[calls.length - 1], 'stop-discord-presence');
assert.ok(calls.includes('clear-windows-visible-overlay-poll'));
assert.ok(calls.includes('clear-linux-mpv-fullscreen-overlay-refresh-timeouts'));
assert.ok(calls.indexOf('flush-mpv-log') < calls.indexOf('destroy-socket'));
});

View File

@@ -7,6 +7,7 @@ export function createOnWillQuitCleanupHandler(deps: {
stopSubtitleWebsocket: () => void;
stopTexthookerService: () => void;
clearWindowsVisibleOverlayForegroundPollLoop: () => void;
clearLinuxMpvFullscreenOverlayRefreshTimeouts: () => void;
destroyMainOverlayWindow: () => void;
destroyModalOverlayWindow: () => void;
destroyYomitanParserWindow: () => void;
@@ -38,6 +39,7 @@ export function createOnWillQuitCleanupHandler(deps: {
deps.stopSubtitleWebsocket();
deps.stopTexthookerService();
deps.clearWindowsVisibleOverlayForegroundPollLoop();
deps.clearLinuxMpvFullscreenOverlayRefreshTimeouts();
deps.destroyMainOverlayWindow();
deps.destroyModalOverlayWindow();
deps.destroyYomitanParserWindow();

View File

@@ -20,6 +20,8 @@ test('cleanup deps builder returns handlers that guard optional runtime objects'
stopTexthookerService: () => calls.push('stop-texthooker'),
clearWindowsVisibleOverlayForegroundPollLoop: () =>
calls.push('clear-windows-visible-overlay-foreground-poll-loop'),
clearLinuxMpvFullscreenOverlayRefreshTimeouts: () =>
calls.push('clear-linux-mpv-fullscreen-overlay-refresh-timeouts'),
getMainOverlayWindow: () => ({
isDestroyed: () => false,
destroy: () => calls.push('destroy-main-overlay-window'),
@@ -88,6 +90,7 @@ test('cleanup deps builder returns handlers that guard optional runtime objects'
assert.ok(calls.includes('stop-jellyfin-remote'));
assert.ok(calls.includes('stop-discord-presence'));
assert.ok(calls.includes('clear-windows-visible-overlay-foreground-poll-loop'));
assert.ok(calls.includes('clear-linux-mpv-fullscreen-overlay-refresh-timeouts'));
assert.equal(reconnectTimer, null);
assert.equal(immersionTracker, null);
});
@@ -103,6 +106,7 @@ test('cleanup deps builder skips destroyed yomitan window', () => {
stopSubtitleWebsocket: () => {},
stopTexthookerService: () => {},
clearWindowsVisibleOverlayForegroundPollLoop: () => {},
clearLinuxMpvFullscreenOverlayRefreshTimeouts: () => {},
getMainOverlayWindow: () => ({
isDestroyed: () => true,
destroy: () => calls.push('destroy-main-overlay-window'),

View File

@@ -26,6 +26,7 @@ export function createBuildOnWillQuitCleanupDepsHandler(deps: {
stopSubtitleWebsocket: () => void;
stopTexthookerService: () => void;
clearWindowsVisibleOverlayForegroundPollLoop: () => void;
clearLinuxMpvFullscreenOverlayRefreshTimeouts: () => void;
getMainOverlayWindow: () => DestroyableWindow | null;
clearMainOverlayWindow: () => void;
getModalOverlayWindow: () => DestroyableWindow | null;
@@ -67,6 +68,8 @@ export function createBuildOnWillQuitCleanupDepsHandler(deps: {
stopTexthookerService: () => deps.stopTexthookerService(),
clearWindowsVisibleOverlayForegroundPollLoop: () =>
deps.clearWindowsVisibleOverlayForegroundPollLoop(),
clearLinuxMpvFullscreenOverlayRefreshTimeouts: () =>
deps.clearLinuxMpvFullscreenOverlayRefreshTimeouts(),
destroyMainOverlayWindow: () => {
const window = deps.getMainOverlayWindow();
if (!window) return;

View File

@@ -22,6 +22,7 @@ test('composeStartupLifecycleHandlers returns callable startup lifecycle handler
stopSubtitleWebsocket: () => {},
stopTexthookerService: () => {},
clearWindowsVisibleOverlayForegroundPollLoop: () => {},
clearLinuxMpvFullscreenOverlayRefreshTimeouts: () => {},
getMainOverlayWindow: () => null,
clearMainOverlayWindow: () => {},
getModalOverlayWindow: () => null,

View File

@@ -0,0 +1,47 @@
import assert from 'node:assert/strict';
import test from 'node:test';
import {
clearLinuxMpvFullscreenOverlayRefreshTimeouts,
scheduleLinuxVisibleOverlayFullscreenRefreshBurst,
} from './linux-mpv-fullscreen-overlay-refresh';
test('linux mpv fullscreen overlay refresh burst schedules overlay refresh work on linux', async () => {
const originalPlatformDescriptor = Object.getOwnPropertyDescriptor(process, 'platform');
Object.defineProperty(process, 'platform', {
configurable: true,
value: 'linux',
});
const calls: string[] = [];
try {
scheduleLinuxVisibleOverlayFullscreenRefreshBurst({
overlayManager: {
getMainWindow: () =>
({
hide: () => calls.push('hide'),
isDestroyed: () => false,
isVisible: () => true,
showInactive: () => calls.push('showInactive'),
}) as never,
getVisibleOverlayVisible: () => true,
},
overlayVisibilityRuntime: {
updateVisibleOverlayVisibility: () => calls.push('updateVisibleOverlayVisibility'),
},
ensureOverlayWindowLevel: () => calls.push('ensureOverlayWindowLevel'),
});
await new Promise((resolve) => setTimeout(resolve, 10));
assert.ok(calls.includes('updateVisibleOverlayVisibility'));
assert.ok(calls.includes('hide'));
assert.ok(calls.includes('showInactive'));
assert.ok(calls.includes('ensureOverlayWindowLevel'));
} finally {
clearLinuxMpvFullscreenOverlayRefreshTimeouts();
if (originalPlatformDescriptor) {
Object.defineProperty(process, 'platform', originalPlatformDescriptor);
}
}
});

View File

@@ -0,0 +1,68 @@
type LinuxMpvFullscreenOverlayWindow = {
hide: () => void;
isDestroyed: () => boolean;
isVisible: () => boolean;
showInactive: () => void;
};
export type LinuxMpvFullscreenOverlayRefreshDeps = {
overlayManager: {
getMainWindow: () => LinuxMpvFullscreenOverlayWindow | null;
getVisibleOverlayVisible: () => boolean;
};
overlayVisibilityRuntime: {
updateVisibleOverlayVisibility: () => void;
};
ensureOverlayWindowLevel: (window: LinuxMpvFullscreenOverlayWindow) => void;
};
const LINUX_MPV_FULLSCREEN_OVERLAY_REFRESH_DELAYS_MS = [0, 50, 150, 300, 600] as const;
let linuxMpvFullscreenOverlayRefreshTimeouts: Array<ReturnType<typeof setTimeout>> = [];
function clearLinuxMpvFullscreenOverlayRefreshTimeouts(): void {
for (const timeout of linuxMpvFullscreenOverlayRefreshTimeouts) {
clearTimeout(timeout);
}
linuxMpvFullscreenOverlayRefreshTimeouts = [];
}
function refreshLinuxVisibleOverlayAfterMpvFullscreenChange(
deps: LinuxMpvFullscreenOverlayRefreshDeps,
): void {
if (process.platform !== 'linux' || !deps.overlayManager.getVisibleOverlayVisible()) {
return;
}
deps.overlayVisibilityRuntime.updateVisibleOverlayVisibility();
const mainWindow = deps.overlayManager.getMainWindow();
if (!mainWindow || mainWindow.isDestroyed() || !mainWindow.isVisible()) {
return;
}
mainWindow.hide();
mainWindow.showInactive();
deps.ensureOverlayWindowLevel(mainWindow);
}
export function scheduleLinuxVisibleOverlayFullscreenRefreshBurst(
deps: LinuxMpvFullscreenOverlayRefreshDeps,
): void {
if (process.platform !== 'linux') {
return;
}
clearLinuxMpvFullscreenOverlayRefreshTimeouts();
for (const delayMs of LINUX_MPV_FULLSCREEN_OVERLAY_REFRESH_DELAYS_MS) {
const refreshTimeout = setTimeout(() => {
linuxMpvFullscreenOverlayRefreshTimeouts = linuxMpvFullscreenOverlayRefreshTimeouts.filter(
(timeout) => timeout !== refreshTimeout,
);
refreshLinuxVisibleOverlayAfterMpvFullscreenChange(deps);
}, delayMs);
refreshTimeout.unref?.();
linuxMpvFullscreenOverlayRefreshTimeouts.push(refreshTimeout);
}
}
export { clearLinuxMpvFullscreenOverlayRefreshTimeouts };