import type { BrowserWindow, Extension } from "electron"; import type { Keybinding, MpvSubtitleRenderMetrics, SecondarySubMode, SubtitlePosition, KikuFieldGroupingChoice, } from "../types"; import type { CliArgs } from "../cli/args"; import type { SubtitleTimingTracker } from "../subtitle-timing-tracker"; import type { AnkiIntegration } from "../anki-integration"; import type { MpvIpcClient } from "../core/services"; import { DEFAULT_MPV_SUBTITLE_RENDER_METRICS } from "../core/services"; import type { RuntimeOptionsManager } from "../runtime-options"; import type { MecabTokenizer } from "../mecab-tokenizer"; import type { BaseWindowTracker } from "../window-trackers"; export interface AppState { yomitanExt: Extension | null; yomitanSettingsWindow: BrowserWindow | null; yomitanParserWindow: BrowserWindow | null; yomitanParserReadyPromise: Promise | null; yomitanParserInitPromise: Promise | null; mpvClient: MpvIpcClient | null; reconnectTimer: ReturnType | null; currentSubText: string; currentSubAssText: string; windowTracker: BaseWindowTracker | null; subtitlePosition: SubtitlePosition | null; currentMediaPath: string | null; currentMediaTitle: string | null; pendingSubtitlePosition: SubtitlePosition | null; mecabTokenizer: MecabTokenizer | null; keybindings: Keybinding[]; subtitleTimingTracker: SubtitleTimingTracker | null; ankiIntegration: AnkiIntegration | null; secondarySubMode: SecondarySubMode; lastSecondarySubToggleAtMs: number; previousSecondarySubVisibility: boolean | null; mpvSubtitleRenderMetrics: MpvSubtitleRenderMetrics; shortcutsRegistered: boolean; overlayRuntimeInitialized: boolean; fieldGroupingResolver: ((choice: KikuFieldGroupingChoice) => void) | null; fieldGroupingResolverSequence: number; runtimeOptionsManager: RuntimeOptionsManager | null; trackerNotReadyWarningShown: boolean; overlayDebugVisualizationEnabled: boolean; subsyncInProgress: boolean; initialArgs: CliArgs | null; mpvSocketPath: string; texthookerPort: number; backendOverride: string | null; autoStartOverlay: boolean; texthookerOnlyMode: boolean; } export interface AppStateInitialValues { mpvSocketPath: string; texthookerPort: number; backendOverride?: string | null; autoStartOverlay?: boolean; texthookerOnlyMode?: boolean; } export interface StartupState { initialArgs: Exclude; mpvSocketPath: AppState["mpvSocketPath"]; texthookerPort: AppState["texthookerPort"]; backendOverride: AppState["backendOverride"]; autoStartOverlay: AppState["autoStartOverlay"]; texthookerOnlyMode: AppState["texthookerOnlyMode"]; } export function createAppState(values: AppStateInitialValues): AppState { return { yomitanExt: null, yomitanSettingsWindow: null, yomitanParserWindow: null, yomitanParserReadyPromise: null, yomitanParserInitPromise: null, mpvClient: null, reconnectTimer: null, currentSubText: "", currentSubAssText: "", windowTracker: null, subtitlePosition: null, currentMediaPath: null, currentMediaTitle: null, pendingSubtitlePosition: null, mecabTokenizer: null, keybindings: [], subtitleTimingTracker: null, ankiIntegration: null, secondarySubMode: "hover", lastSecondarySubToggleAtMs: 0, previousSecondarySubVisibility: null, mpvSubtitleRenderMetrics: { ...DEFAULT_MPV_SUBTITLE_RENDER_METRICS, }, runtimeOptionsManager: null, trackerNotReadyWarningShown: false, overlayDebugVisualizationEnabled: false, shortcutsRegistered: false, overlayRuntimeInitialized: false, fieldGroupingResolver: null, fieldGroupingResolverSequence: 0, subsyncInProgress: false, initialArgs: null, mpvSocketPath: values.mpvSocketPath, texthookerPort: values.texthookerPort, backendOverride: values.backendOverride ?? null, autoStartOverlay: values.autoStartOverlay ?? false, texthookerOnlyMode: values.texthookerOnlyMode ?? false, }; } export function applyStartupState( appState: AppState, startupState: StartupState, ): void { appState.initialArgs = startupState.initialArgs; appState.mpvSocketPath = startupState.mpvSocketPath; appState.texthookerPort = startupState.texthookerPort; appState.backendOverride = startupState.backendOverride; appState.autoStartOverlay = startupState.autoStartOverlay; appState.texthookerOnlyMode = startupState.texthookerOnlyMode; }