diff --git a/src/core/services/index.ts b/src/core/services/index.ts index 71d2e85..097866c 100644 --- a/src/core/services/index.ts +++ b/src/core/services/index.ts @@ -56,7 +56,11 @@ export { updateOverlayBoundsService, } from "./overlay-window-service"; export { initializeOverlayRuntimeService } from "./overlay-runtime-init-service"; -export { syncInvisibleOverlayMousePassthroughService } from "./overlay-visibility-runtime-service"; +export { + setInvisibleOverlayVisibleService, + setVisibleOverlayVisibleService, + syncInvisibleOverlayMousePassthroughService, +} from "./overlay-visibility-runtime-service"; export { setInvisibleOverlayVisibleRuntimeFacadeService, setVisibleOverlayVisibleRuntimeFacadeService, diff --git a/src/core/services/mpv-service.ts b/src/core/services/mpv-service.ts index 3c5c54c..5e52520 100644 --- a/src/core/services/mpv-service.ts +++ b/src/core/services/mpv-service.ts @@ -397,9 +397,9 @@ export class MpvIpcClient implements MpvClient { this.send({ command: ["set_property", "secondary-sid", match.id], }); - this.deps.showMpvOsd( - `Secondary subtitle: ${lang} (track ${match.id})`, - ); + // this.deps.showMpvOsd( + // `Secondary subtitle: ${lang} (track ${match.id})`, + // ); break; } } diff --git a/src/main.ts b/src/main.ts index f47d932..7389b51 100644 --- a/src/main.ts +++ b/src/main.ts @@ -51,12 +51,15 @@ import type { Keybinding, WindowGeometry, SecondarySubMode, + SubsyncManualPayload, SubsyncManualRunRequest, SubsyncResult, KikuFieldGroupingChoice, KikuMergePreviewRequest, KikuMergePreviewResponse, + RuntimeOptionId, RuntimeOptionState, + RuntimeOptionValue, MpvSubtitleRenderMetrics, } from "./types"; import { SubtitleTimingTracker } from "./subtitle-timing-tracker"; @@ -98,22 +101,14 @@ import { createCliCommandDepsRuntimeService, createOverlayManagerService, createFieldGroupingOverlayRuntimeService, - createInitializeOverlayRuntimeDepsService, - createInvisibleOverlayVisibilityDepsRuntimeService, createIpcDepsRuntimeService, createMecabTokenizerAndCheckRuntimeService, - createMpvCommandIpcDepsRuntimeService, createNumericShortcutRuntimeService, createOverlayShortcutRuntimeHandlers, - createOverlayWindowRuntimeDepsService, createOverlayWindowService, - createRuntimeOptionsIpcDepsRuntimeService, createRuntimeOptionsManagerRuntimeService, - createStartupLifecycleHooksRuntimeService, - createSubsyncRuntimeDepsService, createSubtitleTimingTrackerRuntimeService, createTokenizerDepsRuntimeService, - createVisibleOverlayVisibilityDepsRuntimeService, cycleSecondarySubModeService, enforceOverlayLayerOrderService, ensureOverlayWindowLevelService, @@ -148,10 +143,10 @@ import { runSubsyncManualFromIpcRuntimeService, saveSubtitlePositionService, sendMpvCommandRuntimeService, - setInvisibleOverlayVisibleRuntimeFacadeService, + setInvisibleOverlayVisibleService, setMpvSubVisibilityRuntimeService, setOverlayDebugVisualizationEnabledRuntimeService, - setVisibleOverlayVisibleRuntimeFacadeService, + setVisibleOverlayVisibleService, shouldAutoInitializeOverlayRuntimeFromConfigService, shouldBindVisibleOverlayToMpvSubVisibilityService, shortcutMatchesInputForLocalFallback, @@ -160,8 +155,6 @@ import { syncInvisibleOverlayMousePassthroughService, syncOverlayShortcutsRuntimeService, tokenizeSubtitleService, - toggleInvisibleOverlayRuntimeFacadeService, - toggleVisibleOverlayRuntimeFacadeService, triggerFieldGroupingService, triggerSubsyncFromConfigRuntimeService, unregisterOverlayShortcutsRuntimeService, @@ -171,6 +164,13 @@ import { updateOverlayBoundsService, updateVisibleOverlayVisibilityService, } from "./core/services"; +import { runAppReadyRuntimeService } from "./core/services/app-ready-runtime-service"; +import { runAppShutdownRuntimeService } from "./core/services/app-shutdown-runtime-service"; +import { + applyRuntimeOptionResultRuntimeService, + cycleRuntimeOptionFromIpcRuntimeService, + setRuntimeOptionFromIpcRuntimeService, +} from "./core/services/runtime-options-runtime-service"; import { ConfigService, DEFAULT_CONFIG, @@ -487,8 +487,8 @@ const startupState = runStartupBootstrapRuntimeService({ handleCliCommand: (nextArgs, source) => handleCliCommand(nextArgs, source), printHelp: () => printHelp(DEFAULT_TEXTHOOKER_PORT), logNoRunningInstance: () => appLogger.logNoRunningInstance(), - ...createStartupLifecycleHooksRuntimeService({ - appReadyDeps: { + onReady: async () => { + await runAppReadyRuntimeService({ loadSubtitlePosition: () => loadSubtitlePosition(), resolveKeybindings: () => { keybindings = resolveKeybindings(getResolvedConfig(), DEFAULT_KEYBINDINGS); @@ -593,8 +593,10 @@ const startupState = runStartupBootstrapRuntimeService({ shouldAutoInitializeOverlayRuntimeFromConfig(), initializeOverlayRuntime: () => initializeOverlayRuntime(), handleInitialArgs: () => handleInitialArgs(), - }, - appShutdownDeps: { + }); + }, + onWillQuitCleanup: () => { + runAppShutdownRuntimeService({ unregisterAllGlobalShortcuts: () => { globalShortcut.unregisterAll(); }, @@ -639,16 +641,16 @@ const startupState = runStartupBootstrapRuntimeService({ ankiIntegration.destroy(); } }, - }, - shouldRestoreWindowsOnActivate: () => - overlayRuntimeInitialized && BrowserWindow.getAllWindows().length === 0, - restoreWindowsOnActivate: () => { - createMainWindow(); - createInvisibleWindow(); - updateVisibleOverlayVisibility(); - updateInvisibleOverlayVisibility(); - }, - }), + }); + }, + shouldRestoreWindowsOnActivate: () => + overlayRuntimeInitialized && BrowserWindow.getAllWindows().length === 0, + restoreWindowsOnActivate: () => { + createMainWindow(); + createInvisibleWindow(); + updateVisibleOverlayVisibility(); + updateInvisibleOverlayVisibility(); + }, })); }, }); @@ -809,15 +811,17 @@ async function loadYomitanExtension(): Promise { function createOverlayWindow(kind: "visible" | "invisible"): BrowserWindow { return createOverlayWindowService( kind, - createOverlayWindowRuntimeDepsService({ + { isDev, - getOverlayDebugVisualizationEnabled: () => overlayDebugVisualizationEnabled, + overlayDebugVisualizationEnabled, ensureOverlayWindowLevel: (window) => ensureOverlayWindowLevel(window), onRuntimeOptionsChanged: () => broadcastRuntimeOptionsChanged(), setOverlayDebugVisualizationEnabled: (enabled) => setOverlayDebugVisualizationEnabled(enabled), - getVisibleOverlayVisible: () => overlayManager.getVisibleOverlayVisible(), - getInvisibleOverlayVisible: () => overlayManager.getInvisibleOverlayVisible(), + isOverlayVisible: (windowKind) => + windowKind === "visible" + ? overlayManager.getVisibleOverlayVisible() + : overlayManager.getInvisibleOverlayVisible(), tryHandleOverlayShortcutLocalFallback: (input) => tryHandleOverlayShortcutLocalFallback(input), onWindowClosed: (windowKind) => { @@ -827,7 +831,7 @@ function createOverlayWindow(kind: "visible" | "invisible"): BrowserWindow { overlayManager.setInvisibleWindow(null); } }, - }), + }, ); } @@ -847,7 +851,7 @@ function initializeOverlayRuntime(): void { return; } const result = initializeOverlayRuntimeService( - createInitializeOverlayRuntimeDepsService({ + { backendOverride, getInitialInvisibleOverlayVisibility: () => getInitialInvisibleOverlayVisibility(), @@ -888,7 +892,7 @@ function initializeOverlayRuntime(): void { }, showDesktopNotification, createFieldGroupingCallback: () => createFieldGroupingCallback(), - }), + }, ); overlayManager.setInvisibleOverlayVisible(result.invisibleOverlayVisible); overlayRuntimeInitialized = true; @@ -1002,29 +1006,9 @@ const numericShortcutRuntime = createNumericShortcutRuntimeService({ }); const multiCopySession = numericShortcutRuntime.createSession(); const mineSentenceSession = numericShortcutRuntime.createSession(); -const overlayVisibilityFacadeDeps = { - getVisibleOverlayVisible: () => overlayManager.getVisibleOverlayVisible(), - getInvisibleOverlayVisible: () => overlayManager.getInvisibleOverlayVisible(), - setVisibleOverlayVisibleState: (nextVisible: boolean) => { - overlayManager.setVisibleOverlayVisible(nextVisible); - }, - setInvisibleOverlayVisibleState: (nextVisible: boolean) => { - overlayManager.setInvisibleOverlayVisible(nextVisible); - }, - updateVisibleOverlayVisibility: () => updateVisibleOverlayVisibility(), - updateInvisibleOverlayVisibility: () => updateInvisibleOverlayVisibility(), - syncInvisibleOverlayMousePassthrough: () => - syncInvisibleOverlayMousePassthrough(), - shouldBindVisibleOverlayToMpvSubVisibility: () => - shouldBindVisibleOverlayToMpvSubVisibility(), - isMpvConnected: () => Boolean(mpvClient && mpvClient.connected), - setMpvSubVisibility: (mpvSubVisible: boolean) => { - setMpvSubVisibilityRuntimeService(mpvClient, mpvSubVisible); - }, -}; function getSubsyncRuntimeDeps() { - return createSubsyncRuntimeDepsService({ + return { getMpvClient: () => mpvClient, getResolvedSubsyncConfig: () => getSubsyncConfig(getResolvedConfig().subsync), isSubsyncInProgress: () => subsyncInProgress, @@ -1032,9 +1016,12 @@ function getSubsyncRuntimeDeps() { subsyncInProgress = inProgress; }, showMpvOsd: (text: string) => showMpvOsd(text), - sendToVisibleOverlay: (channel, payload, options) => - sendToVisibleOverlay(channel, payload, options), - }); + openManualPicker: (payload: SubsyncManualPayload) => { + sendToVisibleOverlay("subsync:open-manual", payload, { + restoreOnModalClose: "subsync", + }); + }, + }; } async function triggerSubsyncFromConfig(): Promise { @@ -1189,21 +1176,21 @@ function refreshOverlayShortcuts(): void { function updateVisibleOverlayVisibility(): void { updateVisibleOverlayVisibilityService( - createVisibleOverlayVisibilityDepsRuntimeService({ - getVisibleOverlayVisible: () => overlayManager.getVisibleOverlayVisible(), - getMainWindow: () => overlayManager.getMainWindow(), - getWindowTracker: () => windowTracker, - getTrackerNotReadyWarningShown: () => trackerNotReadyWarningShown, + { + visibleOverlayVisible: overlayManager.getVisibleOverlayVisible(), + mainWindow: overlayManager.getMainWindow(), + windowTracker, + trackerNotReadyWarningShown, setTrackerNotReadyWarningShown: (shown) => { trackerNotReadyWarningShown = shown; }, - shouldBindVisibleOverlayToMpvSubVisibility: () => + shouldBindVisibleOverlayToMpvSubVisibility: shouldBindVisibleOverlayToMpvSubVisibility(), - getPreviousSecondarySubVisibility: () => previousSecondarySubVisibility, + previousSecondarySubVisibility, setPreviousSecondarySubVisibility: (value) => { previousSecondarySubVisibility = value; }, - isMpvConnected: () => Boolean(mpvClient && mpvClient.connected), + mpvConnected: Boolean(mpvClient && mpvClient.connected), mpvSend: (payload) => { if (!mpvClient) return; mpvClient.send(payload); @@ -1213,23 +1200,22 @@ function updateVisibleOverlayVisibility(): void { ensureOverlayWindowLevel: (window) => ensureOverlayWindowLevel(window), enforceOverlayLayerOrder: () => enforceOverlayLayerOrder(), syncOverlayShortcuts: () => syncOverlayShortcuts(), - }), + }, ); } function updateInvisibleOverlayVisibility(): void { updateInvisibleOverlayVisibilityService( - createInvisibleOverlayVisibilityDepsRuntimeService({ - getInvisibleWindow: () => overlayManager.getInvisibleWindow(), - getVisibleOverlayVisible: () => overlayManager.getVisibleOverlayVisible(), - getInvisibleOverlayVisible: () => - overlayManager.getInvisibleOverlayVisible(), - getWindowTracker: () => windowTracker, + { + invisibleWindow: overlayManager.getInvisibleWindow(), + visibleOverlayVisible: overlayManager.getVisibleOverlayVisible(), + invisibleOverlayVisible: overlayManager.getInvisibleOverlayVisible(), + windowTracker, updateOverlayBounds: (geometry) => updateOverlayBounds(geometry), ensureOverlayWindowLevel: (window) => ensureOverlayWindowLevel(window), enforceOverlayLayerOrder: () => enforceOverlayLayerOrder(), syncOverlayShortcuts: () => syncOverlayShortcuts(), - }), + }, ); } @@ -1250,21 +1236,41 @@ function syncInvisibleOverlayMousePassthrough(): void { } function setVisibleOverlayVisible(visible: boolean): void { - setVisibleOverlayVisibleRuntimeFacadeService(visible, overlayVisibilityFacadeDeps); + setVisibleOverlayVisibleService({ + visible, + setVisibleOverlayVisibleState: (nextVisible) => { + overlayManager.setVisibleOverlayVisible(nextVisible); + }, + updateVisibleOverlayVisibility: () => updateVisibleOverlayVisibility(), + updateInvisibleOverlayVisibility: () => updateInvisibleOverlayVisibility(), + syncInvisibleOverlayMousePassthrough: () => + syncInvisibleOverlayMousePassthrough(), + shouldBindVisibleOverlayToMpvSubVisibility: () => + shouldBindVisibleOverlayToMpvSubVisibility(), + isMpvConnected: () => Boolean(mpvClient && mpvClient.connected), + setMpvSubVisibility: (mpvSubVisible) => { + setMpvSubVisibilityRuntimeService(mpvClient, mpvSubVisible); + }, + }); } function setInvisibleOverlayVisible(visible: boolean): void { - setInvisibleOverlayVisibleRuntimeFacadeService( + setInvisibleOverlayVisibleService({ visible, - overlayVisibilityFacadeDeps, - ); + setInvisibleOverlayVisibleState: (nextVisible) => { + overlayManager.setInvisibleOverlayVisible(nextVisible); + }, + updateInvisibleOverlayVisibility: () => updateInvisibleOverlayVisibility(), + syncInvisibleOverlayMousePassthrough: () => + syncInvisibleOverlayMousePassthrough(), + }); } function toggleVisibleOverlay(): void { - toggleVisibleOverlayRuntimeFacadeService(overlayVisibilityFacadeDeps); + setVisibleOverlayVisible(!overlayManager.getVisibleOverlayVisible()); } function toggleInvisibleOverlay(): void { - toggleInvisibleOverlayRuntimeFacadeService(overlayVisibilityFacadeDeps); + setInvisibleOverlayVisible(!overlayManager.getInvisibleOverlayVisible()); } function setOverlayVisible(visible: boolean): void { setVisibleOverlayVisible(visible); } function toggleOverlay(): void { toggleVisibleOverlay(); } @@ -1279,18 +1285,27 @@ function handleOverlayModalClosed(modal: OverlayHostedModal): void { function handleMpvCommandFromIpc(command: (string | number)[]): void { handleMpvCommandFromIpcService( command, - createMpvCommandIpcDepsRuntimeService({ + { specialCommands: SPECIAL_COMMANDS, triggerSubsyncFromConfig: () => triggerSubsyncFromConfig(), openRuntimeOptionsPalette: () => openRuntimeOptionsPalette(), - getRuntimeOptionsManager: () => runtimeOptionsManager, + runtimeOptionsCycle: (id, direction) => { + if (!runtimeOptionsManager) { + return { ok: false, error: "Runtime options manager unavailable" }; + } + return applyRuntimeOptionResultRuntimeService( + runtimeOptionsManager.cycleOption(id, direction), + (text) => showMpvOsd(text), + ); + }, showMpvOsd: (text) => showMpvOsd(text), mpvReplaySubtitle: () => replayCurrentSubtitleRuntimeService(mpvClient), mpvPlayNextSubtitle: () => playNextSubtitleRuntimeService(mpvClient), mpvSendCommand: (rawCommand) => sendMpvCommandRuntimeService(mpvClient, rawCommand), isMpvConnected: () => Boolean(mpvClient && mpvClient.connected), - }), + hasRuntimeOptionsManager: () => runtimeOptionsManager !== null, + }, ); } @@ -1300,10 +1315,22 @@ async function runSubsyncManualFromIpc( return runSubsyncManualFromIpcRuntimeService(request, getSubsyncRuntimeDeps()); } -const runtimeOptionsIpcDeps = createRuntimeOptionsIpcDepsRuntimeService({ - getRuntimeOptionsManager: () => runtimeOptionsManager, - showMpvOsd: (text) => showMpvOsd(text), -}); +const runtimeOptionsIpcDeps = { + setRuntimeOption: (id: string, value: unknown) => + setRuntimeOptionFromIpcRuntimeService( + runtimeOptionsManager, + id as RuntimeOptionId, + value as RuntimeOptionValue, + (text) => showMpvOsd(text), + ), + cycleRuntimeOption: (id: string, direction: 1 | -1) => + cycleRuntimeOptionFromIpcRuntimeService( + runtimeOptionsManager, + id as RuntimeOptionId, + direction, + (text) => showMpvOsd(text), + ), +}; registerIpcHandlersService( createIpcDepsRuntimeService({