mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-11-10 04:46:07 -08:00
* refactor(main): drop remaining pass-through main-deps builders Jellyfin client-info/setup-window/remote, overlay shortcuts/visibility/ layout/window-factory, config hot-reload, and startup bootstrap factory builders only forwarded deps. Callers now pass deps to the handlers directly; createWatchConfigPathHandler moves into config-hot-reload-handlers. * refactor(main): fold small-logic main-deps builders into their handlers Tray click, mpv client factory, startup bootstrap exit callbacks, overlay runtime options, and dictionary roots/runtime builders move their logic into the owning handler or call site. The two duplicated dictionary-root builders become one createBuildDictionaryRootsHandler(vendorDirName). * refactor(main): fold cli command context builders into the factory createCliCommandContextFactory now adapts appState and main services to the CLI command context deps directly (socket-path change notification, texthooker port/websocket URL, browser-open default). The pass-through cli-command-context-main-deps and cli-command-context-deps layers are removed; their behavioural tests now run against the factory's context. * refactor(main): fold subtitle tokenization builder into the mpv composer The tokenizer deps builder's logic (known-word lookup recording and the character-dictionary gate on name matching) becomes buildTokenizerRuntimeOptions in mpv-runtime-composer, tested through the composer. The MeCab check and dictionary prewarm handlers move to subtitle-tokenization-warmup.ts, and the unused BuiltMainDeps type is removed from composer contracts. * refactor(main): drop redundant tokenizer deps keys and cast * refactor(main): end the main-deps builder pattern mpv-main-event-main-deps becomes mpv-main-event-binding-deps (createMpvMainEventBindingDeps), returning the binding deps directly instead of a thunk; it owns the subtitle-recording state. The will-quit cleanup adapter likewise returns its deps directly, and the pass-through app-lifecycle activate builders are removed. * docs: drop references to main-deps builders
133 lines
6.2 KiB
TypeScript
133 lines
6.2 KiB
TypeScript
import type {
|
|
AnkiConnectConfig,
|
|
KikuFieldGroupingChoice,
|
|
KikuFieldGroupingRequestData,
|
|
} from '../../types/anki';
|
|
import type { BrowserWindow } from 'electron';
|
|
import type { WindowGeometry } from '../../types/runtime';
|
|
import type { OverlayNotificationPayload } from '../../types/notification';
|
|
import type { BaseWindowTracker } from '../../window-trackers';
|
|
|
|
type OverlayRuntimeOptions = {
|
|
backendOverride: string | null;
|
|
createMainWindow: () => void;
|
|
registerGlobalShortcuts: () => void;
|
|
updateVisibleOverlayBounds: (geometry: WindowGeometry) => void;
|
|
isVisibleOverlayVisible: () => boolean;
|
|
updateVisibleOverlayVisibility: () => void;
|
|
refreshCurrentSubtitle?: () => void;
|
|
getOverlayWindows: () => BrowserWindow[];
|
|
syncOverlayShortcuts: () => void;
|
|
setWindowTracker: (tracker: BaseWindowTracker | null) => void;
|
|
createWindowTracker?: (
|
|
override?: string | null,
|
|
targetMpvSocketPath?: string | null,
|
|
) => BaseWindowTracker | null;
|
|
getResolvedConfig: () => { ankiConnect?: AnkiConnectConfig };
|
|
getSubtitleTimingTracker: () => unknown | null;
|
|
getMpvClient: () => { send?: (payload: { command: string[] }) => void } | null;
|
|
getMpvSocketPath: () => string;
|
|
getRuntimeOptionsManager: () => {
|
|
getEffectiveAnkiConnectConfig: (config?: AnkiConnectConfig) => AnkiConnectConfig;
|
|
} | null;
|
|
setAnkiIntegration: (integration: unknown | null) => void;
|
|
showDesktopNotification: (title: string, options: { body?: string; icon?: string }) => void;
|
|
showOverlayNotification?: (payload: OverlayNotificationPayload) => void;
|
|
dismissOverlayNotification?: (id: string) => void;
|
|
createFieldGroupingCallback: () => (
|
|
data: KikuFieldGroupingRequestData,
|
|
) => Promise<KikuFieldGroupingChoice>;
|
|
getKnownWordCacheStatePath: () => string;
|
|
getCachedMediaPath?: (
|
|
currentVideoPath: string,
|
|
kind: 'audio' | 'video',
|
|
) => Promise<string | null>;
|
|
shouldRequireRemoteMediaCache?: () => boolean;
|
|
getYoutubeMediaSourceUrl?: () => Promise<string | null | undefined> | string | null | undefined;
|
|
shouldStartAnkiIntegration: () => boolean;
|
|
bindOverlayOwner?: () => void;
|
|
releaseOverlayOwner?: () => void;
|
|
};
|
|
|
|
// Builds the options for core initializeOverlayRuntime from main-process state. appState
|
|
// fields are read when the options are built (or lazily via getters), and the window tracker
|
|
// and Anki integration produced by the core are written back to appState.
|
|
export function createBuildInitializeOverlayRuntimeOptionsHandler(deps: {
|
|
appState: {
|
|
backendOverride: string | null;
|
|
windowTracker: Parameters<OverlayRuntimeOptions['setWindowTracker']>[0];
|
|
subtitleTimingTracker: ReturnType<OverlayRuntimeOptions['getSubtitleTimingTracker']>;
|
|
mpvClient: ReturnType<OverlayRuntimeOptions['getMpvClient']>;
|
|
mpvSocketPath: string;
|
|
runtimeOptionsManager: ReturnType<OverlayRuntimeOptions['getRuntimeOptionsManager']>;
|
|
ankiIntegration: Parameters<OverlayRuntimeOptions['setAnkiIntegration']>[0];
|
|
};
|
|
overlayManager: {
|
|
getVisibleOverlayVisible: () => boolean;
|
|
};
|
|
overlayVisibilityRuntime: {
|
|
updateVisibleOverlayVisibility: () => void;
|
|
};
|
|
refreshCurrentSubtitle?: () => void;
|
|
overlayShortcutsRuntime: {
|
|
syncOverlayShortcuts: () => void;
|
|
};
|
|
createMainWindow: () => void;
|
|
registerGlobalShortcuts: () => void;
|
|
updateVisibleOverlayBounds: (geometry: WindowGeometry) => void;
|
|
getOverlayWindows: () => BrowserWindow[];
|
|
createWindowTracker?: OverlayRuntimeOptions['createWindowTracker'];
|
|
getResolvedConfig: () => { ankiConnect?: AnkiConnectConfig };
|
|
showDesktopNotification: OverlayRuntimeOptions['showDesktopNotification'];
|
|
showOverlayNotification?: (payload: OverlayNotificationPayload) => void;
|
|
dismissOverlayNotification?: (id: string) => void;
|
|
createFieldGroupingCallback: OverlayRuntimeOptions['createFieldGroupingCallback'];
|
|
getKnownWordCacheStatePath: () => string;
|
|
getCachedMediaPath?: OverlayRuntimeOptions['getCachedMediaPath'];
|
|
shouldRequireRemoteMediaCache?: () => boolean;
|
|
getYoutubeMediaSourceUrl?: OverlayRuntimeOptions['getYoutubeMediaSourceUrl'];
|
|
shouldStartAnkiIntegration: () => boolean;
|
|
bindOverlayOwner?: () => void;
|
|
releaseOverlayOwner?: () => void;
|
|
}) {
|
|
return (): OverlayRuntimeOptions => ({
|
|
backendOverride: deps.appState.backendOverride,
|
|
createMainWindow: () => deps.createMainWindow(),
|
|
registerGlobalShortcuts: () => deps.registerGlobalShortcuts(),
|
|
updateVisibleOverlayBounds: (geometry) => deps.updateVisibleOverlayBounds(geometry),
|
|
isVisibleOverlayVisible: () => deps.overlayManager.getVisibleOverlayVisible(),
|
|
updateVisibleOverlayVisibility: () =>
|
|
deps.overlayVisibilityRuntime.updateVisibleOverlayVisibility(),
|
|
refreshCurrentSubtitle: () => deps.refreshCurrentSubtitle?.(),
|
|
getOverlayWindows: () => deps.getOverlayWindows(),
|
|
syncOverlayShortcuts: () => deps.overlayShortcutsRuntime.syncOverlayShortcuts(),
|
|
setWindowTracker: (tracker) => {
|
|
deps.appState.windowTracker = tracker;
|
|
},
|
|
createWindowTracker: deps.createWindowTracker,
|
|
getResolvedConfig: () => deps.getResolvedConfig(),
|
|
getSubtitleTimingTracker: () => deps.appState.subtitleTimingTracker,
|
|
getMpvClient: () => deps.appState.mpvClient,
|
|
getMpvSocketPath: () => deps.appState.mpvSocketPath,
|
|
getRuntimeOptionsManager: () => deps.appState.runtimeOptionsManager,
|
|
setAnkiIntegration: (integration) => {
|
|
deps.appState.ankiIntegration = integration;
|
|
},
|
|
showDesktopNotification: deps.showDesktopNotification,
|
|
showOverlayNotification: deps.showOverlayNotification,
|
|
dismissOverlayNotification: deps.dismissOverlayNotification,
|
|
createFieldGroupingCallback: () => deps.createFieldGroupingCallback(),
|
|
getKnownWordCacheStatePath: () => deps.getKnownWordCacheStatePath(),
|
|
...(deps.getCachedMediaPath ? { getCachedMediaPath: deps.getCachedMediaPath } : {}),
|
|
...(deps.shouldRequireRemoteMediaCache
|
|
? { shouldRequireRemoteMediaCache: deps.shouldRequireRemoteMediaCache }
|
|
: {}),
|
|
...(deps.getYoutubeMediaSourceUrl
|
|
? { getYoutubeMediaSourceUrl: deps.getYoutubeMediaSourceUrl }
|
|
: {}),
|
|
shouldStartAnkiIntegration: () => deps.shouldStartAnkiIntegration(),
|
|
bindOverlayOwner: deps.bindOverlayOwner,
|
|
releaseOverlayOwner: deps.releaseOverlayOwner,
|
|
});
|
|
}
|