mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-28 06:22:45 -08:00
refactor: extract main runtime helper groups
- Extract remaining runtime helper clusters from main.ts into dedicated modules for readability:\n - src/main/jlpt-runtime.ts\n - src/main/media-runtime.ts\n - src/main/overlay-visibility-runtime.ts\n- Wire main.ts to use the new runtime services and remove duplicated in-file helpers.\n- Preserve existing behavior via full typecheck + test:fast verification.\n- Finalize and archive TASK-56 backlog entry; update TASK-54 with completion metadata and summary.
This commit is contained in:
77
src/main/jlpt-runtime.ts
Normal file
77
src/main/jlpt-runtime.ts
Normal file
@@ -0,0 +1,77 @@
|
||||
import * as path from "path";
|
||||
import type { JlptLevel } from "../types";
|
||||
|
||||
import { createJlptVocabularyLookupService } from "../core/services";
|
||||
|
||||
export interface JlptDictionarySearchPathDeps {
|
||||
getDictionaryRoots: () => string[];
|
||||
}
|
||||
|
||||
export type JlptLookup = (term: string) => JlptLevel | null;
|
||||
|
||||
export interface JlptDictionaryRuntimeDeps {
|
||||
isJlptEnabled: () => boolean;
|
||||
getSearchPaths: () => string[];
|
||||
setJlptLevelLookup: (lookup: JlptLookup) => void;
|
||||
log: (message: string) => void;
|
||||
}
|
||||
|
||||
let jlptDictionaryLookupInitialized = false;
|
||||
let jlptDictionaryLookupInitialization: Promise<void> | null = null;
|
||||
|
||||
export function getJlptDictionarySearchPaths(
|
||||
deps: JlptDictionarySearchPathDeps,
|
||||
): string[] {
|
||||
const dictionaryRoots = deps.getDictionaryRoots();
|
||||
|
||||
const searchPaths: string[] = [];
|
||||
for (const dictionaryRoot of dictionaryRoots) {
|
||||
searchPaths.push(dictionaryRoot);
|
||||
searchPaths.push(path.join(dictionaryRoot, "vendor", "yomitan-jlpt-vocab"));
|
||||
searchPaths.push(path.join(dictionaryRoot, "yomitan-jlpt-vocab"));
|
||||
}
|
||||
|
||||
const uniquePaths = new Set<string>(searchPaths);
|
||||
return [...uniquePaths];
|
||||
}
|
||||
|
||||
export async function initializeJlptDictionaryLookup(
|
||||
deps: JlptDictionaryRuntimeDeps,
|
||||
): Promise<void> {
|
||||
deps.setJlptLevelLookup(
|
||||
await createJlptVocabularyLookupService({
|
||||
searchPaths: deps.getSearchPaths(),
|
||||
log: deps.log,
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
export async function ensureJlptDictionaryLookup(
|
||||
deps: JlptDictionaryRuntimeDeps,
|
||||
): Promise<void> {
|
||||
if (!deps.isJlptEnabled()) {
|
||||
return;
|
||||
}
|
||||
if (jlptDictionaryLookupInitialized) {
|
||||
return;
|
||||
}
|
||||
if (!jlptDictionaryLookupInitialization) {
|
||||
jlptDictionaryLookupInitialization = initializeJlptDictionaryLookup(deps)
|
||||
.then(() => {
|
||||
jlptDictionaryLookupInitialized = true;
|
||||
})
|
||||
.catch((error) => {
|
||||
jlptDictionaryLookupInitialization = null;
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
await jlptDictionaryLookupInitialization;
|
||||
}
|
||||
|
||||
export function createJlptDictionaryRuntimeService(
|
||||
deps: JlptDictionaryRuntimeDeps,
|
||||
): { ensureJlptDictionaryLookup: () => Promise<void> } {
|
||||
return {
|
||||
ensureJlptDictionaryLookup: () => ensureJlptDictionaryLookup(deps),
|
||||
};
|
||||
}
|
||||
70
src/main/media-runtime.ts
Normal file
70
src/main/media-runtime.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
import { updateCurrentMediaPathService } from "../core/services";
|
||||
|
||||
import type { SubtitlePosition } from "../types";
|
||||
|
||||
export interface MediaRuntimeDeps {
|
||||
isRemoteMediaPath: (mediaPath: string) => boolean;
|
||||
loadSubtitlePosition: () => SubtitlePosition | null;
|
||||
getCurrentMediaPath: () => string | null;
|
||||
getPendingSubtitlePosition: () => SubtitlePosition | null;
|
||||
getSubtitlePositionsDir: () => string;
|
||||
setCurrentMediaPath: (mediaPath: string | null) => void;
|
||||
clearPendingSubtitlePosition: () => void;
|
||||
setSubtitlePosition: (position: SubtitlePosition | null) => void;
|
||||
broadcastSubtitlePosition: (position: SubtitlePosition | null) => void;
|
||||
getCurrentMediaTitle: () => string | null;
|
||||
setCurrentMediaTitle: (title: string | null) => void;
|
||||
}
|
||||
|
||||
export interface MediaRuntimeService {
|
||||
updateCurrentMediaPath: (mediaPath: unknown) => void;
|
||||
updateCurrentMediaTitle: (mediaTitle: unknown) => void;
|
||||
resolveMediaPathForJimaku: (mediaPath: string | null) => string | null;
|
||||
}
|
||||
|
||||
export function createMediaRuntimeService(
|
||||
deps: MediaRuntimeDeps,
|
||||
): MediaRuntimeService {
|
||||
return {
|
||||
updateCurrentMediaPath(mediaPath: unknown): void {
|
||||
if (typeof mediaPath !== "string" || !deps.isRemoteMediaPath(mediaPath)) {
|
||||
deps.setCurrentMediaTitle(null);
|
||||
}
|
||||
|
||||
updateCurrentMediaPathService({
|
||||
mediaPath,
|
||||
currentMediaPath: deps.getCurrentMediaPath(),
|
||||
pendingSubtitlePosition: deps.getPendingSubtitlePosition(),
|
||||
subtitlePositionsDir: deps.getSubtitlePositionsDir(),
|
||||
loadSubtitlePosition: () => deps.loadSubtitlePosition(),
|
||||
setCurrentMediaPath: (nextPath: string | null) => {
|
||||
deps.setCurrentMediaPath(nextPath);
|
||||
},
|
||||
clearPendingSubtitlePosition: () => {
|
||||
deps.clearPendingSubtitlePosition();
|
||||
},
|
||||
setSubtitlePosition: (position: SubtitlePosition | null) => {
|
||||
deps.setSubtitlePosition(position);
|
||||
},
|
||||
broadcastSubtitlePosition: (position: SubtitlePosition | null) => {
|
||||
deps.broadcastSubtitlePosition(position);
|
||||
},
|
||||
});
|
||||
},
|
||||
|
||||
updateCurrentMediaTitle(mediaTitle: unknown): void {
|
||||
if (typeof mediaTitle === "string") {
|
||||
const sanitized = mediaTitle.trim();
|
||||
deps.setCurrentMediaTitle(sanitized.length > 0 ? sanitized : null);
|
||||
return;
|
||||
}
|
||||
deps.setCurrentMediaTitle(null);
|
||||
},
|
||||
|
||||
resolveMediaPathForJimaku(mediaPath: string | null): string | null {
|
||||
return mediaPath && deps.isRemoteMediaPath(mediaPath) && deps.getCurrentMediaTitle()
|
||||
? deps.getCurrentMediaTitle()
|
||||
: mediaPath;
|
||||
},
|
||||
};
|
||||
}
|
||||
92
src/main/overlay-visibility-runtime.ts
Normal file
92
src/main/overlay-visibility-runtime.ts
Normal file
@@ -0,0 +1,92 @@
|
||||
import type { BrowserWindow } from "electron";
|
||||
|
||||
import type { BaseWindowTracker } from "../window-trackers";
|
||||
import type { WindowGeometry } from "../types";
|
||||
import {
|
||||
syncInvisibleOverlayMousePassthroughService,
|
||||
updateInvisibleOverlayVisibilityService,
|
||||
updateVisibleOverlayVisibilityService,
|
||||
} from "../core/services";
|
||||
|
||||
export interface OverlayVisibilityRuntimeDeps {
|
||||
getMainWindow: () => BrowserWindow | null;
|
||||
getInvisibleWindow: () => BrowserWindow | null;
|
||||
getVisibleOverlayVisible: () => boolean;
|
||||
getInvisibleOverlayVisible: () => boolean;
|
||||
getWindowTracker: () => BaseWindowTracker | null;
|
||||
getTrackerNotReadyWarningShown: () => boolean;
|
||||
setTrackerNotReadyWarningShown: (shown: boolean) => void;
|
||||
updateVisibleOverlayBounds: (geometry: WindowGeometry) => void;
|
||||
updateInvisibleOverlayBounds: (geometry: WindowGeometry) => void;
|
||||
ensureOverlayWindowLevel: (window: BrowserWindow) => void;
|
||||
enforceOverlayLayerOrder: () => void;
|
||||
syncOverlayShortcuts: () => void;
|
||||
}
|
||||
|
||||
export interface OverlayVisibilityRuntimeService {
|
||||
updateVisibleOverlayVisibility: () => void;
|
||||
updateInvisibleOverlayVisibility: () => void;
|
||||
syncInvisibleOverlayMousePassthrough: () => void;
|
||||
}
|
||||
|
||||
export function createOverlayVisibilityRuntimeService(
|
||||
deps: OverlayVisibilityRuntimeDeps,
|
||||
): OverlayVisibilityRuntimeService {
|
||||
const hasInvisibleWindow = (): boolean => {
|
||||
const invisibleWindow = deps.getInvisibleWindow();
|
||||
return Boolean(invisibleWindow && !invisibleWindow.isDestroyed());
|
||||
};
|
||||
|
||||
const setIgnoreMouseEvents = (
|
||||
ignore: boolean,
|
||||
options?: Parameters<BrowserWindow["setIgnoreMouseEvents"]>[1],
|
||||
): void => {
|
||||
const invisibleWindow = deps.getInvisibleWindow();
|
||||
if (!invisibleWindow || invisibleWindow.isDestroyed()) return;
|
||||
invisibleWindow.setIgnoreMouseEvents(ignore, options);
|
||||
};
|
||||
|
||||
return {
|
||||
updateVisibleOverlayVisibility(): void {
|
||||
updateVisibleOverlayVisibilityService({
|
||||
visibleOverlayVisible: deps.getVisibleOverlayVisible(),
|
||||
mainWindow: deps.getMainWindow(),
|
||||
windowTracker: deps.getWindowTracker(),
|
||||
trackerNotReadyWarningShown: deps.getTrackerNotReadyWarningShown(),
|
||||
setTrackerNotReadyWarningShown: (shown: boolean) => {
|
||||
deps.setTrackerNotReadyWarningShown(shown);
|
||||
},
|
||||
updateVisibleOverlayBounds: (geometry: WindowGeometry) =>
|
||||
deps.updateVisibleOverlayBounds(geometry),
|
||||
ensureOverlayWindowLevel: (window: BrowserWindow) =>
|
||||
deps.ensureOverlayWindowLevel(window),
|
||||
enforceOverlayLayerOrder: () => deps.enforceOverlayLayerOrder(),
|
||||
syncOverlayShortcuts: () => deps.syncOverlayShortcuts(),
|
||||
});
|
||||
},
|
||||
|
||||
updateInvisibleOverlayVisibility(): void {
|
||||
updateInvisibleOverlayVisibilityService({
|
||||
invisibleWindow: deps.getInvisibleWindow(),
|
||||
visibleOverlayVisible: deps.getVisibleOverlayVisible(),
|
||||
invisibleOverlayVisible: deps.getInvisibleOverlayVisible(),
|
||||
windowTracker: deps.getWindowTracker(),
|
||||
updateInvisibleOverlayBounds: (geometry: WindowGeometry) =>
|
||||
deps.updateInvisibleOverlayBounds(geometry),
|
||||
ensureOverlayWindowLevel: (window: BrowserWindow) =>
|
||||
deps.ensureOverlayWindowLevel(window),
|
||||
enforceOverlayLayerOrder: () => deps.enforceOverlayLayerOrder(),
|
||||
syncOverlayShortcuts: () => deps.syncOverlayShortcuts(),
|
||||
});
|
||||
},
|
||||
|
||||
syncInvisibleOverlayMousePassthrough(): void {
|
||||
syncInvisibleOverlayMousePassthroughService({
|
||||
hasInvisibleWindow,
|
||||
setIgnoreMouseEvents,
|
||||
visibleOverlayVisible: deps.getVisibleOverlayVisible(),
|
||||
invisibleOverlayVisible: deps.getInvisibleOverlayVisible(),
|
||||
});
|
||||
},
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user