From 35c2dd0e32a0911ab54739b56850898d24006643 Mon Sep 17 00:00:00 2001 From: sudacode Date: Mon, 9 Feb 2026 23:19:36 -0800 Subject: [PATCH] refactor: extract runtime option ipc helpers --- package.json | 2 +- .../runtime-options-runtime-service.test.ts | 53 +++++++++++++++ .../runtime-options-runtime-service.ts | 64 +++++++++++++++++++ src/main.ts | 46 +++++-------- 4 files changed, 135 insertions(+), 30 deletions(-) create mode 100644 src/core/services/runtime-options-runtime-service.test.ts create mode 100644 src/core/services/runtime-options-runtime-service.ts diff --git a/package.json b/package.json index af185f4..37cec07 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", + "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", "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-options-runtime-service.test.ts b/src/core/services/runtime-options-runtime-service.test.ts new file mode 100644 index 0000000..cd0b742 --- /dev/null +++ b/src/core/services/runtime-options-runtime-service.test.ts @@ -0,0 +1,53 @@ +import test from "node:test"; +import assert from "node:assert/strict"; +import { + applyRuntimeOptionResultRuntimeService, + cycleRuntimeOptionFromIpcRuntimeService, + setRuntimeOptionFromIpcRuntimeService, +} from "./runtime-options-runtime-service"; + +test("applyRuntimeOptionResultRuntimeService emits success OSD message", () => { + const osd: string[] = []; + const result = applyRuntimeOptionResultRuntimeService( + { ok: true, osdMessage: "Updated" }, + (text) => { + osd.push(text); + }, + ); + + assert.equal(result.ok, true); + assert.deepEqual(osd, ["Updated"]); +}); + +test("setRuntimeOptionFromIpcRuntimeService returns unavailable when manager missing", () => { + const osd: string[] = []; + const result = setRuntimeOptionFromIpcRuntimeService( + null, + "anki.autoUpdateNewCards", + true, + (text) => { + osd.push(text); + }, + ); + assert.equal(result.ok, false); + assert.equal(result.error, "Runtime options manager unavailable"); + assert.deepEqual(osd, []); +}); + +test("cycleRuntimeOptionFromIpcRuntimeService reports errors once", () => { + const osd: string[] = []; + const result = cycleRuntimeOptionFromIpcRuntimeService( + { + setOptionValue: () => ({ ok: true }), + cycleOption: () => ({ ok: false, error: "bad option" }), + }, + "anki.kikuFieldGrouping", + 1, + (text) => { + osd.push(text); + }, + ); + assert.equal(result.ok, false); + assert.equal(result.error, "bad option"); + assert.deepEqual(osd, ["bad option"]); +}); diff --git a/src/core/services/runtime-options-runtime-service.ts b/src/core/services/runtime-options-runtime-service.ts new file mode 100644 index 0000000..339ac27 --- /dev/null +++ b/src/core/services/runtime-options-runtime-service.ts @@ -0,0 +1,64 @@ +import { + RuntimeOptionApplyResult, + RuntimeOptionId, + RuntimeOptionValue, +} from "../../types"; + +export interface RuntimeOptionsManagerLike { + setOptionValue: ( + id: RuntimeOptionId, + value: RuntimeOptionValue, + ) => RuntimeOptionApplyResult; + cycleOption: ( + id: RuntimeOptionId, + direction: 1 | -1, + ) => RuntimeOptionApplyResult; +} + +export function applyRuntimeOptionResultRuntimeService( + result: RuntimeOptionApplyResult, + showMpvOsd: (text: string) => void, +): RuntimeOptionApplyResult { + if (result.ok && result.osdMessage) { + showMpvOsd(result.osdMessage); + } + return result; +} + +export function setRuntimeOptionFromIpcRuntimeService( + manager: RuntimeOptionsManagerLike | null, + id: RuntimeOptionId, + value: RuntimeOptionValue, + showMpvOsd: (text: string) => void, +): RuntimeOptionApplyResult { + if (!manager) { + return { ok: false, error: "Runtime options manager unavailable" }; + } + const result = applyRuntimeOptionResultRuntimeService( + manager.setOptionValue(id, value), + showMpvOsd, + ); + if (!result.ok && result.error) { + showMpvOsd(result.error); + } + return result; +} + +export function cycleRuntimeOptionFromIpcRuntimeService( + manager: RuntimeOptionsManagerLike | null, + id: RuntimeOptionId, + direction: 1 | -1, + showMpvOsd: (text: string) => void, +): RuntimeOptionApplyResult { + if (!manager) { + return { ok: false, error: "Runtime options manager unavailable" }; + } + const result = applyRuntimeOptionResultRuntimeService( + manager.cycleOption(id, direction), + showMpvOsd, + ); + if (!result.ok && result.error) { + showMpvOsd(result.error); + } + return result; +} diff --git a/src/main.ts b/src/main.ts index a893189..dbf5554 100644 --- a/src/main.ts +++ b/src/main.ts @@ -65,7 +65,6 @@ import { KikuFieldGroupingChoice, KikuMergePreviewRequest, KikuMergePreviewResponse, - RuntimeOptionApplyResult, RuntimeOptionId, RuntimeOptionState, RuntimeOptionValue, @@ -139,6 +138,11 @@ import { setMpvSubVisibilityRuntimeService, showMpvOsdRuntimeService, } from "./core/services/mpv-runtime-service"; +import { + applyRuntimeOptionResultRuntimeService, + cycleRuntimeOptionFromIpcRuntimeService, + setRuntimeOptionFromIpcRuntimeService, +} from "./core/services/runtime-options-runtime-service"; import { showDesktopNotification } from "./core/utils/notification"; import { openYomitanSettingsWindow } from "./core/services/yomitan-settings-service"; import { tokenizeSubtitleService } from "./core/services/tokenizer-service"; @@ -306,15 +310,6 @@ function broadcastRuntimeOptionsChanged(): void { broadcastToOverlayWindows("run function setOverlayDebugVisualizationEnabled(enabled: boolean): void { if (overlayDebugVisualizationEnabled === enabled) return; overlayDebugVisualizationEnabled = enabled; broadcastToOverlayWindows("overlay-debug-visualization:set", overlayDebugVisualizationEnabled); } -function applyRuntimeOptionResult( - result: RuntimeOptionApplyResult, -): RuntimeOptionApplyResult { - if (result.ok && result.osdMessage) { - showMpvOsd(result.osdMessage); - } - return result; -} - function openRuntimeOptionsPalette(): void { sendToVisibleOverlay("runtime-options:open", undefined, { restoreOnModalClose: "runtime-options" }); } function getResolvedConfig() { return configService.getConfig(); } @@ -1134,8 +1129,9 @@ function handleMpvCommandFromIpc(command: (string | number)[]): void { if (!runtimeOptionsManager) { return { ok: false, error: "Runtime options manager unavailable" }; } - return applyRuntimeOptionResult( + return applyRuntimeOptionResultRuntimeService( runtimeOptionsManager.cycleOption(id, direction), + (text) => showMpvOsd(text), ); }, showMpvOsd: (text) => showMpvOsd(text), @@ -1195,28 +1191,20 @@ registerIpcHandlersService({ getAnkiConnectStatus: () => ankiIntegration !== null, getRuntimeOptions: () => getRuntimeOptionsState(), setRuntimeOption: (id, value) => { - if (!runtimeOptionsManager) { - return { ok: false, error: "Runtime options manager unavailable" }; - } - const result = applyRuntimeOptionResult( - runtimeOptionsManager.setOptionValue(id as RuntimeOptionId, value as RuntimeOptionValue), + return setRuntimeOptionFromIpcRuntimeService( + runtimeOptionsManager, + id as RuntimeOptionId, + value as RuntimeOptionValue, + (text) => showMpvOsd(text), ); - if (!result.ok && result.error) { - showMpvOsd(result.error); - } - return result; }, cycleRuntimeOption: (id, direction) => { - if (!runtimeOptionsManager) { - return { ok: false, error: "Runtime options manager unavailable" }; - } - const result = applyRuntimeOptionResult( - runtimeOptionsManager.cycleOption(id as RuntimeOptionId, direction), + return cycleRuntimeOptionFromIpcRuntimeService( + runtimeOptionsManager, + id as RuntimeOptionId, + direction, + (text) => showMpvOsd(text), ); - if (!result.ok && result.error) { - showMpvOsd(result.error); - } - return result; }, });