mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-27 06:12:05 -07:00
refactor: compose startup and setup window wiring
This commit is contained in:
@@ -83,7 +83,6 @@ This project uses Backlog.md MCP for all task and project management activities.
|
|||||||
- **When to read it**: BEFORE creating tasks, or when you're unsure whether to track work
|
- **When to read it**: BEFORE creating tasks, or when you're unsure whether to track work
|
||||||
|
|
||||||
These guides cover:
|
These guides cover:
|
||||||
|
|
||||||
- Decision framework for when to create tasks
|
- Decision framework for when to create tasks
|
||||||
- Search-first workflow to avoid duplicates
|
- Search-first workflow to avoid duplicates
|
||||||
- Links to detailed guides for task creation, execution, and finalization
|
- Links to detailed guides for task creation, execution, and finalization
|
||||||
|
|||||||
266
src/main.ts
266
src/main.ts
@@ -170,7 +170,6 @@ import {
|
|||||||
createBuildEnforceOverlayLayerOrderMainDepsHandler,
|
createBuildEnforceOverlayLayerOrderMainDepsHandler,
|
||||||
createBuildEnsureOverlayWindowLevelMainDepsHandler,
|
createBuildEnsureOverlayWindowLevelMainDepsHandler,
|
||||||
createBuildUpdateVisibleOverlayBoundsMainDepsHandler,
|
createBuildUpdateVisibleOverlayBoundsMainDepsHandler,
|
||||||
createOverlayWindowRuntimeHandlers,
|
|
||||||
createTrayRuntimeHandlers,
|
createTrayRuntimeHandlers,
|
||||||
createOverlayVisibilityRuntime,
|
createOverlayVisibilityRuntime,
|
||||||
createBroadcastRuntimeOptionsChangedHandler,
|
createBroadcastRuntimeOptionsChangedHandler,
|
||||||
@@ -235,11 +234,6 @@ import {
|
|||||||
createHandleMineSentenceDigitHandler,
|
createHandleMineSentenceDigitHandler,
|
||||||
createHandleMultiCopyDigitHandler,
|
createHandleMultiCopyDigitHandler,
|
||||||
} from './main/runtime/domains/mining';
|
} from './main/runtime/domains/mining';
|
||||||
import {
|
|
||||||
createCliCommandContextFactory,
|
|
||||||
createInitialArgsRuntimeHandler,
|
|
||||||
createCliCommandRuntimeHandler,
|
|
||||||
} from './main/runtime/domains/ipc';
|
|
||||||
import {
|
import {
|
||||||
enforceUnsupportedWaylandMode,
|
enforceUnsupportedWaylandMode,
|
||||||
forceX11Backend,
|
forceX11Backend,
|
||||||
@@ -384,9 +378,12 @@ import {
|
|||||||
composeAnilistSetupHandlers,
|
composeAnilistSetupHandlers,
|
||||||
composeAnilistTrackingHandlers,
|
composeAnilistTrackingHandlers,
|
||||||
composeAppReadyRuntime,
|
composeAppReadyRuntime,
|
||||||
|
composeCliStartupHandlers,
|
||||||
|
composeHeadlessStartupHandlers,
|
||||||
composeIpcRuntimeHandlers,
|
composeIpcRuntimeHandlers,
|
||||||
composeJellyfinRuntimeHandlers,
|
composeJellyfinRuntimeHandlers,
|
||||||
composeMpvRuntimeHandlers,
|
composeMpvRuntimeHandlers,
|
||||||
|
composeOverlayWindowHandlers,
|
||||||
composeShortcutRuntimes,
|
composeShortcutRuntimes,
|
||||||
composeStartupLifecycleHandlers,
|
composeStartupLifecycleHandlers,
|
||||||
} from './main/runtime/composers';
|
} from './main/runtime/composers';
|
||||||
@@ -421,6 +418,11 @@ import { handleCharacterDictionaryAutoSyncComplete } from './main/runtime/charac
|
|||||||
import { notifyCharacterDictionaryAutoSyncStatus } from './main/runtime/character-dictionary-auto-sync-notifications';
|
import { notifyCharacterDictionaryAutoSyncStatus } from './main/runtime/character-dictionary-auto-sync-notifications';
|
||||||
import { createCurrentMediaTokenizationGate } from './main/runtime/current-media-tokenization-gate';
|
import { createCurrentMediaTokenizationGate } from './main/runtime/current-media-tokenization-gate';
|
||||||
import { createStartupOsdSequencer } from './main/runtime/startup-osd-sequencer';
|
import { createStartupOsdSequencer } from './main/runtime/startup-osd-sequencer';
|
||||||
|
import {
|
||||||
|
createCreateAnilistSetupWindowHandler,
|
||||||
|
createCreateFirstRunSetupWindowHandler,
|
||||||
|
createCreateJellyfinSetupWindowHandler,
|
||||||
|
} from './main/runtime/setup-window-factory';
|
||||||
import { isYoutubePlaybackActive } from './main/runtime/youtube-playback';
|
import { isYoutubePlaybackActive } from './main/runtime/youtube-playback';
|
||||||
import { createYomitanProfilePolicy } from './main/runtime/yomitan-profile-policy';
|
import { createYomitanProfilePolicy } from './main/runtime/yomitan-profile-policy';
|
||||||
import { formatSkippedYomitanWriteAction } from './main/runtime/yomitan-read-only-log';
|
import { formatSkippedYomitanWriteAction } from './main/runtime/yomitan-read-only-log';
|
||||||
@@ -2360,17 +2362,8 @@ const {
|
|||||||
getSetupWindow: () => appState.jellyfinSetupWindow,
|
getSetupWindow: () => appState.jellyfinSetupWindow,
|
||||||
},
|
},
|
||||||
openJellyfinSetupWindowMainDeps: {
|
openJellyfinSetupWindowMainDeps: {
|
||||||
createSetupWindow: () =>
|
createSetupWindow: createCreateJellyfinSetupWindowHandler({
|
||||||
new BrowserWindow({
|
createBrowserWindow: (options) => new BrowserWindow(options),
|
||||||
width: 520,
|
|
||||||
height: 560,
|
|
||||||
title: 'Jellyfin Setup',
|
|
||||||
show: true,
|
|
||||||
autoHideMenuBar: true,
|
|
||||||
webPreferences: {
|
|
||||||
nodeIntegration: false,
|
|
||||||
contextIsolation: true,
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
buildSetupFormHtml: (defaultServer, defaultUser) =>
|
buildSetupFormHtml: (defaultServer, defaultUser) =>
|
||||||
buildJellyfinSetupFormHtml(defaultServer, defaultUser),
|
buildJellyfinSetupFormHtml(defaultServer, defaultUser),
|
||||||
@@ -2405,20 +2398,8 @@ const maybeFocusExistingFirstRunSetupWindow = createMaybeFocusExistingFirstRunSe
|
|||||||
});
|
});
|
||||||
const openFirstRunSetupWindowHandler = createOpenFirstRunSetupWindowHandler({
|
const openFirstRunSetupWindowHandler = createOpenFirstRunSetupWindowHandler({
|
||||||
maybeFocusExistingSetupWindow: maybeFocusExistingFirstRunSetupWindow,
|
maybeFocusExistingSetupWindow: maybeFocusExistingFirstRunSetupWindow,
|
||||||
createSetupWindow: () =>
|
createSetupWindow: createCreateFirstRunSetupWindowHandler({
|
||||||
new BrowserWindow({
|
createBrowserWindow: (options) => new BrowserWindow(options),
|
||||||
width: 480,
|
|
||||||
height: 460,
|
|
||||||
title: 'SubMiner Setup',
|
|
||||||
show: true,
|
|
||||||
autoHideMenuBar: true,
|
|
||||||
resizable: false,
|
|
||||||
minimizable: false,
|
|
||||||
maximizable: false,
|
|
||||||
webPreferences: {
|
|
||||||
nodeIntegration: false,
|
|
||||||
contextIsolation: true,
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
getSetupSnapshot: async () => {
|
getSetupSnapshot: async () => {
|
||||||
const snapshot = await firstRunSetupService.getSetupStatus();
|
const snapshot = await firstRunSetupService.getSetupStatus();
|
||||||
@@ -2564,17 +2545,8 @@ const maybeFocusExistingAnilistSetupWindow = createMaybeFocusExistingAnilistSetu
|
|||||||
const buildOpenAnilistSetupWindowMainDepsHandler = createBuildOpenAnilistSetupWindowMainDepsHandler(
|
const buildOpenAnilistSetupWindowMainDepsHandler = createBuildOpenAnilistSetupWindowMainDepsHandler(
|
||||||
{
|
{
|
||||||
maybeFocusExistingSetupWindow: maybeFocusExistingAnilistSetupWindow,
|
maybeFocusExistingSetupWindow: maybeFocusExistingAnilistSetupWindow,
|
||||||
createSetupWindow: () =>
|
createSetupWindow: createCreateAnilistSetupWindowHandler({
|
||||||
new BrowserWindow({
|
createBrowserWindow: (options) => new BrowserWindow(options),
|
||||||
width: 1000,
|
|
||||||
height: 760,
|
|
||||||
title: 'Anilist Setup',
|
|
||||||
show: true,
|
|
||||||
autoHideMenuBar: true,
|
|
||||||
webPreferences: {
|
|
||||||
nodeIntegration: false,
|
|
||||||
contextIsolation: true,
|
|
||||||
},
|
|
||||||
}),
|
}),
|
||||||
buildAuthorizeUrl: () =>
|
buildAuthorizeUrl: () =>
|
||||||
buildAnilistSetupUrl({
|
buildAnilistSetupUrl({
|
||||||
@@ -3464,88 +3436,6 @@ const { appReadyRuntimeRunner } = composeAppReadyRuntime({
|
|||||||
immersionTrackerStartupMainDeps,
|
immersionTrackerStartupMainDeps,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { runAndApplyStartupState } = runtimeRegistry.startup.createStartupRuntimeHandlers<
|
|
||||||
CliArgs,
|
|
||||||
StartupState,
|
|
||||||
ReturnType<typeof createStartupBootstrapRuntimeDeps>
|
|
||||||
>({
|
|
||||||
appLifecycleRuntimeRunnerMainDeps: {
|
|
||||||
app: appLifecycleApp,
|
|
||||||
platform: process.platform,
|
|
||||||
shouldStartApp: (nextArgs: CliArgs) => shouldStartApp(nextArgs),
|
|
||||||
parseArgs: (argv: string[]) => parseArgs(argv),
|
|
||||||
handleCliCommand: (nextArgs: CliArgs, source: CliCommandSource) =>
|
|
||||||
handleCliCommand(nextArgs, source),
|
|
||||||
printHelp: () => printHelp(DEFAULT_TEXTHOOKER_PORT),
|
|
||||||
logNoRunningInstance: () => appLogger.logNoRunningInstance(),
|
|
||||||
onReady: appReadyRuntimeRunner,
|
|
||||||
onWillQuitCleanup: () => onWillQuitCleanupHandler(),
|
|
||||||
shouldRestoreWindowsOnActivate: () => shouldRestoreWindowsOnActivateHandler(),
|
|
||||||
restoreWindowsOnActivate: () => restoreWindowsOnActivateHandler(),
|
|
||||||
shouldQuitOnWindowAllClosed: () => !appState.backgroundMode,
|
|
||||||
},
|
|
||||||
createAppLifecycleRuntimeRunner: (params) => createAppLifecycleRuntimeRunner(params),
|
|
||||||
buildStartupBootstrapMainDeps: (startAppLifecycle) => ({
|
|
||||||
argv: process.argv,
|
|
||||||
parseArgs: (argv: string[]) => parseArgs(argv),
|
|
||||||
setLogLevel: (level: string, source: LogLevelSource) => {
|
|
||||||
setLogLevel(level, source);
|
|
||||||
},
|
|
||||||
forceX11Backend: (args: CliArgs) => {
|
|
||||||
forceX11Backend(args);
|
|
||||||
},
|
|
||||||
enforceUnsupportedWaylandMode: (args: CliArgs) => {
|
|
||||||
enforceUnsupportedWaylandMode(args);
|
|
||||||
},
|
|
||||||
shouldStartApp: (args: CliArgs) => shouldStartApp(args),
|
|
||||||
getDefaultSocketPath: () => getDefaultSocketPath(),
|
|
||||||
defaultTexthookerPort: DEFAULT_TEXTHOOKER_PORT,
|
|
||||||
configDir: CONFIG_DIR,
|
|
||||||
defaultConfig: DEFAULT_CONFIG,
|
|
||||||
generateConfigTemplate: (config: ResolvedConfig) => generateConfigTemplate(config),
|
|
||||||
generateDefaultConfigFile: (
|
|
||||||
args: CliArgs,
|
|
||||||
options: {
|
|
||||||
configDir: string;
|
|
||||||
defaultConfig: unknown;
|
|
||||||
generateTemplate: (config: unknown) => string;
|
|
||||||
},
|
|
||||||
) => generateDefaultConfigFile(args, options),
|
|
||||||
setExitCode: (code) => {
|
|
||||||
process.exitCode = code;
|
|
||||||
},
|
|
||||||
quitApp: () => requestAppQuit(),
|
|
||||||
logGenerateConfigError: (message) => logger.error(message),
|
|
||||||
startAppLifecycle,
|
|
||||||
}),
|
|
||||||
createStartupBootstrapRuntimeDeps: (deps) => createStartupBootstrapRuntimeDeps(deps),
|
|
||||||
runStartupBootstrapRuntime,
|
|
||||||
applyStartupState: (startupState) => applyStartupState(appState, startupState),
|
|
||||||
});
|
|
||||||
|
|
||||||
runAndApplyStartupState();
|
|
||||||
if (isAnilistTrackingEnabled(getResolvedConfig())) {
|
|
||||||
void refreshAnilistClientSecretStateIfEnabled({ force: true });
|
|
||||||
anilistStateRuntime.refreshRetryQueueState();
|
|
||||||
}
|
|
||||||
void initializeDiscordPresenceService();
|
|
||||||
|
|
||||||
const handleCliCommand = createCliCommandRuntimeHandler({
|
|
||||||
handleTexthookerOnlyModeTransitionMainDeps: {
|
|
||||||
isTexthookerOnlyMode: () => appState.texthookerOnlyMode,
|
|
||||||
ensureOverlayStartupPrereqs: () => ensureOverlayStartupPrereqs(),
|
|
||||||
setTexthookerOnlyMode: (enabled) => {
|
|
||||||
appState.texthookerOnlyMode = enabled;
|
|
||||||
},
|
|
||||||
commandNeedsOverlayStartupPrereqs: (inputArgs) => commandNeedsOverlayStartupPrereqs(inputArgs),
|
|
||||||
startBackgroundWarmups: () => startBackgroundWarmups(),
|
|
||||||
logInfo: (message: string) => logger.info(message),
|
|
||||||
},
|
|
||||||
createCliCommandContext: () => createCliCommandContextHandler(),
|
|
||||||
handleCliCommandRuntimeServiceWithContext: (args, source, cliContext) =>
|
|
||||||
handleCliCommandRuntimeServiceWithContext(args, source, cliContext),
|
|
||||||
});
|
|
||||||
|
|
||||||
function ensureOverlayStartupPrereqs(): void {
|
function ensureOverlayStartupPrereqs(): void {
|
||||||
if (appState.subtitlePosition === null) {
|
if (appState.subtitlePosition === null) {
|
||||||
loadSubtitlePosition();
|
loadSubtitlePosition();
|
||||||
@@ -3590,29 +3480,6 @@ async function ensureYoutubePlaybackRuntimeReady(): Promise<void> {
|
|||||||
ensureOverlayWindowsReadyForVisibilityActions();
|
ensureOverlayWindowsReadyForVisibilityActions();
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleInitialArgsRuntimeHandler = createInitialArgsRuntimeHandler({
|
|
||||||
getInitialArgs: () => appState.initialArgs,
|
|
||||||
isBackgroundMode: () => appState.backgroundMode,
|
|
||||||
shouldEnsureTrayOnStartup: () =>
|
|
||||||
shouldEnsureTrayOnStartupForInitialArgs(process.platform, appState.initialArgs),
|
|
||||||
shouldRunHeadlessInitialCommand: (args) => isHeadlessInitialCommand(args),
|
|
||||||
ensureTray: () => ensureTray(),
|
|
||||||
isTexthookerOnlyMode: () => appState.texthookerOnlyMode,
|
|
||||||
hasImmersionTracker: () => Boolean(appState.immersionTracker),
|
|
||||||
getMpvClient: () => appState.mpvClient,
|
|
||||||
commandNeedsOverlayStartupPrereqs: (args) => commandNeedsOverlayStartupPrereqs(args),
|
|
||||||
commandNeedsOverlayRuntime: (args) => commandNeedsOverlayRuntime(args),
|
|
||||||
ensureOverlayStartupPrereqs: () => ensureOverlayStartupPrereqs(),
|
|
||||||
isOverlayRuntimeInitialized: () => appState.overlayRuntimeInitialized,
|
|
||||||
initializeOverlayRuntime: () => initializeOverlayRuntime(),
|
|
||||||
logInfo: (message) => logger.info(message),
|
|
||||||
handleCliCommand: (args, source) => handleCliCommand(args, source),
|
|
||||||
});
|
|
||||||
|
|
||||||
function handleInitialArgs(): void {
|
|
||||||
handleInitialArgsRuntimeHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
const {
|
const {
|
||||||
createMpvClientRuntimeService: createMpvClientRuntimeServiceHandler,
|
createMpvClientRuntimeService: createMpvClientRuntimeServiceHandler,
|
||||||
updateMpvSubtitleRenderMetrics: updateMpvSubtitleRenderMetricsHandler,
|
updateMpvSubtitleRenderMetrics: updateMpvSubtitleRenderMetricsHandler,
|
||||||
@@ -4763,7 +4630,8 @@ const { registerIpcRuntimeHandlers } = composeIpcRuntimeHandlers({
|
|||||||
registerIpcRuntimeServices,
|
registerIpcRuntimeServices,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const createCliCommandContextHandler = createCliCommandContextFactory({
|
const { handleCliCommand, handleInitialArgs } = composeCliStartupHandlers({
|
||||||
|
cliCommandContextMainDeps: {
|
||||||
appState,
|
appState,
|
||||||
setLogLevel: (level) => setLogLevel(level, 'cli'),
|
setLogLevel: (level) => setLogLevel(level, 'cli'),
|
||||||
texthookerService,
|
texthookerService,
|
||||||
@@ -4814,9 +4682,109 @@ const createCliCommandContextHandler = createCliCommandContextFactory({
|
|||||||
logInfo: (message: string) => logger.info(message),
|
logInfo: (message: string) => logger.info(message),
|
||||||
logWarn: (message: string) => logger.warn(message),
|
logWarn: (message: string) => logger.warn(message),
|
||||||
logError: (message: string, err: unknown) => logger.error(message, err),
|
logError: (message: string, err: unknown) => logger.error(message, err),
|
||||||
|
},
|
||||||
|
cliCommandRuntimeHandlerMainDeps: {
|
||||||
|
handleTexthookerOnlyModeTransitionMainDeps: {
|
||||||
|
isTexthookerOnlyMode: () => appState.texthookerOnlyMode,
|
||||||
|
ensureOverlayStartupPrereqs: () => ensureOverlayStartupPrereqs(),
|
||||||
|
setTexthookerOnlyMode: (enabled) => {
|
||||||
|
appState.texthookerOnlyMode = enabled;
|
||||||
|
},
|
||||||
|
commandNeedsOverlayStartupPrereqs: (inputArgs) =>
|
||||||
|
commandNeedsOverlayStartupPrereqs(inputArgs),
|
||||||
|
startBackgroundWarmups: () => startBackgroundWarmups(),
|
||||||
|
logInfo: (message: string) => logger.info(message),
|
||||||
|
},
|
||||||
|
handleCliCommandRuntimeServiceWithContext: (args, source, cliContext) =>
|
||||||
|
handleCliCommandRuntimeServiceWithContext(args, source, cliContext),
|
||||||
|
},
|
||||||
|
initialArgsRuntimeHandlerMainDeps: {
|
||||||
|
getInitialArgs: () => appState.initialArgs,
|
||||||
|
isBackgroundMode: () => appState.backgroundMode,
|
||||||
|
shouldEnsureTrayOnStartup: () =>
|
||||||
|
shouldEnsureTrayOnStartupForInitialArgs(process.platform, appState.initialArgs),
|
||||||
|
shouldRunHeadlessInitialCommand: (args) => isHeadlessInitialCommand(args),
|
||||||
|
ensureTray: () => ensureTray(),
|
||||||
|
isTexthookerOnlyMode: () => appState.texthookerOnlyMode,
|
||||||
|
hasImmersionTracker: () => Boolean(appState.immersionTracker),
|
||||||
|
getMpvClient: () => appState.mpvClient,
|
||||||
|
commandNeedsOverlayStartupPrereqs: (args) => commandNeedsOverlayStartupPrereqs(args),
|
||||||
|
commandNeedsOverlayRuntime: (args) => commandNeedsOverlayRuntime(args),
|
||||||
|
ensureOverlayStartupPrereqs: () => ensureOverlayStartupPrereqs(),
|
||||||
|
isOverlayRuntimeInitialized: () => appState.overlayRuntimeInitialized,
|
||||||
|
initializeOverlayRuntime: () => initializeOverlayRuntime(),
|
||||||
|
logInfo: (message) => logger.info(message),
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
const { runAndApplyStartupState } = composeHeadlessStartupHandlers<
|
||||||
|
CliArgs,
|
||||||
|
StartupState,
|
||||||
|
ReturnType<typeof createStartupBootstrapRuntimeDeps>
|
||||||
|
>({
|
||||||
|
startupRuntimeHandlersDeps: {
|
||||||
|
appLifecycleRuntimeRunnerMainDeps: {
|
||||||
|
app: appLifecycleApp,
|
||||||
|
platform: process.platform,
|
||||||
|
shouldStartApp: (nextArgs: CliArgs) => shouldStartApp(nextArgs),
|
||||||
|
parseArgs: (argv: string[]) => parseArgs(argv),
|
||||||
|
handleCliCommand: (nextArgs: CliArgs, source: CliCommandSource) =>
|
||||||
|
handleCliCommand(nextArgs, source),
|
||||||
|
printHelp: () => printHelp(DEFAULT_TEXTHOOKER_PORT),
|
||||||
|
logNoRunningInstance: () => appLogger.logNoRunningInstance(),
|
||||||
|
onReady: appReadyRuntimeRunner,
|
||||||
|
onWillQuitCleanup: () => onWillQuitCleanupHandler(),
|
||||||
|
shouldRestoreWindowsOnActivate: () => shouldRestoreWindowsOnActivateHandler(),
|
||||||
|
restoreWindowsOnActivate: () => restoreWindowsOnActivateHandler(),
|
||||||
|
shouldQuitOnWindowAllClosed: () => !appState.backgroundMode,
|
||||||
|
},
|
||||||
|
createAppLifecycleRuntimeRunner: (params) => createAppLifecycleRuntimeRunner(params),
|
||||||
|
buildStartupBootstrapMainDeps: (startAppLifecycle) => ({
|
||||||
|
argv: process.argv,
|
||||||
|
parseArgs: (argv: string[]) => parseArgs(argv),
|
||||||
|
setLogLevel: (level: string, source: LogLevelSource) => {
|
||||||
|
setLogLevel(level, source);
|
||||||
|
},
|
||||||
|
forceX11Backend: (args: CliArgs) => {
|
||||||
|
forceX11Backend(args);
|
||||||
|
},
|
||||||
|
enforceUnsupportedWaylandMode: (args: CliArgs) => {
|
||||||
|
enforceUnsupportedWaylandMode(args);
|
||||||
|
},
|
||||||
|
shouldStartApp: (args: CliArgs) => shouldStartApp(args),
|
||||||
|
getDefaultSocketPath: () => getDefaultSocketPath(),
|
||||||
|
defaultTexthookerPort: DEFAULT_TEXTHOOKER_PORT,
|
||||||
|
configDir: CONFIG_DIR,
|
||||||
|
defaultConfig: DEFAULT_CONFIG,
|
||||||
|
generateConfigTemplate: (config: ResolvedConfig) => generateConfigTemplate(config),
|
||||||
|
generateDefaultConfigFile: (
|
||||||
|
args: CliArgs,
|
||||||
|
options: {
|
||||||
|
configDir: string;
|
||||||
|
defaultConfig: unknown;
|
||||||
|
generateTemplate: (config: unknown) => string;
|
||||||
|
},
|
||||||
|
) => generateDefaultConfigFile(args, options),
|
||||||
|
setExitCode: (code) => {
|
||||||
|
process.exitCode = code;
|
||||||
|
},
|
||||||
|
quitApp: () => requestAppQuit(),
|
||||||
|
logGenerateConfigError: (message) => logger.error(message),
|
||||||
|
startAppLifecycle,
|
||||||
|
}),
|
||||||
|
createStartupBootstrapRuntimeDeps: (deps) => createStartupBootstrapRuntimeDeps(deps),
|
||||||
|
runStartupBootstrapRuntime,
|
||||||
|
applyStartupState: (startupState) => applyStartupState(appState, startupState),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
runAndApplyStartupState();
|
||||||
|
if (isAnilistTrackingEnabled(getResolvedConfig())) {
|
||||||
|
void refreshAnilistClientSecretStateIfEnabled({ force: true });
|
||||||
|
anilistStateRuntime.refreshRetryQueueState();
|
||||||
|
}
|
||||||
|
void initializeDiscordPresenceService();
|
||||||
const { createMainWindow: createMainWindowHandler, createModalWindow: createModalWindowHandler } =
|
const { createMainWindow: createMainWindowHandler, createModalWindow: createModalWindowHandler } =
|
||||||
createOverlayWindowRuntimeHandlers<BrowserWindow>({
|
composeOverlayWindowHandlers<BrowserWindow>({
|
||||||
createOverlayWindowDeps: {
|
createOverlayWindowDeps: {
|
||||||
createOverlayWindowCore: (kind, options) => createOverlayWindowCore(kind, options),
|
createOverlayWindowCore: (kind, options) => createOverlayWindowCore(kind, options),
|
||||||
isDev,
|
isDev,
|
||||||
|
|||||||
83
src/main/runtime/composers/cli-startup-composer.test.ts
Normal file
83
src/main/runtime/composers/cli-startup-composer.test.ts
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import test from 'node:test';
|
||||||
|
import { composeCliStartupHandlers } from './cli-startup-composer';
|
||||||
|
|
||||||
|
test('composeCliStartupHandlers returns callable CLI startup handlers', () => {
|
||||||
|
const handlers = composeCliStartupHandlers({
|
||||||
|
cliCommandContextMainDeps: {
|
||||||
|
appState: {} as never,
|
||||||
|
setLogLevel: () => {},
|
||||||
|
texthookerService: {} as never,
|
||||||
|
getResolvedConfig: () => ({}) as never,
|
||||||
|
openExternal: async () => {},
|
||||||
|
logBrowserOpenError: () => {},
|
||||||
|
showMpvOsd: () => {},
|
||||||
|
initializeOverlayRuntime: () => {},
|
||||||
|
toggleVisibleOverlay: () => {},
|
||||||
|
openFirstRunSetupWindow: () => {},
|
||||||
|
setVisibleOverlayVisible: () => {},
|
||||||
|
copyCurrentSubtitle: () => {},
|
||||||
|
startPendingMultiCopy: () => {},
|
||||||
|
mineSentenceCard: async () => {},
|
||||||
|
startPendingMineSentenceMultiple: () => {},
|
||||||
|
updateLastCardFromClipboard: async () => {},
|
||||||
|
refreshKnownWordCache: async () => {},
|
||||||
|
triggerFieldGrouping: async () => {},
|
||||||
|
triggerSubsyncFromConfig: async () => {},
|
||||||
|
markLastCardAsAudioCard: async () => {},
|
||||||
|
getAnilistStatus: () => ({}) as never,
|
||||||
|
clearAnilistToken: () => {},
|
||||||
|
openAnilistSetupWindow: () => {},
|
||||||
|
openJellyfinSetupWindow: () => {},
|
||||||
|
getAnilistQueueStatus: () => ({}) as never,
|
||||||
|
processNextAnilistRetryUpdate: async () => ({ ok: true, message: 'done' }),
|
||||||
|
generateCharacterDictionary: async () =>
|
||||||
|
({ zipPath: '/tmp/test.zip', fromCache: false, mediaId: 1, mediaTitle: 'Test', entryCount: 1 }),
|
||||||
|
runJellyfinCommand: async () => {},
|
||||||
|
runStatsCommand: async () => {},
|
||||||
|
runYoutubePlaybackFlow: async () => {},
|
||||||
|
openYomitanSettings: () => {},
|
||||||
|
cycleSecondarySubMode: () => {},
|
||||||
|
openRuntimeOptionsPalette: () => {},
|
||||||
|
printHelp: () => {},
|
||||||
|
stopApp: () => {},
|
||||||
|
hasMainWindow: () => false,
|
||||||
|
getMultiCopyTimeoutMs: () => 0,
|
||||||
|
schedule: () => 0 as never,
|
||||||
|
logInfo: () => {},
|
||||||
|
logWarn: () => {},
|
||||||
|
logError: () => {},
|
||||||
|
},
|
||||||
|
cliCommandRuntimeHandlerMainDeps: {
|
||||||
|
handleTexthookerOnlyModeTransitionMainDeps: {
|
||||||
|
isTexthookerOnlyMode: () => false,
|
||||||
|
ensureOverlayStartupPrereqs: () => {},
|
||||||
|
setTexthookerOnlyMode: () => {},
|
||||||
|
commandNeedsOverlayStartupPrereqs: () => false,
|
||||||
|
startBackgroundWarmups: () => {},
|
||||||
|
logInfo: () => {},
|
||||||
|
},
|
||||||
|
handleCliCommandRuntimeServiceWithContext: () => {},
|
||||||
|
},
|
||||||
|
initialArgsRuntimeHandlerMainDeps: {
|
||||||
|
getInitialArgs: () => null,
|
||||||
|
isBackgroundMode: () => false,
|
||||||
|
shouldEnsureTrayOnStartup: () => false,
|
||||||
|
shouldRunHeadlessInitialCommand: () => false,
|
||||||
|
ensureTray: () => {},
|
||||||
|
isTexthookerOnlyMode: () => false,
|
||||||
|
hasImmersionTracker: () => false,
|
||||||
|
getMpvClient: () => null,
|
||||||
|
commandNeedsOverlayStartupPrereqs: () => false,
|
||||||
|
commandNeedsOverlayRuntime: () => false,
|
||||||
|
ensureOverlayStartupPrereqs: () => {},
|
||||||
|
isOverlayRuntimeInitialized: () => false,
|
||||||
|
initializeOverlayRuntime: () => {},
|
||||||
|
logInfo: () => {},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(typeof handlers.createCliCommandContext, 'function');
|
||||||
|
assert.equal(typeof handlers.handleCliCommand, 'function');
|
||||||
|
assert.equal(typeof handlers.handleInitialArgs, 'function');
|
||||||
|
});
|
||||||
50
src/main/runtime/composers/cli-startup-composer.ts
Normal file
50
src/main/runtime/composers/cli-startup-composer.ts
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
import type { CliArgs, CliCommandSource } from '../../../cli/args';
|
||||||
|
import { createCliCommandContextFactory } from '../cli-command-context-factory';
|
||||||
|
import { createCliCommandRuntimeHandler } from '../cli-command-runtime-handler';
|
||||||
|
import { createInitialArgsRuntimeHandler } from '../initial-args-runtime-handler';
|
||||||
|
import type { ComposerInputs, ComposerOutputs } from './contracts';
|
||||||
|
|
||||||
|
type CliCommandContextMainDeps = Parameters<typeof createCliCommandContextFactory>[0];
|
||||||
|
type CliCommandContext = ReturnType<ReturnType<typeof createCliCommandContextFactory>>;
|
||||||
|
type CliCommandRuntimeHandlerMainDeps = Omit<
|
||||||
|
Parameters<typeof createCliCommandRuntimeHandler<CliCommandContext>>[0],
|
||||||
|
'createCliCommandContext'
|
||||||
|
>;
|
||||||
|
type InitialArgsRuntimeHandlerMainDeps = Omit<
|
||||||
|
Parameters<typeof createInitialArgsRuntimeHandler>[0],
|
||||||
|
'handleCliCommand'
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type CliStartupComposerOptions = ComposerInputs<{
|
||||||
|
cliCommandContextMainDeps: CliCommandContextMainDeps;
|
||||||
|
cliCommandRuntimeHandlerMainDeps: CliCommandRuntimeHandlerMainDeps;
|
||||||
|
initialArgsRuntimeHandlerMainDeps: InitialArgsRuntimeHandlerMainDeps;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type CliStartupComposerResult = ComposerOutputs<{
|
||||||
|
createCliCommandContext: () => CliCommandContext;
|
||||||
|
handleCliCommand: (args: CliArgs, source?: CliCommandSource) => void;
|
||||||
|
handleInitialArgs: () => void;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export function composeCliStartupHandlers(
|
||||||
|
options: CliStartupComposerOptions,
|
||||||
|
): CliStartupComposerResult {
|
||||||
|
const createCliCommandContext = createCliCommandContextFactory(
|
||||||
|
options.cliCommandContextMainDeps,
|
||||||
|
);
|
||||||
|
const handleCliCommand = createCliCommandRuntimeHandler({
|
||||||
|
...options.cliCommandRuntimeHandlerMainDeps,
|
||||||
|
createCliCommandContext: () => createCliCommandContext(),
|
||||||
|
});
|
||||||
|
const handleInitialArgs = createInitialArgsRuntimeHandler({
|
||||||
|
...options.initialArgsRuntimeHandlerMainDeps,
|
||||||
|
handleCliCommand: (args, source) => handleCliCommand(args, source),
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
createCliCommandContext,
|
||||||
|
handleCliCommand,
|
||||||
|
handleInitialArgs,
|
||||||
|
};
|
||||||
|
}
|
||||||
66
src/main/runtime/composers/headless-startup-composer.test.ts
Normal file
66
src/main/runtime/composers/headless-startup-composer.test.ts
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import test from 'node:test';
|
||||||
|
import type { CliArgs } from '../../../cli/args';
|
||||||
|
import { composeHeadlessStartupHandlers } from './headless-startup-composer';
|
||||||
|
|
||||||
|
test('composeHeadlessStartupHandlers returns startup bootstrap handlers', () => {
|
||||||
|
const calls: string[] = [];
|
||||||
|
|
||||||
|
const handlers = composeHeadlessStartupHandlers<
|
||||||
|
CliArgs,
|
||||||
|
{ mode: string },
|
||||||
|
{ startAppLifecycle: (args: CliArgs) => void }
|
||||||
|
>({
|
||||||
|
startupRuntimeHandlersDeps: {
|
||||||
|
appLifecycleRuntimeRunnerMainDeps: {
|
||||||
|
app: { on: () => {} } as never,
|
||||||
|
platform: 'darwin',
|
||||||
|
shouldStartApp: () => true,
|
||||||
|
parseArgs: () => ({}) as never,
|
||||||
|
handleCliCommand: () => {},
|
||||||
|
printHelp: () => {},
|
||||||
|
logNoRunningInstance: () => {},
|
||||||
|
onReady: async () => {},
|
||||||
|
onWillQuitCleanup: () => {},
|
||||||
|
shouldRestoreWindowsOnActivate: () => false,
|
||||||
|
restoreWindowsOnActivate: () => {},
|
||||||
|
shouldQuitOnWindowAllClosed: () => false,
|
||||||
|
},
|
||||||
|
createAppLifecycleRuntimeRunner: () => (args) => {
|
||||||
|
calls.push(`lifecycle:${(args as { command?: string }).command ?? 'unknown'}`);
|
||||||
|
},
|
||||||
|
buildStartupBootstrapMainDeps: (startAppLifecycle) => ({
|
||||||
|
argv: ['node', 'main.js'],
|
||||||
|
parseArgs: () => ({ command: 'start' }) as never,
|
||||||
|
setLogLevel: () => {},
|
||||||
|
forceX11Backend: () => {},
|
||||||
|
enforceUnsupportedWaylandMode: () => {},
|
||||||
|
shouldStartApp: () => true,
|
||||||
|
getDefaultSocketPath: () => '/tmp/mpv.sock',
|
||||||
|
defaultTexthookerPort: 5174,
|
||||||
|
configDir: '/tmp/config',
|
||||||
|
defaultConfig: {} as never,
|
||||||
|
generateConfigTemplate: () => 'template',
|
||||||
|
generateDefaultConfigFile: async () => 0,
|
||||||
|
setExitCode: () => {},
|
||||||
|
quitApp: () => {},
|
||||||
|
logGenerateConfigError: () => {},
|
||||||
|
startAppLifecycle: (args) => startAppLifecycle(args as never),
|
||||||
|
}),
|
||||||
|
createStartupBootstrapRuntimeDeps: (deps) => ({
|
||||||
|
startAppLifecycle: deps.startAppLifecycle,
|
||||||
|
}),
|
||||||
|
runStartupBootstrapRuntime: (deps) => {
|
||||||
|
deps.startAppLifecycle({ command: 'start' } as unknown as CliArgs);
|
||||||
|
return { mode: 'started' };
|
||||||
|
},
|
||||||
|
applyStartupState: (state) => {
|
||||||
|
calls.push(`apply:${state.mode}`);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(typeof handlers.runAndApplyStartupState, 'function');
|
||||||
|
assert.deepEqual(handlers.runAndApplyStartupState(), { mode: 'started' });
|
||||||
|
assert.deepEqual(calls, ['lifecycle:start', 'apply:started']);
|
||||||
|
});
|
||||||
49
src/main/runtime/composers/headless-startup-composer.ts
Normal file
49
src/main/runtime/composers/headless-startup-composer.ts
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
import { createStartupRuntimeHandlers } from '../startup-runtime-handlers';
|
||||||
|
import type { ComposerInputs, ComposerOutputs } from './contracts';
|
||||||
|
|
||||||
|
type StartupRuntimeHandlersDeps<TCliArgs, TStartupState, TStartupBootstrapRuntimeDeps> = Parameters<
|
||||||
|
typeof createStartupRuntimeHandlers<TCliArgs, TStartupState, TStartupBootstrapRuntimeDeps>
|
||||||
|
>[0];
|
||||||
|
type StartupRuntimeHandlers<TCliArgs, TStartupState, TStartupBootstrapRuntimeDeps> = ReturnType<
|
||||||
|
typeof createStartupRuntimeHandlers<TCliArgs, TStartupState, TStartupBootstrapRuntimeDeps>
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type HeadlessStartupComposerOptions<
|
||||||
|
TCliArgs,
|
||||||
|
TStartupState,
|
||||||
|
TStartupBootstrapRuntimeDeps,
|
||||||
|
> = ComposerInputs<{
|
||||||
|
startupRuntimeHandlersDeps: StartupRuntimeHandlersDeps<
|
||||||
|
TCliArgs,
|
||||||
|
TStartupState,
|
||||||
|
TStartupBootstrapRuntimeDeps
|
||||||
|
>;
|
||||||
|
}>;
|
||||||
|
|
||||||
|
export type HeadlessStartupComposerResult<
|
||||||
|
TCliArgs,
|
||||||
|
TStartupState,
|
||||||
|
TStartupBootstrapRuntimeDeps,
|
||||||
|
> = ComposerOutputs<
|
||||||
|
Pick<
|
||||||
|
StartupRuntimeHandlers<TCliArgs, TStartupState, TStartupBootstrapRuntimeDeps>,
|
||||||
|
'appLifecycleRuntimeRunner' | 'runAndApplyStartupState'
|
||||||
|
>
|
||||||
|
>;
|
||||||
|
|
||||||
|
export function composeHeadlessStartupHandlers<
|
||||||
|
TCliArgs,
|
||||||
|
TStartupState,
|
||||||
|
TStartupBootstrapRuntimeDeps,
|
||||||
|
>(
|
||||||
|
options: HeadlessStartupComposerOptions<TCliArgs, TStartupState, TStartupBootstrapRuntimeDeps>,
|
||||||
|
): HeadlessStartupComposerResult<TCliArgs, TStartupState, TStartupBootstrapRuntimeDeps> {
|
||||||
|
const { appLifecycleRuntimeRunner, runAndApplyStartupState } = createStartupRuntimeHandlers(
|
||||||
|
options.startupRuntimeHandlersDeps,
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
appLifecycleRuntimeRunner,
|
||||||
|
runAndApplyStartupState,
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,10 +1,13 @@
|
|||||||
export * from './anilist-setup-composer';
|
export * from './anilist-setup-composer';
|
||||||
export * from './anilist-tracking-composer';
|
export * from './anilist-tracking-composer';
|
||||||
export * from './app-ready-composer';
|
export * from './app-ready-composer';
|
||||||
|
export * from './cli-startup-composer';
|
||||||
export * from './contracts';
|
export * from './contracts';
|
||||||
|
export * from './headless-startup-composer';
|
||||||
export * from './ipc-runtime-composer';
|
export * from './ipc-runtime-composer';
|
||||||
export * from './jellyfin-remote-composer';
|
export * from './jellyfin-remote-composer';
|
||||||
export * from './jellyfin-runtime-composer';
|
export * from './jellyfin-runtime-composer';
|
||||||
export * from './mpv-runtime-composer';
|
export * from './mpv-runtime-composer';
|
||||||
|
export * from './overlay-window-composer';
|
||||||
export * from './shortcuts-runtime-composer';
|
export * from './shortcuts-runtime-composer';
|
||||||
export * from './startup-lifecycle-composer';
|
export * from './startup-lifecycle-composer';
|
||||||
|
|||||||
34
src/main/runtime/composers/overlay-window-composer.test.ts
Normal file
34
src/main/runtime/composers/overlay-window-composer.test.ts
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import test from 'node:test';
|
||||||
|
import { composeOverlayWindowHandlers } from './overlay-window-composer';
|
||||||
|
|
||||||
|
test('composeOverlayWindowHandlers returns overlay window handlers', () => {
|
||||||
|
let mainWindow: { kind: string } | null = null;
|
||||||
|
let modalWindow: { kind: string } | null = null;
|
||||||
|
|
||||||
|
const handlers = composeOverlayWindowHandlers<{ kind: string }>({
|
||||||
|
createOverlayWindowDeps: {
|
||||||
|
createOverlayWindowCore: (kind) => ({ kind }),
|
||||||
|
isDev: false,
|
||||||
|
ensureOverlayWindowLevel: () => {},
|
||||||
|
onRuntimeOptionsChanged: () => {},
|
||||||
|
setOverlayDebugVisualizationEnabled: () => {},
|
||||||
|
isOverlayVisible: (kind) => kind === 'visible',
|
||||||
|
tryHandleOverlayShortcutLocalFallback: () => false,
|
||||||
|
forwardTabToMpv: () => {},
|
||||||
|
onWindowClosed: () => {},
|
||||||
|
getYomitanSession: () => null,
|
||||||
|
},
|
||||||
|
setMainWindow: (window) => {
|
||||||
|
mainWindow = window;
|
||||||
|
},
|
||||||
|
setModalWindow: (window) => {
|
||||||
|
modalWindow = window;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(handlers.createMainWindow(), { kind: 'visible' });
|
||||||
|
assert.deepEqual(mainWindow, { kind: 'visible' });
|
||||||
|
assert.deepEqual(handlers.createModalWindow(), { kind: 'modal' });
|
||||||
|
assert.deepEqual(modalWindow, { kind: 'modal' });
|
||||||
|
});
|
||||||
18
src/main/runtime/composers/overlay-window-composer.ts
Normal file
18
src/main/runtime/composers/overlay-window-composer.ts
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
import { createOverlayWindowRuntimeHandlers } from '../overlay-window-runtime-handlers';
|
||||||
|
import type { ComposerInputs, ComposerOutputs } from './contracts';
|
||||||
|
|
||||||
|
type OverlayWindowRuntimeDeps<TWindow> =
|
||||||
|
Parameters<typeof createOverlayWindowRuntimeHandlers<TWindow>>[0];
|
||||||
|
type OverlayWindowRuntimeHandlers<TWindow> = ReturnType<
|
||||||
|
typeof createOverlayWindowRuntimeHandlers<TWindow>
|
||||||
|
>;
|
||||||
|
|
||||||
|
export type OverlayWindowComposerOptions<TWindow> = ComposerInputs<OverlayWindowRuntimeDeps<TWindow>>;
|
||||||
|
export type OverlayWindowComposerResult<TWindow> =
|
||||||
|
ComposerOutputs<OverlayWindowRuntimeHandlers<TWindow>>;
|
||||||
|
|
||||||
|
export function composeOverlayWindowHandlers<TWindow>(
|
||||||
|
options: OverlayWindowComposerOptions<TWindow>,
|
||||||
|
): OverlayWindowComposerResult<TWindow> {
|
||||||
|
return createOverlayWindowRuntimeHandlers<TWindow>(options);
|
||||||
|
}
|
||||||
79
src/main/runtime/setup-window-factory.test.ts
Normal file
79
src/main/runtime/setup-window-factory.test.ts
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import test from 'node:test';
|
||||||
|
import {
|
||||||
|
createCreateAnilistSetupWindowHandler,
|
||||||
|
createCreateFirstRunSetupWindowHandler,
|
||||||
|
createCreateJellyfinSetupWindowHandler,
|
||||||
|
} from './setup-window-factory';
|
||||||
|
|
||||||
|
test('createCreateFirstRunSetupWindowHandler builds first-run setup window', () => {
|
||||||
|
let options: Electron.BrowserWindowConstructorOptions | null = null;
|
||||||
|
const createSetupWindow = createCreateFirstRunSetupWindowHandler({
|
||||||
|
createBrowserWindow: (nextOptions) => {
|
||||||
|
options = nextOptions;
|
||||||
|
return { id: 'first-run' } as never;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(createSetupWindow(), { id: 'first-run' });
|
||||||
|
assert.deepEqual(options, {
|
||||||
|
width: 480,
|
||||||
|
height: 460,
|
||||||
|
title: 'SubMiner Setup',
|
||||||
|
show: true,
|
||||||
|
autoHideMenuBar: true,
|
||||||
|
resizable: false,
|
||||||
|
minimizable: false,
|
||||||
|
maximizable: false,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: false,
|
||||||
|
contextIsolation: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('createCreateJellyfinSetupWindowHandler builds jellyfin setup window', () => {
|
||||||
|
let options: Electron.BrowserWindowConstructorOptions | null = null;
|
||||||
|
const createSetupWindow = createCreateJellyfinSetupWindowHandler({
|
||||||
|
createBrowserWindow: (nextOptions) => {
|
||||||
|
options = nextOptions;
|
||||||
|
return { id: 'jellyfin' } as never;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(createSetupWindow(), { id: 'jellyfin' });
|
||||||
|
assert.deepEqual(options, {
|
||||||
|
width: 520,
|
||||||
|
height: 560,
|
||||||
|
title: 'Jellyfin Setup',
|
||||||
|
show: true,
|
||||||
|
autoHideMenuBar: true,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: false,
|
||||||
|
contextIsolation: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
test('createCreateAnilistSetupWindowHandler builds anilist setup window', () => {
|
||||||
|
let options: Electron.BrowserWindowConstructorOptions | null = null;
|
||||||
|
const createSetupWindow = createCreateAnilistSetupWindowHandler({
|
||||||
|
createBrowserWindow: (nextOptions) => {
|
||||||
|
options = nextOptions;
|
||||||
|
return { id: 'anilist' } as never;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.deepEqual(createSetupWindow(), { id: 'anilist' });
|
||||||
|
assert.deepEqual(options, {
|
||||||
|
width: 1000,
|
||||||
|
height: 760,
|
||||||
|
title: 'Anilist Setup',
|
||||||
|
show: true,
|
||||||
|
autoHideMenuBar: true,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: false,
|
||||||
|
contextIsolation: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
53
src/main/runtime/setup-window-factory.ts
Normal file
53
src/main/runtime/setup-window-factory.ts
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
export function createCreateFirstRunSetupWindowHandler<TWindow>(deps: {
|
||||||
|
createBrowserWindow: (options: Electron.BrowserWindowConstructorOptions) => TWindow;
|
||||||
|
}) {
|
||||||
|
return (): TWindow =>
|
||||||
|
deps.createBrowserWindow({
|
||||||
|
width: 480,
|
||||||
|
height: 460,
|
||||||
|
title: 'SubMiner Setup',
|
||||||
|
show: true,
|
||||||
|
autoHideMenuBar: true,
|
||||||
|
resizable: false,
|
||||||
|
minimizable: false,
|
||||||
|
maximizable: false,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: false,
|
||||||
|
contextIsolation: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCreateJellyfinSetupWindowHandler<TWindow>(deps: {
|
||||||
|
createBrowserWindow: (options: Electron.BrowserWindowConstructorOptions) => TWindow;
|
||||||
|
}) {
|
||||||
|
return (): TWindow =>
|
||||||
|
deps.createBrowserWindow({
|
||||||
|
width: 520,
|
||||||
|
height: 560,
|
||||||
|
title: 'Jellyfin Setup',
|
||||||
|
show: true,
|
||||||
|
autoHideMenuBar: true,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: false,
|
||||||
|
contextIsolation: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function createCreateAnilistSetupWindowHandler<TWindow>(deps: {
|
||||||
|
createBrowserWindow: (options: Electron.BrowserWindowConstructorOptions) => TWindow;
|
||||||
|
}) {
|
||||||
|
return (): TWindow =>
|
||||||
|
deps.createBrowserWindow({
|
||||||
|
width: 1000,
|
||||||
|
height: 760,
|
||||||
|
title: 'Anilist Setup',
|
||||||
|
show: true,
|
||||||
|
autoHideMenuBar: true,
|
||||||
|
webPreferences: {
|
||||||
|
nodeIntegration: false,
|
||||||
|
contextIsolation: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user