From ff389208c8eeec260ecd17a12283c26c86cfdfbf Mon Sep 17 00:00:00 2001 From: sudacode Date: Mon, 9 Feb 2026 23:37:29 -0800 Subject: [PATCH] refactor: extract overlay visibility facade runtime --- package.json | 2 +- .../overlay-visibility-facade-service.test.ts | 63 ++++++++++++++++++ .../overlay-visibility-facade-service.ts | 64 +++++++++++++++++++ src/main.ts | 48 ++++++++------ 4 files changed, 156 insertions(+), 21 deletions(-) create mode 100644 src/core/services/overlay-visibility-facade-service.test.ts create mode 100644 src/core/services/overlay-visibility-facade-service.ts diff --git a/package.json b/package.json index 9c23b35..8b7d951 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/numeric-shortcut-session-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", + "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/numeric-shortcut-session-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", "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-visibility-facade-service.test.ts b/src/core/services/overlay-visibility-facade-service.test.ts new file mode 100644 index 0000000..e234d2b --- /dev/null +++ b/src/core/services/overlay-visibility-facade-service.test.ts @@ -0,0 +1,63 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { + OverlayVisibilityFacadeDeps, + setVisibleOverlayVisibleRuntimeFacadeService, + toggleInvisibleOverlayRuntimeFacadeService, + toggleVisibleOverlayRuntimeFacadeService, +} from "./overlay-visibility-facade-service"; + +function makeDeps(initialVisible = false, initialInvisible = false): { + deps: OverlayVisibilityFacadeDeps; + getState: () => { visible: boolean; invisible: boolean; mpvSubVisible: boolean | null }; +} { + let visible = initialVisible; + let invisible = initialInvisible; + let mpvSubVisible: boolean | null = null; + + const deps: OverlayVisibilityFacadeDeps = { + getVisibleOverlayVisible: () => visible, + getInvisibleOverlayVisible: () => invisible, + setVisibleOverlayVisibleState: (value) => { + visible = value; + }, + setInvisibleOverlayVisibleState: (value) => { + invisible = value; + }, + updateVisibleOverlayVisibility: () => {}, + updateInvisibleOverlayVisibility: () => {}, + syncInvisibleOverlayMousePassthrough: () => {}, + shouldBindVisibleOverlayToMpvSubVisibility: () => true, + isMpvConnected: () => true, + setMpvSubVisibility: (value) => { + mpvSubVisible = value; + }, + }; + + return { + deps, + getState: () => ({ visible, invisible, mpvSubVisible }), + }; +} + +test("setVisibleOverlayVisibleRuntimeFacadeService updates visible state and mpv subtitle visibility", () => { + const { deps, getState } = makeDeps(false, true); + setVisibleOverlayVisibleRuntimeFacadeService(true, deps); + assert.deepEqual(getState(), { + visible: true, + invisible: true, + mpvSubVisible: false, + }); +}); + +test("toggleVisibleOverlayRuntimeFacadeService flips visible overlay state", () => { + const { deps, getState } = makeDeps(false, false); + toggleVisibleOverlayRuntimeFacadeService(deps); + assert.equal(getState().visible, true); +}); + +test("toggleInvisibleOverlayRuntimeFacadeService flips invisible overlay state", () => { + const { deps, getState } = makeDeps(false, false); + toggleInvisibleOverlayRuntimeFacadeService(deps); + assert.equal(getState().invisible, true); +}); diff --git a/src/core/services/overlay-visibility-facade-service.ts b/src/core/services/overlay-visibility-facade-service.ts new file mode 100644 index 0000000..309d7e2 --- /dev/null +++ b/src/core/services/overlay-visibility-facade-service.ts @@ -0,0 +1,64 @@ +import { + setInvisibleOverlayVisibleService, + setVisibleOverlayVisibleService, +} from "./overlay-visibility-runtime-service"; + +export interface OverlayVisibilityFacadeDeps { + getVisibleOverlayVisible: () => boolean; + getInvisibleOverlayVisible: () => boolean; + setVisibleOverlayVisibleState: (visible: boolean) => void; + setInvisibleOverlayVisibleState: (visible: boolean) => void; + updateVisibleOverlayVisibility: () => void; + updateInvisibleOverlayVisibility: () => void; + syncInvisibleOverlayMousePassthrough: () => void; + shouldBindVisibleOverlayToMpvSubVisibility: () => boolean; + isMpvConnected: () => boolean; + setMpvSubVisibility: (visible: boolean) => void; +} + +export function setVisibleOverlayVisibleRuntimeFacadeService( + visible: boolean, + deps: OverlayVisibilityFacadeDeps, +): void { + setVisibleOverlayVisibleService({ + visible, + setVisibleOverlayVisibleState: deps.setVisibleOverlayVisibleState, + updateVisibleOverlayVisibility: deps.updateVisibleOverlayVisibility, + updateInvisibleOverlayVisibility: deps.updateInvisibleOverlayVisibility, + syncInvisibleOverlayMousePassthrough: deps.syncInvisibleOverlayMousePassthrough, + shouldBindVisibleOverlayToMpvSubVisibility: + deps.shouldBindVisibleOverlayToMpvSubVisibility, + isMpvConnected: deps.isMpvConnected, + setMpvSubVisibility: deps.setMpvSubVisibility, + }); +} + +export function setInvisibleOverlayVisibleRuntimeFacadeService( + visible: boolean, + deps: OverlayVisibilityFacadeDeps, +): void { + setInvisibleOverlayVisibleService({ + visible, + setInvisibleOverlayVisibleState: deps.setInvisibleOverlayVisibleState, + updateInvisibleOverlayVisibility: deps.updateInvisibleOverlayVisibility, + syncInvisibleOverlayMousePassthrough: deps.syncInvisibleOverlayMousePassthrough, + }); +} + +export function toggleVisibleOverlayRuntimeFacadeService( + deps: OverlayVisibilityFacadeDeps, +): void { + setVisibleOverlayVisibleRuntimeFacadeService( + !deps.getVisibleOverlayVisible(), + deps, + ); +} + +export function toggleInvisibleOverlayRuntimeFacadeService( + deps: OverlayVisibilityFacadeDeps, +): void { + setInvisibleOverlayVisibleRuntimeFacadeService( + !deps.getInvisibleOverlayVisible(), + deps, + ); +} diff --git a/src/main.ts b/src/main.ts index c23de5b..371d7dd 100644 --- a/src/main.ts +++ b/src/main.ts @@ -172,10 +172,14 @@ import { } from "./core/services/overlay-window-service"; import { initializeOverlayRuntimeService } from "./core/services/overlay-runtime-init-service"; import { - setInvisibleOverlayVisibleService, - setVisibleOverlayVisibleService, syncInvisibleOverlayMousePassthroughService, } from "./core/services/overlay-visibility-runtime-service"; +import { + setInvisibleOverlayVisibleRuntimeFacadeService, + setVisibleOverlayVisibleRuntimeFacadeService, + toggleInvisibleOverlayRuntimeFacadeService, + toggleVisibleOverlayRuntimeFacadeService, +} from "./core/services/overlay-visibility-facade-service"; import { MpvIpcClient, MPV_REQUEST_ID_SECONDARY_SUB_VISIBILITY, @@ -1102,11 +1106,23 @@ function syncInvisibleOverlayMousePassthrough(): void { } function setVisibleOverlayVisible(visible: boolean): void { - setVisibleOverlayVisibleService({ - visible, - setVisibleOverlayVisibleState: (nextVisible) => { + setVisibleOverlayVisibleRuntimeFacadeService(visible, getOverlayVisibilityFacadeDeps()); +} + +function setInvisibleOverlayVisible(visible: boolean): void { + setInvisibleOverlayVisibleRuntimeFacadeService(visible, getOverlayVisibilityFacadeDeps()); +} + +function getOverlayVisibilityFacadeDeps() { + return { + getVisibleOverlayVisible: () => visibleOverlayVisible, + getInvisibleOverlayVisible: () => invisibleOverlayVisible, + setVisibleOverlayVisibleState: (nextVisible: boolean) => { visibleOverlayVisible = nextVisible; }, + setInvisibleOverlayVisibleState: (nextVisible: boolean) => { + invisibleOverlayVisible = nextVisible; + }, updateVisibleOverlayVisibility: () => updateVisibleOverlayVisibility(), updateInvisibleOverlayVisibility: () => updateInvisibleOverlayVisibility(), syncInvisibleOverlayMousePassthrough: () => @@ -1114,26 +1130,18 @@ function setVisibleOverlayVisible(visible: boolean): void { shouldBindVisibleOverlayToMpvSubVisibility: () => shouldBindVisibleOverlayToMpvSubVisibility(), isMpvConnected: () => Boolean(mpvClient && mpvClient.connected), - setMpvSubVisibility: (mpvSubVisible) => { + setMpvSubVisibility: (mpvSubVisible: boolean) => { setMpvSubVisibilityRuntimeService(mpvClient, mpvSubVisible); }, - }); + }; } -function setInvisibleOverlayVisible(visible: boolean): void { - setInvisibleOverlayVisibleService({ - visible, - setInvisibleOverlayVisibleState: (nextVisible) => { - invisibleOverlayVisible = nextVisible; - }, - updateInvisibleOverlayVisibility: () => updateInvisibleOverlayVisibility(), - syncInvisibleOverlayMousePassthrough: () => - syncInvisibleOverlayMousePassthrough(), - }); +function toggleVisibleOverlay(): void { + toggleVisibleOverlayRuntimeFacadeService(getOverlayVisibilityFacadeDeps()); +} +function toggleInvisibleOverlay(): void { + toggleInvisibleOverlayRuntimeFacadeService(getOverlayVisibilityFacadeDeps()); } - -function toggleVisibleOverlay(): void { setVisibleOverlayVisible(!visibleOverlayVisible); } -function toggleInvisibleOverlay(): void { setInvisibleOverlayVisible(!invisibleOverlayVisible); } function setOverlayVisible(visible: boolean): void { setVisibleOverlayVisible(visible); } function toggleOverlay(): void { toggleVisibleOverlay(); } function handleOverlayModalClosed(modal: OverlayHostedModal): void {