mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-28 06:22:45 -08:00
refactor runtime deps wiring and docs/config updates
This commit is contained in:
@@ -1,48 +0,0 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
AnkiJimakuIpcDepsRuntimeOptions,
|
||||
createAnkiJimakuIpcDepsRuntimeService,
|
||||
} from "./anki-jimaku-ipc-deps-runtime-service";
|
||||
|
||||
test("createAnkiJimakuIpcDepsRuntimeService returns passthrough runtime options", async () => {
|
||||
const calls: string[] = [];
|
||||
const options = {
|
||||
patchAnkiConnectEnabled: () => calls.push("patch"),
|
||||
getResolvedConfig: () => ({ ankiConnect: undefined }),
|
||||
getRuntimeOptionsManager: () => null,
|
||||
getSubtitleTimingTracker: () => null,
|
||||
getMpvClient: () => null,
|
||||
getAnkiIntegration: () => null,
|
||||
setAnkiIntegration: () => calls.push("set-integration"),
|
||||
showDesktopNotification: () => calls.push("notify"),
|
||||
createFieldGroupingCallback: () => async () => ({
|
||||
keepNoteId: 0,
|
||||
deleteNoteId: 0,
|
||||
deleteDuplicate: false,
|
||||
cancelled: true,
|
||||
}),
|
||||
broadcastRuntimeOptionsChanged: () => calls.push("broadcast"),
|
||||
getFieldGroupingResolver: () => null,
|
||||
setFieldGroupingResolver: () => calls.push("set-resolver"),
|
||||
parseMediaInfo: () => ({ mediaPath: null, baseName: null, episode: null }),
|
||||
getCurrentMediaPath: () => "/tmp/a.mp4",
|
||||
jimakuFetchJson: async () => ({ ok: true, data: [] }),
|
||||
getJimakuMaxEntryResults: () => 100,
|
||||
getJimakuLanguagePreference: () => "prefer-japanese",
|
||||
resolveJimakuApiKey: async () => "abc",
|
||||
isRemoteMediaPath: () => false,
|
||||
downloadToFile: async () => ({ ok: true, path: "/tmp/a.srt" }),
|
||||
} as unknown as AnkiJimakuIpcDepsRuntimeOptions;
|
||||
|
||||
const runtime = createAnkiJimakuIpcDepsRuntimeService(options);
|
||||
|
||||
runtime.patchAnkiConnectEnabled(true);
|
||||
runtime.broadcastRuntimeOptionsChanged();
|
||||
runtime.setFieldGroupingResolver(null);
|
||||
|
||||
assert.deepEqual(calls, ["patch", "broadcast", "set-resolver"]);
|
||||
assert.equal(runtime.getCurrentMediaPath(), "/tmp/a.mp4");
|
||||
assert.equal(runtime.getJimakuMaxEntryResults(), 100);
|
||||
assert.equal(await runtime.resolveJimakuApiKey(), "abc");
|
||||
});
|
||||
@@ -1,32 +0,0 @@
|
||||
import {
|
||||
AnkiJimakuIpcRuntimeOptions,
|
||||
} from "./anki-jimaku-runtime-service";
|
||||
|
||||
export type AnkiJimakuIpcDepsRuntimeOptions = AnkiJimakuIpcRuntimeOptions;
|
||||
|
||||
export function createAnkiJimakuIpcDepsRuntimeService(
|
||||
options: AnkiJimakuIpcDepsRuntimeOptions,
|
||||
): AnkiJimakuIpcRuntimeOptions {
|
||||
return {
|
||||
patchAnkiConnectEnabled: options.patchAnkiConnectEnabled,
|
||||
getResolvedConfig: options.getResolvedConfig,
|
||||
getRuntimeOptionsManager: options.getRuntimeOptionsManager,
|
||||
getSubtitleTimingTracker: options.getSubtitleTimingTracker,
|
||||
getMpvClient: options.getMpvClient,
|
||||
getAnkiIntegration: options.getAnkiIntegration,
|
||||
setAnkiIntegration: options.setAnkiIntegration,
|
||||
showDesktopNotification: options.showDesktopNotification,
|
||||
createFieldGroupingCallback: options.createFieldGroupingCallback,
|
||||
broadcastRuntimeOptionsChanged: options.broadcastRuntimeOptionsChanged,
|
||||
getFieldGroupingResolver: options.getFieldGroupingResolver,
|
||||
setFieldGroupingResolver: options.setFieldGroupingResolver,
|
||||
parseMediaInfo: options.parseMediaInfo,
|
||||
getCurrentMediaPath: options.getCurrentMediaPath,
|
||||
jimakuFetchJson: options.jimakuFetchJson,
|
||||
getJimakuMaxEntryResults: options.getJimakuMaxEntryResults,
|
||||
getJimakuLanguagePreference: options.getJimakuLanguagePreference,
|
||||
resolveJimakuApiKey: options.resolveJimakuApiKey,
|
||||
isRemoteMediaPath: options.isRemoteMediaPath,
|
||||
downloadToFile: options.downloadToFile,
|
||||
};
|
||||
}
|
||||
@@ -73,15 +73,12 @@ export {
|
||||
getOverlayWindowsRuntimeService,
|
||||
setOverlayDebugVisualizationEnabledRuntimeService,
|
||||
} from "./overlay-broadcast-runtime-service";
|
||||
export { createMpvIpcClientDepsRuntimeService } from "./mpv-client-deps-runtime-service";
|
||||
export { createAppLifecycleDepsRuntimeService } from "./app-lifecycle-deps-runtime-service";
|
||||
export { createCliCommandDepsRuntimeService } from "./cli-command-deps-runtime-service";
|
||||
export { createIpcDepsRuntimeService } from "./ipc-deps-runtime-service";
|
||||
export { createAnkiJimakuIpcDepsRuntimeService } from "./anki-jimaku-ipc-deps-runtime-service";
|
||||
export { createFieldGroupingOverlayRuntimeService } from "./field-grouping-overlay-runtime-service";
|
||||
export { createSubsyncRuntimeDepsService } from "./subsync-deps-runtime-service";
|
||||
export { createNumericShortcutRuntimeService } from "./numeric-shortcut-runtime-service";
|
||||
export { createOverlayVisibilityFacadeDepsRuntimeService } from "./overlay-visibility-facade-deps-runtime-service";
|
||||
export { createMpvCommandIpcDepsRuntimeService } from "./mpv-command-ipc-deps-runtime-service";
|
||||
export { createRuntimeOptionsIpcDepsRuntimeService } from "./runtime-options-ipc-deps-runtime-service";
|
||||
export { createTokenizerDepsRuntimeService } from "./tokenizer-deps-runtime-service";
|
||||
@@ -90,26 +87,8 @@ export {
|
||||
createInvisibleOverlayVisibilityDepsRuntimeService,
|
||||
createOverlayWindowRuntimeDepsService,
|
||||
createVisibleOverlayVisibilityDepsRuntimeService,
|
||||
} from "./overlay-runtime-deps-service";
|
||||
export {
|
||||
createOverlayShortcutLifecycleDepsRuntimeService,
|
||||
createOverlayShortcutRuntimeDepsService,
|
||||
} from "./overlay-shortcut-runtime-deps-service";
|
||||
export {
|
||||
createCopyCurrentSubtitleDepsRuntimeService,
|
||||
createHandleMineSentenceDigitDepsRuntimeService,
|
||||
createHandleMultiCopyDigitDepsRuntimeService,
|
||||
createMarkLastCardAsAudioCardDepsRuntimeService,
|
||||
createMineSentenceCardDepsRuntimeService,
|
||||
createTriggerFieldGroupingDepsRuntimeService,
|
||||
createUpdateLastCardFromClipboardDepsRuntimeService,
|
||||
} from "./mining-runtime-deps-service";
|
||||
export {
|
||||
createGlobalShortcutRegistrationDepsRuntimeService,
|
||||
createSecondarySubtitleCycleDepsRuntimeService,
|
||||
createYomitanSettingsWindowDepsRuntimeService,
|
||||
runOverlayShortcutLocalFallbackRuntimeService,
|
||||
} from "./shortcut-ui-runtime-deps-service";
|
||||
} from "./overlay-deps-runtime-service";
|
||||
export { runOverlayShortcutLocalFallbackRuntimeService } from "./shortcut-ui-deps-runtime-service";
|
||||
export { createStartupLifecycleHooksRuntimeService } from "./startup-lifecycle-hooks-runtime-service";
|
||||
export { createRuntimeOptionsManagerRuntimeService } from "./runtime-options-manager-runtime-service";
|
||||
export { createAppLoggingRuntimeService } from "./app-logging-runtime-service";
|
||||
@@ -122,3 +101,4 @@ export { runStartupBootstrapRuntimeService } from "./startup-bootstrap-runtime-s
|
||||
export { runSubsyncManualFromIpcRuntimeService, triggerSubsyncFromConfigRuntimeService } from "./subsync-runtime-service";
|
||||
export { updateInvisibleOverlayVisibilityService, updateVisibleOverlayVisibilityService } from "./overlay-visibility-service";
|
||||
export { registerAnkiJimakuIpcRuntimeService } from "./anki-jimaku-runtime-service";
|
||||
export { createOverlayManagerService } from "./overlay-manager-service";
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
createCopyCurrentSubtitleDepsRuntimeService,
|
||||
createHandleMineSentenceDigitDepsRuntimeService,
|
||||
createHandleMultiCopyDigitDepsRuntimeService,
|
||||
createMarkLastCardAsAudioCardDepsRuntimeService,
|
||||
createMineSentenceCardDepsRuntimeService,
|
||||
createTriggerFieldGroupingDepsRuntimeService,
|
||||
createUpdateLastCardFromClipboardDepsRuntimeService,
|
||||
} from "./mining-runtime-deps-service";
|
||||
|
||||
test("mining runtime deps builders preserve references", () => {
|
||||
const showMpvOsd = (_text: string) => {};
|
||||
const writeClipboardText = (_text: string) => {};
|
||||
const readClipboardText = () => "x";
|
||||
const logError = (_message: string, _err: unknown) => {};
|
||||
const subtitleTimingTracker = null;
|
||||
const ankiIntegration = null;
|
||||
const mpvClient = null;
|
||||
|
||||
const multiCopy = createHandleMultiCopyDigitDepsRuntimeService({
|
||||
subtitleTimingTracker,
|
||||
writeClipboardText,
|
||||
showMpvOsd,
|
||||
});
|
||||
const copyCurrent = createCopyCurrentSubtitleDepsRuntimeService({
|
||||
subtitleTimingTracker,
|
||||
writeClipboardText,
|
||||
showMpvOsd,
|
||||
});
|
||||
const updateLast = createUpdateLastCardFromClipboardDepsRuntimeService({
|
||||
ankiIntegration,
|
||||
readClipboardText,
|
||||
showMpvOsd,
|
||||
});
|
||||
const fieldGrouping = createTriggerFieldGroupingDepsRuntimeService({
|
||||
ankiIntegration,
|
||||
showMpvOsd,
|
||||
});
|
||||
const markAudio = createMarkLastCardAsAudioCardDepsRuntimeService({
|
||||
ankiIntegration,
|
||||
showMpvOsd,
|
||||
});
|
||||
const mineCard = createMineSentenceCardDepsRuntimeService({
|
||||
ankiIntegration,
|
||||
mpvClient,
|
||||
showMpvOsd,
|
||||
});
|
||||
const mineDigit = createHandleMineSentenceDigitDepsRuntimeService({
|
||||
subtitleTimingTracker,
|
||||
ankiIntegration,
|
||||
getCurrentSecondarySubText: () => undefined,
|
||||
showMpvOsd,
|
||||
logError,
|
||||
});
|
||||
|
||||
assert.equal(multiCopy.writeClipboardText, writeClipboardText);
|
||||
assert.equal(copyCurrent.showMpvOsd, showMpvOsd);
|
||||
assert.equal(updateLast.readClipboardText, readClipboardText);
|
||||
assert.equal(fieldGrouping.ankiIntegration, ankiIntegration);
|
||||
assert.equal(markAudio.showMpvOsd, showMpvOsd);
|
||||
assert.equal(mineCard.mpvClient, mpvClient);
|
||||
assert.equal(mineDigit.logError, logError);
|
||||
});
|
||||
@@ -1,107 +0,0 @@
|
||||
import {
|
||||
copyCurrentSubtitleService,
|
||||
handleMineSentenceDigitService,
|
||||
handleMultiCopyDigitService,
|
||||
markLastCardAsAudioCardService,
|
||||
mineSentenceCardService,
|
||||
triggerFieldGroupingService,
|
||||
updateLastCardFromClipboardService,
|
||||
} from "./mining-runtime-service";
|
||||
|
||||
export function createHandleMultiCopyDigitDepsRuntimeService(
|
||||
options: {
|
||||
subtitleTimingTracker: Parameters<typeof handleMultiCopyDigitService>[1]["subtitleTimingTracker"];
|
||||
writeClipboardText: Parameters<typeof handleMultiCopyDigitService>[1]["writeClipboardText"];
|
||||
showMpvOsd: Parameters<typeof handleMultiCopyDigitService>[1]["showMpvOsd"];
|
||||
},
|
||||
): Parameters<typeof handleMultiCopyDigitService>[1] {
|
||||
return {
|
||||
subtitleTimingTracker: options.subtitleTimingTracker,
|
||||
writeClipboardText: options.writeClipboardText,
|
||||
showMpvOsd: options.showMpvOsd,
|
||||
};
|
||||
}
|
||||
|
||||
export function createCopyCurrentSubtitleDepsRuntimeService(
|
||||
options: {
|
||||
subtitleTimingTracker: Parameters<typeof copyCurrentSubtitleService>[0]["subtitleTimingTracker"];
|
||||
writeClipboardText: Parameters<typeof copyCurrentSubtitleService>[0]["writeClipboardText"];
|
||||
showMpvOsd: Parameters<typeof copyCurrentSubtitleService>[0]["showMpvOsd"];
|
||||
},
|
||||
): Parameters<typeof copyCurrentSubtitleService>[0] {
|
||||
return {
|
||||
subtitleTimingTracker: options.subtitleTimingTracker,
|
||||
writeClipboardText: options.writeClipboardText,
|
||||
showMpvOsd: options.showMpvOsd,
|
||||
};
|
||||
}
|
||||
|
||||
export function createUpdateLastCardFromClipboardDepsRuntimeService(
|
||||
options: {
|
||||
ankiIntegration: Parameters<typeof updateLastCardFromClipboardService>[0]["ankiIntegration"];
|
||||
readClipboardText: Parameters<typeof updateLastCardFromClipboardService>[0]["readClipboardText"];
|
||||
showMpvOsd: Parameters<typeof updateLastCardFromClipboardService>[0]["showMpvOsd"];
|
||||
},
|
||||
): Parameters<typeof updateLastCardFromClipboardService>[0] {
|
||||
return {
|
||||
ankiIntegration: options.ankiIntegration,
|
||||
readClipboardText: options.readClipboardText,
|
||||
showMpvOsd: options.showMpvOsd,
|
||||
};
|
||||
}
|
||||
|
||||
export function createTriggerFieldGroupingDepsRuntimeService(
|
||||
options: {
|
||||
ankiIntegration: Parameters<typeof triggerFieldGroupingService>[0]["ankiIntegration"];
|
||||
showMpvOsd: Parameters<typeof triggerFieldGroupingService>[0]["showMpvOsd"];
|
||||
},
|
||||
): Parameters<typeof triggerFieldGroupingService>[0] {
|
||||
return {
|
||||
ankiIntegration: options.ankiIntegration,
|
||||
showMpvOsd: options.showMpvOsd,
|
||||
};
|
||||
}
|
||||
|
||||
export function createMarkLastCardAsAudioCardDepsRuntimeService(
|
||||
options: {
|
||||
ankiIntegration: Parameters<typeof markLastCardAsAudioCardService>[0]["ankiIntegration"];
|
||||
showMpvOsd: Parameters<typeof markLastCardAsAudioCardService>[0]["showMpvOsd"];
|
||||
},
|
||||
): Parameters<typeof markLastCardAsAudioCardService>[0] {
|
||||
return {
|
||||
ankiIntegration: options.ankiIntegration,
|
||||
showMpvOsd: options.showMpvOsd,
|
||||
};
|
||||
}
|
||||
|
||||
export function createMineSentenceCardDepsRuntimeService(
|
||||
options: {
|
||||
ankiIntegration: Parameters<typeof mineSentenceCardService>[0]["ankiIntegration"];
|
||||
mpvClient: Parameters<typeof mineSentenceCardService>[0]["mpvClient"];
|
||||
showMpvOsd: Parameters<typeof mineSentenceCardService>[0]["showMpvOsd"];
|
||||
},
|
||||
): Parameters<typeof mineSentenceCardService>[0] {
|
||||
return {
|
||||
ankiIntegration: options.ankiIntegration,
|
||||
mpvClient: options.mpvClient,
|
||||
showMpvOsd: options.showMpvOsd,
|
||||
};
|
||||
}
|
||||
|
||||
export function createHandleMineSentenceDigitDepsRuntimeService(
|
||||
options: {
|
||||
subtitleTimingTracker: Parameters<typeof handleMineSentenceDigitService>[1]["subtitleTimingTracker"];
|
||||
ankiIntegration: Parameters<typeof handleMineSentenceDigitService>[1]["ankiIntegration"];
|
||||
getCurrentSecondarySubText: Parameters<typeof handleMineSentenceDigitService>[1]["getCurrentSecondarySubText"];
|
||||
showMpvOsd: Parameters<typeof handleMineSentenceDigitService>[1]["showMpvOsd"];
|
||||
logError: Parameters<typeof handleMineSentenceDigitService>[1]["logError"];
|
||||
},
|
||||
): Parameters<typeof handleMineSentenceDigitService>[1] {
|
||||
return {
|
||||
subtitleTimingTracker: options.subtitleTimingTracker,
|
||||
ankiIntegration: options.ankiIntegration,
|
||||
getCurrentSecondarySubText: options.getCurrentSecondarySubText,
|
||||
showMpvOsd: options.showMpvOsd,
|
||||
logError: options.logError,
|
||||
};
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { createMpvIpcClientDepsRuntimeService } from "./mpv-client-deps-runtime-service";
|
||||
|
||||
test("createMpvIpcClientDepsRuntimeService returns passthrough dep object", async () => {
|
||||
const marker = {
|
||||
getResolvedConfig: () => ({ auto_start_overlay: false } as never),
|
||||
autoStartOverlay: true,
|
||||
setOverlayVisible: () => {},
|
||||
shouldBindVisibleOverlayToMpvSubVisibility: () => true,
|
||||
isVisibleOverlayVisible: () => false,
|
||||
getReconnectTimer: () => null,
|
||||
setReconnectTimer: () => {},
|
||||
getCurrentSubText: () => "x",
|
||||
setCurrentSubText: () => {},
|
||||
setCurrentSubAssText: () => {},
|
||||
getSubtitleTimingTracker: () => null,
|
||||
subtitleWsBroadcast: () => {},
|
||||
getOverlayWindowsCount: () => 0,
|
||||
tokenizeSubtitle: async () => ({ text: "x", tokens: [], mergedTokens: [] }),
|
||||
broadcastToOverlayWindows: () => {},
|
||||
updateCurrentMediaPath: () => {},
|
||||
updateMpvSubtitleRenderMetrics: () => {},
|
||||
getMpvSubtitleRenderMetrics: () => ({
|
||||
subPos: 100,
|
||||
subFontSize: 40,
|
||||
subScale: 1,
|
||||
subMarginY: 0,
|
||||
subMarginX: 0,
|
||||
subFont: "sans",
|
||||
subSpacing: 0,
|
||||
subBold: false,
|
||||
subItalic: false,
|
||||
subBorderSize: 0,
|
||||
subShadowOffset: 0,
|
||||
subAssOverride: "yes",
|
||||
subScaleByWindow: true,
|
||||
subUseMargins: true,
|
||||
osdHeight: 720,
|
||||
osdDimensions: null,
|
||||
}),
|
||||
setPreviousSecondarySubVisibility: () => {},
|
||||
showMpvOsd: () => {},
|
||||
};
|
||||
|
||||
const deps = createMpvIpcClientDepsRuntimeService(marker);
|
||||
assert.equal(deps.autoStartOverlay, true);
|
||||
assert.equal(deps.getCurrentSubText(), "x");
|
||||
assert.equal(deps.getOverlayWindowsCount(), 0);
|
||||
assert.equal(deps.shouldBindVisibleOverlayToMpvSubVisibility(), true);
|
||||
});
|
||||
@@ -1,61 +0,0 @@
|
||||
import {
|
||||
MpvIpcClientDeps,
|
||||
} from "./mpv-service";
|
||||
import { Config, MpvSubtitleRenderMetrics, SubtitleData } from "../../types";
|
||||
|
||||
interface SubtitleTimingTrackerLike {
|
||||
recordSubtitle: (text: string, start: number, end: number) => void;
|
||||
}
|
||||
|
||||
export interface MpvClientDepsRuntimeOptions {
|
||||
getResolvedConfig: () => Config;
|
||||
autoStartOverlay: boolean;
|
||||
setOverlayVisible: (visible: boolean) => void;
|
||||
shouldBindVisibleOverlayToMpvSubVisibility: () => boolean;
|
||||
isVisibleOverlayVisible: () => boolean;
|
||||
getReconnectTimer: () => ReturnType<typeof setTimeout> | null;
|
||||
setReconnectTimer: (timer: ReturnType<typeof setTimeout> | null) => void;
|
||||
getCurrentSubText: () => string;
|
||||
setCurrentSubText: (text: string) => void;
|
||||
setCurrentSubAssText: (text: string) => void;
|
||||
getSubtitleTimingTracker: () => SubtitleTimingTrackerLike | null;
|
||||
subtitleWsBroadcast: (text: string) => void;
|
||||
getOverlayWindowsCount: () => number;
|
||||
tokenizeSubtitle: (text: string) => Promise<SubtitleData>;
|
||||
broadcastToOverlayWindows: (channel: string, ...args: unknown[]) => void;
|
||||
updateCurrentMediaPath: (mediaPath: unknown) => void;
|
||||
updateMpvSubtitleRenderMetrics: (
|
||||
patch: Partial<MpvSubtitleRenderMetrics>,
|
||||
) => void;
|
||||
getMpvSubtitleRenderMetrics: () => MpvSubtitleRenderMetrics;
|
||||
setPreviousSecondarySubVisibility: (value: boolean | null) => void;
|
||||
showMpvOsd: (text: string) => void;
|
||||
}
|
||||
|
||||
export function createMpvIpcClientDepsRuntimeService(
|
||||
options: MpvClientDepsRuntimeOptions,
|
||||
): MpvIpcClientDeps {
|
||||
return {
|
||||
getResolvedConfig: options.getResolvedConfig,
|
||||
autoStartOverlay: options.autoStartOverlay,
|
||||
setOverlayVisible: options.setOverlayVisible,
|
||||
shouldBindVisibleOverlayToMpvSubVisibility:
|
||||
options.shouldBindVisibleOverlayToMpvSubVisibility,
|
||||
isVisibleOverlayVisible: options.isVisibleOverlayVisible,
|
||||
getReconnectTimer: options.getReconnectTimer,
|
||||
setReconnectTimer: options.setReconnectTimer,
|
||||
getCurrentSubText: options.getCurrentSubText,
|
||||
setCurrentSubText: options.setCurrentSubText,
|
||||
setCurrentSubAssText: options.setCurrentSubAssText,
|
||||
getSubtitleTimingTracker: options.getSubtitleTimingTracker,
|
||||
subtitleWsBroadcast: options.subtitleWsBroadcast,
|
||||
getOverlayWindowsCount: options.getOverlayWindowsCount,
|
||||
tokenizeSubtitle: options.tokenizeSubtitle,
|
||||
broadcastToOverlayWindows: options.broadcastToOverlayWindows,
|
||||
updateCurrentMediaPath: options.updateCurrentMediaPath,
|
||||
updateMpvSubtitleRenderMetrics: options.updateMpvSubtitleRenderMetrics,
|
||||
getMpvSubtitleRenderMetrics: options.getMpvSubtitleRenderMetrics,
|
||||
setPreviousSecondarySubVisibility: options.setPreviousSecondarySubVisibility,
|
||||
showMpvOsd: options.showMpvOsd,
|
||||
};
|
||||
}
|
||||
@@ -5,7 +5,7 @@ import {
|
||||
createInvisibleOverlayVisibilityDepsRuntimeService,
|
||||
createOverlayWindowRuntimeDepsService,
|
||||
createVisibleOverlayVisibilityDepsRuntimeService,
|
||||
} from "./overlay-runtime-deps-service";
|
||||
} from "./overlay-deps-runtime-service";
|
||||
|
||||
test("createOverlayWindowRuntimeDepsService maps runtime state providers", () => {
|
||||
let visible = true;
|
||||
42
src/core/services/overlay-manager-service.test.ts
Normal file
42
src/core/services/overlay-manager-service.test.ts
Normal file
@@ -0,0 +1,42 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { createOverlayManagerService } from "./overlay-manager-service";
|
||||
|
||||
test("overlay manager initializes with empty windows and hidden overlays", () => {
|
||||
const manager = createOverlayManagerService();
|
||||
assert.equal(manager.getMainWindow(), null);
|
||||
assert.equal(manager.getInvisibleWindow(), null);
|
||||
assert.equal(manager.getVisibleOverlayVisible(), false);
|
||||
assert.equal(manager.getInvisibleOverlayVisible(), false);
|
||||
assert.deepEqual(manager.getOverlayWindows(), []);
|
||||
});
|
||||
|
||||
test("overlay manager stores window references and returns stable window order", () => {
|
||||
const manager = createOverlayManagerService();
|
||||
const visibleWindow = { isDestroyed: () => false } as unknown as Electron.BrowserWindow;
|
||||
const invisibleWindow = { isDestroyed: () => false } as unknown as Electron.BrowserWindow;
|
||||
|
||||
manager.setMainWindow(visibleWindow);
|
||||
manager.setInvisibleWindow(invisibleWindow);
|
||||
|
||||
assert.equal(manager.getMainWindow(), visibleWindow);
|
||||
assert.equal(manager.getInvisibleWindow(), invisibleWindow);
|
||||
assert.deepEqual(manager.getOverlayWindows(), [visibleWindow, invisibleWindow]);
|
||||
});
|
||||
|
||||
test("overlay manager excludes destroyed windows", () => {
|
||||
const manager = createOverlayManagerService();
|
||||
manager.setMainWindow({ isDestroyed: () => true } as unknown as Electron.BrowserWindow);
|
||||
manager.setInvisibleWindow({ isDestroyed: () => false } as unknown as Electron.BrowserWindow);
|
||||
|
||||
assert.equal(manager.getOverlayWindows().length, 1);
|
||||
});
|
||||
|
||||
test("overlay manager stores visibility state", () => {
|
||||
const manager = createOverlayManagerService();
|
||||
|
||||
manager.setVisibleOverlayVisible(true);
|
||||
manager.setInvisibleOverlayVisible(true);
|
||||
assert.equal(manager.getVisibleOverlayVisible(), true);
|
||||
assert.equal(manager.getInvisibleOverlayVisible(), true);
|
||||
});
|
||||
49
src/core/services/overlay-manager-service.ts
Normal file
49
src/core/services/overlay-manager-service.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
import { BrowserWindow } from "electron";
|
||||
|
||||
export interface OverlayManagerService {
|
||||
getMainWindow: () => BrowserWindow | null;
|
||||
setMainWindow: (window: BrowserWindow | null) => void;
|
||||
getInvisibleWindow: () => BrowserWindow | null;
|
||||
setInvisibleWindow: (window: BrowserWindow | null) => void;
|
||||
getVisibleOverlayVisible: () => boolean;
|
||||
setVisibleOverlayVisible: (visible: boolean) => void;
|
||||
getInvisibleOverlayVisible: () => boolean;
|
||||
setInvisibleOverlayVisible: (visible: boolean) => void;
|
||||
getOverlayWindows: () => BrowserWindow[];
|
||||
}
|
||||
|
||||
export function createOverlayManagerService(): OverlayManagerService {
|
||||
let mainWindow: BrowserWindow | null = null;
|
||||
let invisibleWindow: BrowserWindow | null = null;
|
||||
let visibleOverlayVisible = false;
|
||||
let invisibleOverlayVisible = false;
|
||||
|
||||
return {
|
||||
getMainWindow: () => mainWindow,
|
||||
setMainWindow: (window) => {
|
||||
mainWindow = window;
|
||||
},
|
||||
getInvisibleWindow: () => invisibleWindow,
|
||||
setInvisibleWindow: (window) => {
|
||||
invisibleWindow = window;
|
||||
},
|
||||
getVisibleOverlayVisible: () => visibleOverlayVisible,
|
||||
setVisibleOverlayVisible: (visible) => {
|
||||
visibleOverlayVisible = visible;
|
||||
},
|
||||
getInvisibleOverlayVisible: () => invisibleOverlayVisible,
|
||||
setInvisibleOverlayVisible: (visible) => {
|
||||
invisibleOverlayVisible = visible;
|
||||
},
|
||||
getOverlayWindows: () => {
|
||||
const windows: BrowserWindow[] = [];
|
||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||
windows.push(mainWindow);
|
||||
}
|
||||
if (invisibleWindow && !invisibleWindow.isDestroyed()) {
|
||||
windows.push(invisibleWindow);
|
||||
}
|
||||
return windows;
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
createOverlayShortcutLifecycleDepsRuntimeService,
|
||||
createOverlayShortcutRuntimeDepsService,
|
||||
} from "./overlay-shortcut-runtime-deps-service";
|
||||
|
||||
test("createOverlayShortcutRuntimeDepsService returns callable runtime deps", async () => {
|
||||
const calls: string[] = [];
|
||||
const deps = createOverlayShortcutRuntimeDepsService({
|
||||
showMpvOsd: () => calls.push("showMpvOsd"),
|
||||
openRuntimeOptions: () => calls.push("openRuntimeOptions"),
|
||||
openJimaku: () => calls.push("openJimaku"),
|
||||
markAudioCard: async () => {
|
||||
calls.push("markAudioCard");
|
||||
},
|
||||
copySubtitleMultiple: () => calls.push("copySubtitleMultiple"),
|
||||
copySubtitle: () => calls.push("copySubtitle"),
|
||||
toggleSecondarySub: () => calls.push("toggleSecondarySub"),
|
||||
updateLastCardFromClipboard: async () => {
|
||||
calls.push("updateLastCardFromClipboard");
|
||||
},
|
||||
triggerFieldGrouping: async () => {
|
||||
calls.push("triggerFieldGrouping");
|
||||
},
|
||||
triggerSubsync: async () => {
|
||||
calls.push("triggerSubsync");
|
||||
},
|
||||
mineSentence: async () => {
|
||||
calls.push("mineSentence");
|
||||
},
|
||||
mineSentenceMultiple: () => calls.push("mineSentenceMultiple"),
|
||||
});
|
||||
|
||||
deps.copySubtitle();
|
||||
await deps.mineSentence();
|
||||
deps.mineSentenceMultiple(2);
|
||||
|
||||
assert.deepEqual(calls, ["copySubtitle", "mineSentence", "mineSentenceMultiple"]);
|
||||
});
|
||||
|
||||
test("createOverlayShortcutLifecycleDepsRuntimeService returns lifecycle passthrough", () => {
|
||||
const deps = createOverlayShortcutLifecycleDepsRuntimeService({
|
||||
getConfiguredShortcuts: () => ({ actions: [] } as never),
|
||||
getOverlayHandlers: () => ({} as never),
|
||||
cancelPendingMultiCopy: () => {},
|
||||
cancelPendingMineSentenceMultiple: () => {},
|
||||
});
|
||||
|
||||
assert.ok(deps.getConfiguredShortcuts());
|
||||
assert.ok(deps.getOverlayHandlers());
|
||||
});
|
||||
@@ -1,59 +0,0 @@
|
||||
import {
|
||||
OverlayShortcutLifecycleDeps,
|
||||
} from "./overlay-shortcut-lifecycle-service";
|
||||
import {
|
||||
OverlayShortcutRuntimeDeps,
|
||||
} from "./overlay-shortcut-runtime-service";
|
||||
|
||||
export interface OverlayShortcutRuntimeDepsOptions {
|
||||
showMpvOsd: (text: string) => void;
|
||||
openRuntimeOptions: () => void;
|
||||
openJimaku: () => void;
|
||||
markAudioCard: () => Promise<void>;
|
||||
copySubtitleMultiple: (timeoutMs: number) => void;
|
||||
copySubtitle: () => void;
|
||||
toggleSecondarySub: () => void;
|
||||
updateLastCardFromClipboard: () => Promise<void>;
|
||||
triggerFieldGrouping: () => Promise<void>;
|
||||
triggerSubsync: () => Promise<void>;
|
||||
mineSentence: () => Promise<void>;
|
||||
mineSentenceMultiple: (timeoutMs: number) => void;
|
||||
}
|
||||
|
||||
export interface OverlayShortcutLifecycleDepsOptions {
|
||||
getConfiguredShortcuts: OverlayShortcutLifecycleDeps["getConfiguredShortcuts"];
|
||||
getOverlayHandlers: OverlayShortcutLifecycleDeps["getOverlayHandlers"];
|
||||
cancelPendingMultiCopy: () => void;
|
||||
cancelPendingMineSentenceMultiple: () => void;
|
||||
}
|
||||
|
||||
export function createOverlayShortcutRuntimeDepsService(
|
||||
options: OverlayShortcutRuntimeDepsOptions,
|
||||
): OverlayShortcutRuntimeDeps {
|
||||
return {
|
||||
showMpvOsd: options.showMpvOsd,
|
||||
openRuntimeOptions: options.openRuntimeOptions,
|
||||
openJimaku: options.openJimaku,
|
||||
markAudioCard: options.markAudioCard,
|
||||
copySubtitleMultiple: options.copySubtitleMultiple,
|
||||
copySubtitle: options.copySubtitle,
|
||||
toggleSecondarySub: options.toggleSecondarySub,
|
||||
updateLastCardFromClipboard: options.updateLastCardFromClipboard,
|
||||
triggerFieldGrouping: options.triggerFieldGrouping,
|
||||
triggerSubsync: options.triggerSubsync,
|
||||
mineSentence: options.mineSentence,
|
||||
mineSentenceMultiple: options.mineSentenceMultiple,
|
||||
};
|
||||
}
|
||||
|
||||
export function createOverlayShortcutLifecycleDepsRuntimeService(
|
||||
options: OverlayShortcutLifecycleDepsOptions,
|
||||
): OverlayShortcutLifecycleDeps {
|
||||
return {
|
||||
getConfiguredShortcuts: options.getConfiguredShortcuts,
|
||||
getOverlayHandlers: options.getOverlayHandlers,
|
||||
cancelPendingMultiCopy: options.cancelPendingMultiCopy,
|
||||
cancelPendingMineSentenceMultiple:
|
||||
options.cancelPendingMineSentenceMultiple,
|
||||
};
|
||||
}
|
||||
@@ -1,46 +0,0 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import { createOverlayVisibilityFacadeDepsRuntimeService } from "./overlay-visibility-facade-deps-runtime-service";
|
||||
|
||||
test("createOverlayVisibilityFacadeDepsRuntimeService returns working deps object", () => {
|
||||
let visible = false;
|
||||
let invisible = true;
|
||||
let mpvSubVisible: boolean | null = null;
|
||||
let syncCalls = 0;
|
||||
|
||||
const deps = createOverlayVisibilityFacadeDepsRuntimeService({
|
||||
getVisibleOverlayVisible: () => visible,
|
||||
getInvisibleOverlayVisible: () => invisible,
|
||||
setVisibleOverlayVisibleState: (nextVisible) => {
|
||||
visible = nextVisible;
|
||||
},
|
||||
setInvisibleOverlayVisibleState: (nextVisible) => {
|
||||
invisible = nextVisible;
|
||||
},
|
||||
updateVisibleOverlayVisibility: () => {},
|
||||
updateInvisibleOverlayVisibility: () => {},
|
||||
syncInvisibleOverlayMousePassthrough: () => {
|
||||
syncCalls += 1;
|
||||
},
|
||||
shouldBindVisibleOverlayToMpvSubVisibility: () => true,
|
||||
isMpvConnected: () => true,
|
||||
setMpvSubVisibility: (nextVisible) => {
|
||||
mpvSubVisible = nextVisible;
|
||||
},
|
||||
});
|
||||
|
||||
assert.equal(deps.getVisibleOverlayVisible(), false);
|
||||
assert.equal(deps.getInvisibleOverlayVisible(), true);
|
||||
|
||||
deps.setVisibleOverlayVisibleState(true);
|
||||
deps.setInvisibleOverlayVisibleState(false);
|
||||
deps.syncInvisibleOverlayMousePassthrough();
|
||||
deps.setMpvSubVisibility(false);
|
||||
|
||||
assert.equal(visible, true);
|
||||
assert.equal(invisible, false);
|
||||
assert.equal(syncCalls, 1);
|
||||
assert.equal(mpvSubVisible, false);
|
||||
assert.equal(deps.shouldBindVisibleOverlayToMpvSubVisibility(), true);
|
||||
assert.equal(deps.isMpvConnected(), true);
|
||||
});
|
||||
@@ -1,35 +0,0 @@
|
||||
import {
|
||||
OverlayVisibilityFacadeDeps,
|
||||
} from "./overlay-visibility-facade-service";
|
||||
|
||||
export interface OverlayVisibilityFacadeDepsRuntimeOptions {
|
||||
getVisibleOverlayVisible: () => boolean;
|
||||
getInvisibleOverlayVisible: () => boolean;
|
||||
setVisibleOverlayVisibleState: (nextVisible: boolean) => void;
|
||||
setInvisibleOverlayVisibleState: (nextVisible: boolean) => void;
|
||||
updateVisibleOverlayVisibility: () => void;
|
||||
updateInvisibleOverlayVisibility: () => void;
|
||||
syncInvisibleOverlayMousePassthrough: () => void;
|
||||
shouldBindVisibleOverlayToMpvSubVisibility: () => boolean;
|
||||
isMpvConnected: () => boolean;
|
||||
setMpvSubVisibility: (mpvSubVisible: boolean) => void;
|
||||
}
|
||||
|
||||
export function createOverlayVisibilityFacadeDepsRuntimeService(
|
||||
options: OverlayVisibilityFacadeDepsRuntimeOptions,
|
||||
): OverlayVisibilityFacadeDeps {
|
||||
return {
|
||||
getVisibleOverlayVisible: options.getVisibleOverlayVisible,
|
||||
getInvisibleOverlayVisible: options.getInvisibleOverlayVisible,
|
||||
setVisibleOverlayVisibleState: options.setVisibleOverlayVisibleState,
|
||||
setInvisibleOverlayVisibleState: options.setInvisibleOverlayVisibleState,
|
||||
updateVisibleOverlayVisibility: options.updateVisibleOverlayVisibility,
|
||||
updateInvisibleOverlayVisibility: options.updateInvisibleOverlayVisibility,
|
||||
syncInvisibleOverlayMousePassthrough:
|
||||
options.syncInvisibleOverlayMousePassthrough,
|
||||
shouldBindVisibleOverlayToMpvSubVisibility:
|
||||
options.shouldBindVisibleOverlayToMpvSubVisibility,
|
||||
isMpvConnected: options.isMpvConnected,
|
||||
setMpvSubVisibility: options.setMpvSubVisibility,
|
||||
};
|
||||
}
|
||||
@@ -1,35 +1,11 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
createGlobalShortcutRegistrationDepsRuntimeService,
|
||||
createSecondarySubtitleCycleDepsRuntimeService,
|
||||
createYomitanSettingsWindowDepsRuntimeService,
|
||||
runOverlayShortcutLocalFallbackRuntimeService,
|
||||
} from "./shortcut-ui-runtime-deps-service";
|
||||
} from "./shortcut-ui-deps-runtime-service";
|
||||
|
||||
function makeOptions() {
|
||||
return {
|
||||
yomitanExt: null,
|
||||
getYomitanSettingsWindow: () => null,
|
||||
setYomitanSettingsWindow: () => {},
|
||||
|
||||
shortcuts: {
|
||||
toggleVisibleOverlayGlobal: "Ctrl+Shift+O",
|
||||
toggleInvisibleOverlayGlobal: "Ctrl+Alt+O",
|
||||
},
|
||||
onToggleVisibleOverlay: () => {},
|
||||
onToggleInvisibleOverlay: () => {},
|
||||
onOpenYomitanSettings: () => {},
|
||||
isDev: false,
|
||||
getMainWindow: () => null,
|
||||
|
||||
getSecondarySubMode: () => "hover" as const,
|
||||
setSecondarySubMode: () => {},
|
||||
getLastSecondarySubToggleAtMs: () => 0,
|
||||
setLastSecondarySubToggleAtMs: () => {},
|
||||
broadcastSecondarySubMode: () => {},
|
||||
showMpvOsd: () => {},
|
||||
|
||||
getConfiguredShortcuts: () => ({
|
||||
toggleVisibleOverlayGlobal: null,
|
||||
toggleInvisibleOverlayGlobal: null,
|
||||
@@ -63,17 +39,6 @@ function makeOptions() {
|
||||
};
|
||||
}
|
||||
|
||||
test("shortcut ui deps builders return expected adapters", () => {
|
||||
const options = makeOptions();
|
||||
const yomitan = createYomitanSettingsWindowDepsRuntimeService(options);
|
||||
const globalShortcuts = createGlobalShortcutRegistrationDepsRuntimeService(options);
|
||||
const secondary = createSecondarySubtitleCycleDepsRuntimeService(options);
|
||||
|
||||
assert.equal(yomitan.yomitanExt, null);
|
||||
assert.equal(typeof globalShortcuts.onOpenYomitanSettings, "function");
|
||||
assert.equal(secondary.getSecondarySubMode(), "hover");
|
||||
});
|
||||
|
||||
test("runOverlayShortcutLocalFallbackRuntimeService delegates and returns boolean", () => {
|
||||
const options = {
|
||||
...makeOptions(),
|
||||
24
src/core/services/shortcut-ui-deps-runtime-service.ts
Normal file
24
src/core/services/shortcut-ui-deps-runtime-service.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import { ConfiguredShortcuts } from "../utils/shortcut-config";
|
||||
import { OverlayShortcutFallbackHandlers, runOverlayShortcutLocalFallback } from "./overlay-shortcut-fallback-runner";
|
||||
|
||||
export interface ShortcutUiRuntimeDepsOptions {
|
||||
getConfiguredShortcuts: () => ConfiguredShortcuts;
|
||||
getOverlayShortcutFallbackHandlers: () => OverlayShortcutFallbackHandlers;
|
||||
shortcutMatcher: (
|
||||
input: Electron.Input,
|
||||
accelerator: string,
|
||||
allowWhenRegistered?: boolean,
|
||||
) => boolean;
|
||||
}
|
||||
|
||||
export function runOverlayShortcutLocalFallbackRuntimeService(
|
||||
input: Electron.Input,
|
||||
options: ShortcutUiRuntimeDepsOptions,
|
||||
): boolean {
|
||||
return runOverlayShortcutLocalFallback(
|
||||
input,
|
||||
options.getConfiguredShortcuts(),
|
||||
options.shortcutMatcher,
|
||||
options.getOverlayShortcutFallbackHandlers(),
|
||||
);
|
||||
}
|
||||
@@ -1,83 +0,0 @@
|
||||
import { Extension } from "electron";
|
||||
import { SecondarySubMode } from "../../types";
|
||||
import { ConfiguredShortcuts } from "../utils/shortcut-config";
|
||||
import { CycleSecondarySubModeDeps } from "./secondary-subtitle-service";
|
||||
import { OverlayShortcutFallbackHandlers, runOverlayShortcutLocalFallback } from "./overlay-shortcut-fallback-runner";
|
||||
import { OpenYomitanSettingsWindowOptions } from "./yomitan-settings-service";
|
||||
import { RegisterGlobalShortcutsServiceOptions } from "./shortcut-service";
|
||||
|
||||
export interface ShortcutUiRuntimeDepsOptions {
|
||||
yomitanExt: Extension | null;
|
||||
getYomitanSettingsWindow: OpenYomitanSettingsWindowOptions["getExistingWindow"];
|
||||
setYomitanSettingsWindow: OpenYomitanSettingsWindowOptions["setWindow"];
|
||||
|
||||
shortcuts: RegisterGlobalShortcutsServiceOptions["shortcuts"];
|
||||
onToggleVisibleOverlay: () => void;
|
||||
onToggleInvisibleOverlay: () => void;
|
||||
onOpenYomitanSettings: () => void;
|
||||
isDev: boolean;
|
||||
getMainWindow: RegisterGlobalShortcutsServiceOptions["getMainWindow"];
|
||||
|
||||
getSecondarySubMode: () => SecondarySubMode;
|
||||
setSecondarySubMode: (mode: SecondarySubMode) => void;
|
||||
getLastSecondarySubToggleAtMs: () => number;
|
||||
setLastSecondarySubToggleAtMs: (timestampMs: number) => void;
|
||||
broadcastSecondarySubMode: (mode: SecondarySubMode) => void;
|
||||
showMpvOsd: (text: string) => void;
|
||||
|
||||
getConfiguredShortcuts: () => ConfiguredShortcuts;
|
||||
getOverlayShortcutFallbackHandlers: () => OverlayShortcutFallbackHandlers;
|
||||
shortcutMatcher: (
|
||||
input: Electron.Input,
|
||||
accelerator: string,
|
||||
allowWhenRegistered?: boolean,
|
||||
) => boolean;
|
||||
}
|
||||
|
||||
export function createYomitanSettingsWindowDepsRuntimeService(
|
||||
options: ShortcutUiRuntimeDepsOptions,
|
||||
): OpenYomitanSettingsWindowOptions {
|
||||
return {
|
||||
yomitanExt: options.yomitanExt,
|
||||
getExistingWindow: options.getYomitanSettingsWindow,
|
||||
setWindow: options.setYomitanSettingsWindow,
|
||||
};
|
||||
}
|
||||
|
||||
export function createGlobalShortcutRegistrationDepsRuntimeService(
|
||||
options: ShortcutUiRuntimeDepsOptions,
|
||||
): RegisterGlobalShortcutsServiceOptions {
|
||||
return {
|
||||
shortcuts: options.shortcuts,
|
||||
onToggleVisibleOverlay: options.onToggleVisibleOverlay,
|
||||
onToggleInvisibleOverlay: options.onToggleInvisibleOverlay,
|
||||
onOpenYomitanSettings: options.onOpenYomitanSettings,
|
||||
isDev: options.isDev,
|
||||
getMainWindow: options.getMainWindow,
|
||||
};
|
||||
}
|
||||
|
||||
export function createSecondarySubtitleCycleDepsRuntimeService(
|
||||
options: ShortcutUiRuntimeDepsOptions,
|
||||
): CycleSecondarySubModeDeps {
|
||||
return {
|
||||
getSecondarySubMode: options.getSecondarySubMode,
|
||||
setSecondarySubMode: options.setSecondarySubMode,
|
||||
getLastSecondarySubToggleAtMs: options.getLastSecondarySubToggleAtMs,
|
||||
setLastSecondarySubToggleAtMs: options.setLastSecondarySubToggleAtMs,
|
||||
broadcastSecondarySubMode: options.broadcastSecondarySubMode,
|
||||
showMpvOsd: options.showMpvOsd,
|
||||
};
|
||||
}
|
||||
|
||||
export function runOverlayShortcutLocalFallbackRuntimeService(
|
||||
input: Electron.Input,
|
||||
options: ShortcutUiRuntimeDepsOptions,
|
||||
): boolean {
|
||||
return runOverlayShortcutLocalFallback(
|
||||
input,
|
||||
options.getConfiguredShortcuts(),
|
||||
options.shortcutMatcher,
|
||||
options.getOverlayShortcutFallbackHandlers(),
|
||||
);
|
||||
}
|
||||
@@ -7,10 +7,6 @@ import {
|
||||
AppShutdownRuntimeDeps,
|
||||
runAppShutdownRuntimeService,
|
||||
} from "./app-shutdown-runtime-service";
|
||||
import {
|
||||
createStartupAppReadyDepsRuntimeService,
|
||||
createStartupAppShutdownDepsRuntimeService,
|
||||
} from "./startup-lifecycle-runtime-deps-service";
|
||||
|
||||
type StartupLifecycleHookDeps = Pick<
|
||||
AppLifecycleDepsRuntimeOptions,
|
||||
@@ -29,14 +25,10 @@ export function createStartupLifecycleHooksRuntimeService(
|
||||
): StartupLifecycleHookDeps {
|
||||
return {
|
||||
onReady: async () => {
|
||||
await runAppReadyRuntimeService(
|
||||
createStartupAppReadyDepsRuntimeService(options.appReadyDeps),
|
||||
);
|
||||
await runAppReadyRuntimeService(options.appReadyDeps);
|
||||
},
|
||||
onWillQuitCleanup: () => {
|
||||
runAppShutdownRuntimeService(
|
||||
createStartupAppShutdownDepsRuntimeService(options.appShutdownDeps),
|
||||
);
|
||||
runAppShutdownRuntimeService(options.appShutdownDeps);
|
||||
},
|
||||
shouldRestoreWindowsOnActivate: options.shouldRestoreWindowsOnActivate,
|
||||
restoreWindowsOnActivate: options.restoreWindowsOnActivate,
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
import {
|
||||
createStartupAppReadyDepsRuntimeService,
|
||||
createStartupAppShutdownDepsRuntimeService,
|
||||
} from "./startup-lifecycle-runtime-deps-service";
|
||||
|
||||
test("createStartupAppReadyDepsRuntimeService preserves runtime deps behavior", async () => {
|
||||
const calls: string[] = [];
|
||||
const deps = createStartupAppReadyDepsRuntimeService({
|
||||
loadSubtitlePosition: () => calls.push("loadSubtitlePosition"),
|
||||
resolveKeybindings: () => calls.push("resolveKeybindings"),
|
||||
createMpvClient: () => calls.push("createMpvClient"),
|
||||
reloadConfig: () => calls.push("reloadConfig"),
|
||||
getResolvedConfig: () => ({
|
||||
secondarySub: { defaultMode: "hover" },
|
||||
websocket: { enabled: "auto", port: 1234 },
|
||||
}),
|
||||
getConfigWarnings: () => [],
|
||||
logConfigWarning: () => {},
|
||||
initRuntimeOptionsManager: () => calls.push("initRuntimeOptionsManager"),
|
||||
setSecondarySubMode: () => calls.push("setSecondarySubMode"),
|
||||
defaultSecondarySubMode: "hover",
|
||||
defaultWebsocketPort: 8765,
|
||||
hasMpvWebsocketPlugin: () => true,
|
||||
startSubtitleWebsocket: () => calls.push("startSubtitleWebsocket"),
|
||||
log: () => calls.push("log"),
|
||||
createMecabTokenizerAndCheck: async () => {
|
||||
calls.push("createMecab");
|
||||
},
|
||||
createSubtitleTimingTracker: () => calls.push("createSubtitleTimingTracker"),
|
||||
loadYomitanExtension: async () => {
|
||||
calls.push("loadYomitan");
|
||||
},
|
||||
texthookerOnlyMode: false,
|
||||
shouldAutoInitializeOverlayRuntimeFromConfig: () => false,
|
||||
initializeOverlayRuntime: () => calls.push("initOverlayRuntime"),
|
||||
handleInitialArgs: () => calls.push("handleInitialArgs"),
|
||||
});
|
||||
|
||||
deps.loadSubtitlePosition();
|
||||
await deps.createMecabTokenizerAndCheck();
|
||||
deps.handleInitialArgs();
|
||||
|
||||
assert.equal(deps.defaultWebsocketPort, 8765);
|
||||
assert.equal(deps.defaultSecondarySubMode, "hover");
|
||||
assert.deepEqual(calls, ["loadSubtitlePosition", "createMecab", "handleInitialArgs"]);
|
||||
});
|
||||
|
||||
test("createStartupAppShutdownDepsRuntimeService preserves shutdown handlers", () => {
|
||||
const calls: string[] = [];
|
||||
const deps = createStartupAppShutdownDepsRuntimeService({
|
||||
unregisterAllGlobalShortcuts: () => calls.push("unregisterAllGlobalShortcuts"),
|
||||
stopSubtitleWebsocket: () => calls.push("stopSubtitleWebsocket"),
|
||||
stopTexthookerService: () => calls.push("stopTexthookerService"),
|
||||
destroyYomitanParserWindow: () => calls.push("destroyYomitanParserWindow"),
|
||||
clearYomitanParserPromises: () => calls.push("clearYomitanParserPromises"),
|
||||
stopWindowTracker: () => calls.push("stopWindowTracker"),
|
||||
destroyMpvSocket: () => calls.push("destroyMpvSocket"),
|
||||
clearReconnectTimer: () => calls.push("clearReconnectTimer"),
|
||||
destroySubtitleTimingTracker: () => calls.push("destroySubtitleTimingTracker"),
|
||||
destroyAnkiIntegration: () => calls.push("destroyAnkiIntegration"),
|
||||
});
|
||||
|
||||
deps.stopSubtitleWebsocket();
|
||||
deps.clearReconnectTimer();
|
||||
deps.destroyAnkiIntegration();
|
||||
|
||||
assert.deepEqual(calls, [
|
||||
"stopSubtitleWebsocket",
|
||||
"clearReconnectTimer",
|
||||
"destroyAnkiIntegration",
|
||||
]);
|
||||
});
|
||||
@@ -1,55 +0,0 @@
|
||||
import {
|
||||
AppReadyRuntimeDeps,
|
||||
} from "./app-ready-runtime-service";
|
||||
import {
|
||||
AppShutdownRuntimeDeps,
|
||||
} from "./app-shutdown-runtime-service";
|
||||
|
||||
export type StartupAppReadyDepsRuntimeOptions = AppReadyRuntimeDeps;
|
||||
export type StartupAppShutdownDepsRuntimeOptions = AppShutdownRuntimeDeps;
|
||||
|
||||
export function createStartupAppReadyDepsRuntimeService(
|
||||
options: StartupAppReadyDepsRuntimeOptions,
|
||||
): AppReadyRuntimeDeps {
|
||||
return {
|
||||
loadSubtitlePosition: options.loadSubtitlePosition,
|
||||
resolveKeybindings: options.resolveKeybindings,
|
||||
createMpvClient: options.createMpvClient,
|
||||
reloadConfig: options.reloadConfig,
|
||||
getResolvedConfig: options.getResolvedConfig,
|
||||
getConfigWarnings: options.getConfigWarnings,
|
||||
logConfigWarning: options.logConfigWarning,
|
||||
initRuntimeOptionsManager: options.initRuntimeOptionsManager,
|
||||
setSecondarySubMode: options.setSecondarySubMode,
|
||||
defaultSecondarySubMode: options.defaultSecondarySubMode,
|
||||
defaultWebsocketPort: options.defaultWebsocketPort,
|
||||
hasMpvWebsocketPlugin: options.hasMpvWebsocketPlugin,
|
||||
startSubtitleWebsocket: options.startSubtitleWebsocket,
|
||||
log: options.log,
|
||||
createMecabTokenizerAndCheck: options.createMecabTokenizerAndCheck,
|
||||
createSubtitleTimingTracker: options.createSubtitleTimingTracker,
|
||||
loadYomitanExtension: options.loadYomitanExtension,
|
||||
texthookerOnlyMode: options.texthookerOnlyMode,
|
||||
shouldAutoInitializeOverlayRuntimeFromConfig:
|
||||
options.shouldAutoInitializeOverlayRuntimeFromConfig,
|
||||
initializeOverlayRuntime: options.initializeOverlayRuntime,
|
||||
handleInitialArgs: options.handleInitialArgs,
|
||||
};
|
||||
}
|
||||
|
||||
export function createStartupAppShutdownDepsRuntimeService(
|
||||
options: StartupAppShutdownDepsRuntimeOptions,
|
||||
): AppShutdownRuntimeDeps {
|
||||
return {
|
||||
unregisterAllGlobalShortcuts: options.unregisterAllGlobalShortcuts,
|
||||
stopSubtitleWebsocket: options.stopSubtitleWebsocket,
|
||||
stopTexthookerService: options.stopTexthookerService,
|
||||
destroyYomitanParserWindow: options.destroyYomitanParserWindow,
|
||||
clearYomitanParserPromises: options.clearYomitanParserPromises,
|
||||
stopWindowTracker: options.stopWindowTracker,
|
||||
destroyMpvSocket: options.destroyMpvSocket,
|
||||
clearReconnectTimer: options.clearReconnectTimer,
|
||||
destroySubtitleTimingTracker: options.destroySubtitleTimingTracker,
|
||||
destroyAnkiIntegration: options.destroyAnkiIntegration,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user