From 528cf0a57ea334c519b4bdd61795f712cdb9df49 Mon Sep 17 00:00:00 2001 From: sudacode Date: Tue, 10 Feb 2026 01:25:25 -0800 Subject: [PATCH] refactor: extract overlay runtime deps bundle --- package.json | 2 +- .../overlay-runtime-deps-service.test.ts | 105 +++++++++ .../services/overlay-runtime-deps-service.ts | 174 +++++++++++++++ src/main.ts | 204 ++++++++++-------- 4 files changed, 390 insertions(+), 95 deletions(-) create mode 100644 src/core/services/overlay-runtime-deps-service.test.ts create mode 100644 src/core/services/overlay-runtime-deps-service.ts diff --git a/package.json b/package.json index c4bd3a6..d731b1d 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,7 @@ "docs:build": "vitepress build docs", "docs:preview": "vitepress preview docs --host 0.0.0.0 --port 4173 --strictPort", "test:config": "pnpm run build && node --test dist/config/config.test.js", - "test:core": "pnpm run build && node --test dist/cli/args.test.js dist/cli/help.test.js dist/core/services/cli-command-service.test.js dist/core/services/cli-command-deps-runtime-service.test.js dist/core/services/ipc-deps-runtime-service.test.js dist/core/services/anki-jimaku-ipc-deps-runtime-service.test.js dist/core/services/field-grouping-overlay-runtime-service.test.js dist/core/services/subsync-deps-runtime-service.test.js dist/core/services/numeric-shortcut-runtime-service.test.js dist/core/services/numeric-shortcut-session-service.test.js dist/core/services/overlay-visibility-facade-deps-runtime-service.test.js dist/core/services/mpv-command-ipc-deps-runtime-service.test.js dist/core/services/runtime-options-ipc-deps-runtime-service.test.js dist/core/services/tokenizer-deps-runtime-service.test.js dist/core/services/secondary-subtitle-service.test.js dist/core/services/mpv-render-metrics-service.test.js dist/core/services/mpv-runtime-service.test.js dist/core/services/runtime-options-runtime-service.test.js dist/core/services/overlay-modal-restore-service.test.js dist/core/services/runtime-config-service.test.js dist/core/services/overlay-bridge-runtime-service.test.js dist/core/services/overlay-visibility-facade-service.test.js dist/core/services/overlay-broadcast-runtime-service.test.js dist/core/services/app-ready-runtime-service.test.js dist/core/services/app-shutdown-runtime-service.test.js dist/core/services/mpv-client-deps-runtime-service.test.js dist/core/services/app-lifecycle-deps-runtime-service.test.js dist/core/services/runtime-options-manager-runtime-service.test.js dist/core/services/config-warning-runtime-service.test.js dist/core/services/app-logging-runtime-service.test.js dist/core/services/startup-resource-runtime-service.test.js dist/core/services/config-generation-runtime-service.test.js", + "test:core": "pnpm run build && node --test dist/cli/args.test.js dist/cli/help.test.js dist/core/services/cli-command-service.test.js dist/core/services/cli-command-deps-runtime-service.test.js dist/core/services/ipc-deps-runtime-service.test.js dist/core/services/anki-jimaku-ipc-deps-runtime-service.test.js dist/core/services/field-grouping-overlay-runtime-service.test.js dist/core/services/subsync-deps-runtime-service.test.js dist/core/services/numeric-shortcut-runtime-service.test.js dist/core/services/numeric-shortcut-session-service.test.js dist/core/services/overlay-visibility-facade-deps-runtime-service.test.js dist/core/services/mpv-command-ipc-deps-runtime-service.test.js dist/core/services/runtime-options-ipc-deps-runtime-service.test.js dist/core/services/tokenizer-deps-runtime-service.test.js dist/core/services/overlay-runtime-deps-service.test.js dist/core/services/secondary-subtitle-service.test.js dist/core/services/mpv-render-metrics-service.test.js dist/core/services/mpv-runtime-service.test.js dist/core/services/runtime-options-runtime-service.test.js dist/core/services/overlay-modal-restore-service.test.js dist/core/services/runtime-config-service.test.js dist/core/services/overlay-bridge-runtime-service.test.js dist/core/services/overlay-visibility-facade-service.test.js dist/core/services/overlay-broadcast-runtime-service.test.js dist/core/services/app-ready-runtime-service.test.js dist/core/services/app-shutdown-runtime-service.test.js dist/core/services/mpv-client-deps-runtime-service.test.js dist/core/services/app-lifecycle-deps-runtime-service.test.js dist/core/services/runtime-options-manager-runtime-service.test.js dist/core/services/config-warning-runtime-service.test.js dist/core/services/app-logging-runtime-service.test.js dist/core/services/startup-resource-runtime-service.test.js dist/core/services/config-generation-runtime-service.test.js", "test:subtitle": "pnpm run build && node --test dist/subtitle/stages.test.js dist/subtitle/pipeline.test.js", "generate:config-example": "pnpm run build && node dist/generate-config-example.js", "start": "pnpm run build && electron . --start", diff --git a/src/core/services/overlay-runtime-deps-service.test.ts b/src/core/services/overlay-runtime-deps-service.test.ts new file mode 100644 index 0000000..a28c24f --- /dev/null +++ b/src/core/services/overlay-runtime-deps-service.test.ts @@ -0,0 +1,105 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { + createInitializeOverlayRuntimeDepsService, + createInvisibleOverlayVisibilityDepsRuntimeService, + createOverlayWindowRuntimeDepsService, + createVisibleOverlayVisibilityDepsRuntimeService, +} from "./overlay-runtime-deps-service"; + +test("createOverlayWindowRuntimeDepsService maps runtime state providers", () => { + let visible = true; + let invisible = false; + const deps = createOverlayWindowRuntimeDepsService({ + isDev: false, + getOverlayDebugVisualizationEnabled: () => true, + ensureOverlayWindowLevel: () => {}, + onRuntimeOptionsChanged: () => {}, + setOverlayDebugVisualizationEnabled: () => {}, + getVisibleOverlayVisible: () => visible, + getInvisibleOverlayVisible: () => invisible, + tryHandleOverlayShortcutLocalFallback: () => false, + onWindowClosed: () => {}, + }); + + assert.equal(deps.isOverlayVisible("visible"), true); + assert.equal(deps.isOverlayVisible("invisible"), false); + visible = false; + invisible = true; + assert.equal(deps.isOverlayVisible("visible"), false); + assert.equal(deps.isOverlayVisible("invisible"), true); +}); + +test("createInitializeOverlayRuntimeDepsService passes through overlay init deps", () => { + const windows: any[] = []; + const deps = createInitializeOverlayRuntimeDepsService({ + backendOverride: null, + getInitialInvisibleOverlayVisibility: () => true, + createMainWindow: () => {}, + createInvisibleWindow: () => {}, + registerGlobalShortcuts: () => {}, + updateOverlayBounds: () => {}, + isVisibleOverlayVisible: () => false, + isInvisibleOverlayVisible: () => true, + updateVisibleOverlayVisibility: () => {}, + updateInvisibleOverlayVisibility: () => {}, + getOverlayWindows: () => windows as never, + syncOverlayShortcuts: () => {}, + setWindowTracker: () => {}, + getResolvedConfig: () => ({ ankiConnect: undefined }), + getSubtitleTimingTracker: () => null, + getMpvClient: () => null, + getRuntimeOptionsManager: () => null, + setAnkiIntegration: () => {}, + showDesktopNotification: () => {}, + createFieldGroupingCallback: () => async () => ({ + keepNoteId: 0, + deleteNoteId: 0, + deleteDuplicate: true, + cancelled: true, + }), + }); + + assert.equal(deps.getInitialInvisibleOverlayVisibility(), true); + assert.equal(deps.getOverlayWindows().length, 0); +}); + +test("createVisibleOverlayVisibilityDepsRuntimeService snapshots runtime values", () => { + const deps = createVisibleOverlayVisibilityDepsRuntimeService({ + getVisibleOverlayVisible: () => true, + getMainWindow: () => null, + getWindowTracker: () => null, + getTrackerNotReadyWarningShown: () => false, + setTrackerNotReadyWarningShown: () => {}, + shouldBindVisibleOverlayToMpvSubVisibility: () => true, + getPreviousSecondarySubVisibility: () => null, + setPreviousSecondarySubVisibility: () => {}, + isMpvConnected: () => false, + mpvSend: () => {}, + secondarySubVisibilityRequestId: 123, + updateOverlayBounds: () => {}, + ensureOverlayWindowLevel: () => {}, + enforceOverlayLayerOrder: () => {}, + syncOverlayShortcuts: () => {}, + }); + + assert.equal(deps.visibleOverlayVisible, true); + assert.equal(deps.secondarySubVisibilityRequestId, 123); + assert.equal(deps.mpvConnected, false); +}); + +test("createInvisibleOverlayVisibilityDepsRuntimeService snapshots runtime values", () => { + const deps = createInvisibleOverlayVisibilityDepsRuntimeService({ + getInvisibleWindow: () => null, + getVisibleOverlayVisible: () => true, + getInvisibleOverlayVisible: () => false, + getWindowTracker: () => null, + updateOverlayBounds: () => {}, + ensureOverlayWindowLevel: () => {}, + enforceOverlayLayerOrder: () => {}, + syncOverlayShortcuts: () => {}, + }); + + assert.equal(deps.visibleOverlayVisible, true); + assert.equal(deps.invisibleOverlayVisible, false); +}); diff --git a/src/core/services/overlay-runtime-deps-service.ts b/src/core/services/overlay-runtime-deps-service.ts new file mode 100644 index 0000000..d04716b --- /dev/null +++ b/src/core/services/overlay-runtime-deps-service.ts @@ -0,0 +1,174 @@ +import { BrowserWindow } from "electron"; +import { BaseWindowTracker } from "../../window-trackers"; +import { + AnkiConnectConfig, + KikuFieldGroupingChoice, + KikuFieldGroupingRequestData, + WindowGeometry, +} from "../../types"; +import { createOverlayWindowService } from "./overlay-window-service"; +import { initializeOverlayRuntimeService } from "./overlay-runtime-init-service"; +import { + updateInvisibleOverlayVisibilityService, + updateVisibleOverlayVisibilityService, +} from "./overlay-visibility-service"; + +interface MpvCommandSender { + command: Array; + request_id?: number; +} + +export interface OverlayWindowRuntimeDepsOptions { + isDev: boolean; + getOverlayDebugVisualizationEnabled: () => boolean; + ensureOverlayWindowLevel: (window: BrowserWindow) => void; + onRuntimeOptionsChanged: () => void; + setOverlayDebugVisualizationEnabled: (enabled: boolean) => void; + getVisibleOverlayVisible: () => boolean; + getInvisibleOverlayVisible: () => boolean; + tryHandleOverlayShortcutLocalFallback: (input: Electron.Input) => boolean; + onWindowClosed: (kind: "visible" | "invisible") => void; +} + +export interface InitializeOverlayRuntimeDepsOptions { + backendOverride: string | null; + getInitialInvisibleOverlayVisibility: () => boolean; + createMainWindow: () => void; + createInvisibleWindow: () => void; + registerGlobalShortcuts: () => void; + updateOverlayBounds: (geometry: WindowGeometry) => void; + isVisibleOverlayVisible: () => boolean; + isInvisibleOverlayVisible: () => boolean; + updateVisibleOverlayVisibility: () => void; + updateInvisibleOverlayVisibility: () => void; + getOverlayWindows: () => BrowserWindow[]; + syncOverlayShortcuts: () => void; + setWindowTracker: (tracker: BaseWindowTracker | null) => void; + getResolvedConfig: () => { ankiConnect?: AnkiConnectConfig }; + getSubtitleTimingTracker: () => unknown | null; + getMpvClient: () => { send?: (payload: { command: string[] }) => void } | null; + getRuntimeOptionsManager: () => { + getEffectiveAnkiConnectConfig: (config?: AnkiConnectConfig) => AnkiConnectConfig; + } | null; + setAnkiIntegration: (integration: unknown | null) => void; + showDesktopNotification: (title: string, options: { body?: string; icon?: string }) => void; + createFieldGroupingCallback: () => ( + data: KikuFieldGroupingRequestData, + ) => Promise; +} + +export interface VisibleOverlayVisibilityDepsRuntimeOptions { + getVisibleOverlayVisible: () => boolean; + getMainWindow: () => BrowserWindow | null; + getWindowTracker: () => BaseWindowTracker | null; + getTrackerNotReadyWarningShown: () => boolean; + setTrackerNotReadyWarningShown: (shown: boolean) => void; + shouldBindVisibleOverlayToMpvSubVisibility: () => boolean; + getPreviousSecondarySubVisibility: () => boolean | null; + setPreviousSecondarySubVisibility: (value: boolean | null) => void; + isMpvConnected: () => boolean; + mpvSend: (payload: MpvCommandSender) => void; + secondarySubVisibilityRequestId: number; + updateOverlayBounds: (geometry: WindowGeometry) => void; + ensureOverlayWindowLevel: (window: BrowserWindow) => void; + enforceOverlayLayerOrder: () => void; + syncOverlayShortcuts: () => void; +} + +export interface InvisibleOverlayVisibilityDepsRuntimeOptions { + getInvisibleWindow: () => BrowserWindow | null; + getVisibleOverlayVisible: () => boolean; + getInvisibleOverlayVisible: () => boolean; + getWindowTracker: () => BaseWindowTracker | null; + updateOverlayBounds: (geometry: WindowGeometry) => void; + ensureOverlayWindowLevel: (window: BrowserWindow) => void; + enforceOverlayLayerOrder: () => void; + syncOverlayShortcuts: () => void; +} + +export function createOverlayWindowRuntimeDepsService( + options: OverlayWindowRuntimeDepsOptions, +): Parameters[1] { + return { + isDev: options.isDev, + overlayDebugVisualizationEnabled: + options.getOverlayDebugVisualizationEnabled(), + ensureOverlayWindowLevel: options.ensureOverlayWindowLevel, + onRuntimeOptionsChanged: options.onRuntimeOptionsChanged, + setOverlayDebugVisualizationEnabled: + options.setOverlayDebugVisualizationEnabled, + isOverlayVisible: (kind) => + kind === "visible" + ? options.getVisibleOverlayVisible() + : options.getInvisibleOverlayVisible(), + tryHandleOverlayShortcutLocalFallback: + options.tryHandleOverlayShortcutLocalFallback, + onWindowClosed: options.onWindowClosed, + }; +} + +export function createInitializeOverlayRuntimeDepsService( + options: InitializeOverlayRuntimeDepsOptions, +): Parameters[0] { + return { + backendOverride: options.backendOverride, + getInitialInvisibleOverlayVisibility: + options.getInitialInvisibleOverlayVisibility, + createMainWindow: options.createMainWindow, + createInvisibleWindow: options.createInvisibleWindow, + registerGlobalShortcuts: options.registerGlobalShortcuts, + updateOverlayBounds: options.updateOverlayBounds, + isVisibleOverlayVisible: options.isVisibleOverlayVisible, + isInvisibleOverlayVisible: options.isInvisibleOverlayVisible, + updateVisibleOverlayVisibility: options.updateVisibleOverlayVisibility, + updateInvisibleOverlayVisibility: options.updateInvisibleOverlayVisibility, + getOverlayWindows: options.getOverlayWindows, + syncOverlayShortcuts: options.syncOverlayShortcuts, + setWindowTracker: options.setWindowTracker, + getResolvedConfig: options.getResolvedConfig, + getSubtitleTimingTracker: options.getSubtitleTimingTracker, + getMpvClient: options.getMpvClient, + getRuntimeOptionsManager: options.getRuntimeOptionsManager, + setAnkiIntegration: options.setAnkiIntegration, + showDesktopNotification: options.showDesktopNotification, + createFieldGroupingCallback: options.createFieldGroupingCallback, + }; +} + +export function createVisibleOverlayVisibilityDepsRuntimeService( + options: VisibleOverlayVisibilityDepsRuntimeOptions, +): Parameters[0] { + return { + visibleOverlayVisible: options.getVisibleOverlayVisible(), + mainWindow: options.getMainWindow(), + windowTracker: options.getWindowTracker(), + trackerNotReadyWarningShown: options.getTrackerNotReadyWarningShown(), + setTrackerNotReadyWarningShown: options.setTrackerNotReadyWarningShown, + shouldBindVisibleOverlayToMpvSubVisibility: + options.shouldBindVisibleOverlayToMpvSubVisibility(), + previousSecondarySubVisibility: options.getPreviousSecondarySubVisibility(), + setPreviousSecondarySubVisibility: options.setPreviousSecondarySubVisibility, + mpvConnected: options.isMpvConnected(), + mpvSend: options.mpvSend, + secondarySubVisibilityRequestId: options.secondarySubVisibilityRequestId, + updateOverlayBounds: options.updateOverlayBounds, + ensureOverlayWindowLevel: options.ensureOverlayWindowLevel, + enforceOverlayLayerOrder: options.enforceOverlayLayerOrder, + syncOverlayShortcuts: options.syncOverlayShortcuts, + }; +} + +export function createInvisibleOverlayVisibilityDepsRuntimeService( + options: InvisibleOverlayVisibilityDepsRuntimeOptions, +): Parameters[0] { + return { + invisibleWindow: options.getInvisibleWindow(), + visibleOverlayVisible: options.getVisibleOverlayVisible(), + invisibleOverlayVisible: options.getInvisibleOverlayVisible(), + windowTracker: options.getWindowTracker(), + updateOverlayBounds: options.updateOverlayBounds, + ensureOverlayWindowLevel: options.ensureOverlayWindowLevel, + enforceOverlayLayerOrder: options.enforceOverlayLayerOrder, + syncOverlayShortcuts: options.syncOverlayShortcuts, + }; +} diff --git a/src/main.ts b/src/main.ts index 9db5abc..66930cc 100644 --- a/src/main.ts +++ b/src/main.ts @@ -201,6 +201,12 @@ import { createOverlayVisibilityFacadeDepsRuntimeService } from "./core/services import { createMpvCommandIpcDepsRuntimeService } from "./core/services/mpv-command-ipc-deps-runtime-service"; import { createRuntimeOptionsIpcDepsRuntimeService } from "./core/services/runtime-options-ipc-deps-runtime-service"; import { createTokenizerDepsRuntimeService } from "./core/services/tokenizer-deps-runtime-service"; +import { + createInitializeOverlayRuntimeDepsService, + createInvisibleOverlayVisibilityDepsRuntimeService, + createOverlayWindowRuntimeDepsService, + createVisibleOverlayVisibilityDepsRuntimeService, +} from "./core/services/overlay-runtime-deps-service"; import { createRuntimeOptionsManagerRuntimeService } from "./core/services/runtime-options-manager-runtime-service"; import { createAppLoggingRuntimeService } from "./core/services/app-logging-runtime-service"; import { @@ -807,25 +813,28 @@ async function loadYomitanExtension(): Promise { } function createOverlayWindow(kind: "visible" | "invisible"): BrowserWindow { - return createOverlayWindowService(kind, { - isDev, - overlayDebugVisualizationEnabled, - ensureOverlayWindowLevel: (window) => ensureOverlayWindowLevel(window), - onRuntimeOptionsChanged: () => broadcastRuntimeOptionsChanged(), - setOverlayDebugVisualizationEnabled: (enabled) => - setOverlayDebugVisualizationEnabled(enabled), - isOverlayVisible: (windowKind) => - windowKind === "visible" ? visibleOverlayVisible : invisibleOverlayVisible, - tryHandleOverlayShortcutLocalFallback: (input) => - tryHandleOverlayShortcutLocalFallback(input), - onWindowClosed: (windowKind) => { - if (windowKind === "visible") { - mainWindow = null; - } else { - invisibleWindow = null; - } - }, - }); + return createOverlayWindowService( + kind, + createOverlayWindowRuntimeDepsService({ + isDev, + getOverlayDebugVisualizationEnabled: () => overlayDebugVisualizationEnabled, + ensureOverlayWindowLevel: (window) => ensureOverlayWindowLevel(window), + onRuntimeOptionsChanged: () => broadcastRuntimeOptionsChanged(), + setOverlayDebugVisualizationEnabled: (enabled) => + setOverlayDebugVisualizationEnabled(enabled), + getVisibleOverlayVisible: () => visibleOverlayVisible, + getInvisibleOverlayVisible: () => invisibleOverlayVisible, + tryHandleOverlayShortcutLocalFallback: (input) => + tryHandleOverlayShortcutLocalFallback(input), + onWindowClosed: (windowKind) => { + if (windowKind === "visible") { + mainWindow = null; + } else { + invisibleWindow = null; + } + }, + }), + ); } function createMainWindow(): BrowserWindow { mainWindow = createOverlayWindow("visible"); return mainWindow; } @@ -835,46 +844,49 @@ function initializeOverlayRuntime(): void { if (overlayRuntimeInitialized) { return; } - const result = initializeOverlayRuntimeService({ - backendOverride, - getInitialInvisibleOverlayVisibility: () => getInitialInvisibleOverlayVisibility(), - createMainWindow: () => { - createMainWindow(); - }, - createInvisibleWindow: () => { - createInvisibleWindow(); - }, - registerGlobalShortcuts: () => { - registerGlobalShortcuts(); - }, - updateOverlayBounds: (geometry) => { - updateOverlayBounds(geometry); - }, - isVisibleOverlayVisible: () => visibleOverlayVisible, - isInvisibleOverlayVisible: () => invisibleOverlayVisible, - updateVisibleOverlayVisibility: () => { - updateVisibleOverlayVisibility(); - }, - updateInvisibleOverlayVisibility: () => { - updateInvisibleOverlayVisibility(); - }, - getOverlayWindows: () => getOverlayWindows(), - syncOverlayShortcuts: () => { - syncOverlayShortcuts(); - }, - setWindowTracker: (tracker) => { - windowTracker = tracker; - }, - getResolvedConfig: () => getResolvedConfig(), - getSubtitleTimingTracker: () => subtitleTimingTracker, - getMpvClient: () => mpvClient, - getRuntimeOptionsManager: () => runtimeOptionsManager, - setAnkiIntegration: (integration) => { - ankiIntegration = integration as AnkiIntegration | null; - }, - showDesktopNotification, - createFieldGroupingCallback: () => createFieldGroupingCallback(), - }); + const result = initializeOverlayRuntimeService( + createInitializeOverlayRuntimeDepsService({ + backendOverride, + getInitialInvisibleOverlayVisibility: () => + getInitialInvisibleOverlayVisibility(), + createMainWindow: () => { + createMainWindow(); + }, + createInvisibleWindow: () => { + createInvisibleWindow(); + }, + registerGlobalShortcuts: () => { + registerGlobalShortcuts(); + }, + updateOverlayBounds: (geometry) => { + updateOverlayBounds(geometry); + }, + isVisibleOverlayVisible: () => visibleOverlayVisible, + isInvisibleOverlayVisible: () => invisibleOverlayVisible, + updateVisibleOverlayVisibility: () => { + updateVisibleOverlayVisibility(); + }, + updateInvisibleOverlayVisibility: () => { + updateInvisibleOverlayVisibility(); + }, + getOverlayWindows: () => getOverlayWindows(), + syncOverlayShortcuts: () => { + syncOverlayShortcuts(); + }, + setWindowTracker: (tracker) => { + windowTracker = tracker; + }, + getResolvedConfig: () => getResolvedConfig(), + getSubtitleTimingTracker: () => subtitleTimingTracker, + getMpvClient: () => mpvClient, + getRuntimeOptionsManager: () => runtimeOptionsManager, + setAnkiIntegration: (integration) => { + ankiIntegration = integration as AnkiIntegration | null; + }, + showDesktopNotification, + createFieldGroupingCallback: () => createFieldGroupingCallback(), + }), + ); invisibleOverlayVisible = result.invisibleOverlayVisible; overlayRuntimeInitialized = true; } @@ -1127,44 +1139,48 @@ function refreshOverlayShortcuts(): void { } function updateVisibleOverlayVisibility(): void { - updateVisibleOverlayVisibilityService({ - visibleOverlayVisible, - mainWindow, - windowTracker, - trackerNotReadyWarningShown, - setTrackerNotReadyWarningShown: (shown) => { - trackerNotReadyWarningShown = shown; - }, - shouldBindVisibleOverlayToMpvSubVisibility: - shouldBindVisibleOverlayToMpvSubVisibility(), - previousSecondarySubVisibility, - setPreviousSecondarySubVisibility: (value) => { - previousSecondarySubVisibility = value; - }, - mpvConnected: Boolean(mpvClient && mpvClient.connected), - mpvSend: (payload) => { - if (!mpvClient) return; - mpvClient.send(payload); - }, - secondarySubVisibilityRequestId: MPV_REQUEST_ID_SECONDARY_SUB_VISIBILITY, - updateOverlayBounds: (geometry) => updateOverlayBounds(geometry), - ensureOverlayWindowLevel: (window) => ensureOverlayWindowLevel(window), - enforceOverlayLayerOrder: () => enforceOverlayLayerOrder(), - syncOverlayShortcuts: () => syncOverlayShortcuts(), - }); + updateVisibleOverlayVisibilityService( + createVisibleOverlayVisibilityDepsRuntimeService({ + getVisibleOverlayVisible: () => visibleOverlayVisible, + getMainWindow: () => mainWindow, + getWindowTracker: () => windowTracker, + getTrackerNotReadyWarningShown: () => trackerNotReadyWarningShown, + setTrackerNotReadyWarningShown: (shown) => { + trackerNotReadyWarningShown = shown; + }, + shouldBindVisibleOverlayToMpvSubVisibility: () => + shouldBindVisibleOverlayToMpvSubVisibility(), + getPreviousSecondarySubVisibility: () => previousSecondarySubVisibility, + setPreviousSecondarySubVisibility: (value) => { + previousSecondarySubVisibility = value; + }, + isMpvConnected: () => Boolean(mpvClient && mpvClient.connected), + mpvSend: (payload) => { + if (!mpvClient) return; + mpvClient.send(payload); + }, + secondarySubVisibilityRequestId: MPV_REQUEST_ID_SECONDARY_SUB_VISIBILITY, + updateOverlayBounds: (geometry) => updateOverlayBounds(geometry), + ensureOverlayWindowLevel: (window) => ensureOverlayWindowLevel(window), + enforceOverlayLayerOrder: () => enforceOverlayLayerOrder(), + syncOverlayShortcuts: () => syncOverlayShortcuts(), + }), + ); } function updateInvisibleOverlayVisibility(): void { - updateInvisibleOverlayVisibilityService({ - invisibleWindow, - visibleOverlayVisible, - invisibleOverlayVisible, - windowTracker, - updateOverlayBounds: (geometry) => updateOverlayBounds(geometry), - ensureOverlayWindowLevel: (window) => ensureOverlayWindowLevel(window), - enforceOverlayLayerOrder: () => enforceOverlayLayerOrder(), - syncOverlayShortcuts: () => syncOverlayShortcuts(), - }); + updateInvisibleOverlayVisibilityService( + createInvisibleOverlayVisibilityDepsRuntimeService({ + getInvisibleWindow: () => invisibleWindow, + getVisibleOverlayVisible: () => visibleOverlayVisible, + getInvisibleOverlayVisible: () => invisibleOverlayVisible, + getWindowTracker: () => windowTracker, + updateOverlayBounds: (geometry) => updateOverlayBounds(geometry), + ensureOverlayWindowLevel: (window) => ensureOverlayWindowLevel(window), + enforceOverlayLayerOrder: () => enforceOverlayLayerOrder(), + syncOverlayShortcuts: () => syncOverlayShortcuts(), + }), + ); } function syncInvisibleOverlayMousePassthrough(): void {