From 53c2e6353ae331843a120530df9fc4e2cae48e0c Mon Sep 17 00:00:00 2001 From: sudacode Date: Mon, 9 Feb 2026 23:31:30 -0800 Subject: [PATCH] refactor: extract runtime config access helpers --- package.json | 2 +- .../services/runtime-config-service.test.ts | 98 +++++++++++++++++++ src/core/services/runtime-config-service.ts | 50 ++++++++++ src/main.ts | 29 ++++-- 4 files changed, 170 insertions(+), 9 deletions(-) create mode 100644 src/core/services/runtime-config-service.test.ts create mode 100644 src/core/services/runtime-config-service.ts diff --git a/package.json b/package.json index dfc2738..e8bdad3 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", + "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", "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/runtime-config-service.test.ts b/src/core/services/runtime-config-service.test.ts new file mode 100644 index 0000000..2cde904 --- /dev/null +++ b/src/core/services/runtime-config-service.test.ts @@ -0,0 +1,98 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { + getInitialInvisibleOverlayVisibilityService, + isAutoUpdateEnabledRuntimeService, + shouldAutoInitializeOverlayRuntimeFromConfigService, + shouldBindVisibleOverlayToMpvSubVisibilityService, +} from "./runtime-config-service"; + +const BASE_CONFIG = { + auto_start_overlay: false, + bind_visible_overlay_to_mpv_sub_visibility: true, + invisibleOverlay: { + startupVisibility: "platform-default" as const, + }, + ankiConnect: { + behavior: { + autoUpdateNewCards: true, + }, + }, +}; + +test("getInitialInvisibleOverlayVisibilityService handles visibility + platform", () => { + assert.equal( + getInitialInvisibleOverlayVisibilityService( + { ...BASE_CONFIG, invisibleOverlay: { startupVisibility: "visible" } }, + "linux", + ), + true, + ); + assert.equal( + getInitialInvisibleOverlayVisibilityService( + { ...BASE_CONFIG, invisibleOverlay: { startupVisibility: "hidden" } }, + "darwin", + ), + false, + ); + assert.equal( + getInitialInvisibleOverlayVisibilityService(BASE_CONFIG, "linux"), + false, + ); + assert.equal( + getInitialInvisibleOverlayVisibilityService(BASE_CONFIG, "darwin"), + true, + ); +}); + +test("shouldAutoInitializeOverlayRuntimeFromConfigService respects auto start and visible startup", () => { + assert.equal( + shouldAutoInitializeOverlayRuntimeFromConfigService(BASE_CONFIG), + false, + ); + assert.equal( + shouldAutoInitializeOverlayRuntimeFromConfigService({ + ...BASE_CONFIG, + auto_start_overlay: true, + }), + true, + ); + assert.equal( + shouldAutoInitializeOverlayRuntimeFromConfigService({ + ...BASE_CONFIG, + invisibleOverlay: { startupVisibility: "visible" }, + }), + true, + ); +}); + +test("shouldBindVisibleOverlayToMpvSubVisibilityService returns config value", () => { + assert.equal(shouldBindVisibleOverlayToMpvSubVisibilityService(BASE_CONFIG), true); + assert.equal( + shouldBindVisibleOverlayToMpvSubVisibilityService({ + ...BASE_CONFIG, + bind_visible_overlay_to_mpv_sub_visibility: false, + }), + false, + ); +}); + +test("isAutoUpdateEnabledRuntimeService prefers runtime option and falls back to config", () => { + assert.equal( + isAutoUpdateEnabledRuntimeService(BASE_CONFIG, { + getOptionValue: () => false, + }), + false, + ); + assert.equal( + isAutoUpdateEnabledRuntimeService( + { + ...BASE_CONFIG, + ankiConnect: { behavior: { autoUpdateNewCards: false } }, + }, + null, + ), + false, + ); + assert.equal(isAutoUpdateEnabledRuntimeService(BASE_CONFIG, null), true); +}); diff --git a/src/core/services/runtime-config-service.ts b/src/core/services/runtime-config-service.ts new file mode 100644 index 0000000..620f438 --- /dev/null +++ b/src/core/services/runtime-config-service.ts @@ -0,0 +1,50 @@ +interface RuntimeAutoUpdateOptionManagerLike { + getOptionValue: (id: "anki.autoUpdateNewCards") => unknown; +} + +interface RuntimeConfigLike { + auto_start_overlay?: boolean; + bind_visible_overlay_to_mpv_sub_visibility: boolean; + invisibleOverlay: { + startupVisibility: "visible" | "hidden" | "platform-default"; + }; + ankiConnect?: { + behavior?: { + autoUpdateNewCards?: boolean; + }; + }; +} + +export function getInitialInvisibleOverlayVisibilityService( + config: RuntimeConfigLike, + platform: NodeJS.Platform, +): boolean { + const visibility = config.invisibleOverlay.startupVisibility; + if (visibility === "visible") return true; + if (visibility === "hidden") return false; + if (platform === "linux") return false; + return true; +} + +export function shouldAutoInitializeOverlayRuntimeFromConfigService( + config: RuntimeConfigLike, +): boolean { + if (config.auto_start_overlay === true) return true; + if (config.invisibleOverlay.startupVisibility === "visible") return true; + return false; +} + +export function shouldBindVisibleOverlayToMpvSubVisibilityService( + config: RuntimeConfigLike, +): boolean { + return config.bind_visible_overlay_to_mpv_sub_visibility; +} + +export function isAutoUpdateEnabledRuntimeService( + config: RuntimeConfigLike, + runtimeOptionsManager: RuntimeAutoUpdateOptionManagerLike | null, +): boolean { + const value = runtimeOptionsManager?.getOptionValue("anki.autoUpdateNewCards"); + if (typeof value === "boolean") return value; + return config.ankiConnect?.behavior?.autoUpdateNewCards !== false; +} diff --git a/src/main.ts b/src/main.ts index 333a735..f676bc1 100644 --- a/src/main.ts +++ b/src/main.ts @@ -143,6 +143,12 @@ import { cycleRuntimeOptionFromIpcRuntimeService, setRuntimeOptionFromIpcRuntimeService, } from "./core/services/runtime-options-runtime-service"; +import { + getInitialInvisibleOverlayVisibilityService, + isAutoUpdateEnabledRuntimeService, + shouldAutoInitializeOverlayRuntimeFromConfigService, + shouldBindVisibleOverlayToMpvSubVisibilityService, +} from "./core/services/runtime-config-service"; import { showDesktopNotification } from "./core/utils/notification"; import { openYomitanSettingsWindow } from "./core/services/yomitan-settings-service"; import { tokenizeSubtitleService } from "./core/services/tokenizer-service"; @@ -318,19 +324,26 @@ function openRuntimeOptionsPalette(): void { sendToVisibleOverlay("runtime-optio function getResolvedConfig() { return configService.getConfig(); } -function getInitialInvisibleOverlayVisibility(): boolean { const visibility = getResolvedConfig().invisibleOverlay.startupVisibility; if (visibility === "visible") return true; if (visibility === "hidden") return false; if (process.platform === "linux") return false; return true; } +function getInitialInvisibleOverlayVisibility(): boolean { + return getInitialInvisibleOverlayVisibilityService( + getResolvedConfig(), + process.platform, + ); +} -function shouldAutoInitializeOverlayRuntimeFromConfig(): boolean { const config = getResolvedConfig(); if (config.auto_start_overlay === true) return true; if (config.invisibleOverlay.startupVisibility === "visible") return true; return false; } +function shouldAutoInitializeOverlayRuntimeFromConfig(): boolean { + return shouldAutoInitializeOverlayRuntimeFromConfigService(getResolvedConfig()); +} -function shouldBindVisibleOverlayToMpvSubVisibility(): boolean { return getResolvedConfig().bind_visible_overlay_to_mpv_sub_visibility; } +function shouldBindVisibleOverlayToMpvSubVisibility(): boolean { + return shouldBindVisibleOverlayToMpvSubVisibilityService(getResolvedConfig()); +} function isAutoUpdateEnabledRuntime(): boolean { - const value = runtimeOptionsManager?.getOptionValue( - "anki.autoUpdateNewCards", + return isAutoUpdateEnabledRuntimeService( + getResolvedConfig(), + runtimeOptionsManager, ); - if (typeof value === "boolean") return value; - const config = getResolvedConfig(); - return config.ankiConnect?.behavior?.autoUpdateNewCards !== false; } function getJimakuLanguagePreference(): JimakuLanguagePreference { return getJimakuLanguagePreferenceService(() => getResolvedConfig(), DEFAULT_CONFIG.jimaku.languagePreference); }