Files
SubMiner/src/config/definitions.ts
sudacode ffe5c6e1c6 feat(stats): add stats server, API endpoints, config, and Anki integration
- Hono HTTP server with 20+ REST endpoints for stats data
- Stats overlay BrowserWindow with toggle keybinding
- IPC channel definitions and preload bridge
- Stats config section (toggleKey, serverPort, autoStartServer, autoOpenBrowser)
- Config resolver for stats section
- AnkiConnect proxy endpoints (guiBrowse, notesInfo)
- Note ID passthrough in card mining callback chain
- Stats CLI command with autoOpenBrowser respect
2026-03-17 20:04:40 -07:00

111 lines
3.5 KiB
TypeScript

import { RawConfig, ResolvedConfig } from '../types';
import { CORE_DEFAULT_CONFIG } from './definitions/defaults-core';
import { IMMERSION_DEFAULT_CONFIG } from './definitions/defaults-immersion';
import { INTEGRATIONS_DEFAULT_CONFIG } from './definitions/defaults-integrations';
import { STATS_DEFAULT_CONFIG } from './definitions/defaults-stats';
import { SUBTITLE_DEFAULT_CONFIG } from './definitions/defaults-subtitle';
import { buildCoreConfigOptionRegistry } from './definitions/options-core';
import { buildImmersionConfigOptionRegistry } from './definitions/options-immersion';
import { buildIntegrationConfigOptionRegistry } from './definitions/options-integrations';
import { buildStatsConfigOptionRegistry } from './definitions/options-stats';
import { buildSubtitleConfigOptionRegistry } from './definitions/options-subtitle';
import { buildRuntimeOptionRegistry } from './definitions/runtime-options';
import { CONFIG_TEMPLATE_SECTIONS } from './definitions/template-sections';
export { DEFAULT_KEYBINDINGS, SPECIAL_COMMANDS } from './definitions/shared';
export type {
ConfigOptionRegistryEntry,
ConfigTemplateSection,
ConfigValueKind,
RuntimeOptionRegistryEntry,
} from './definitions/shared';
const {
subtitlePosition,
keybindings,
websocket,
annotationWebsocket,
logging,
texthooker,
controller,
shortcuts,
secondarySub,
subsync,
startupWarmups,
auto_start_overlay,
} = CORE_DEFAULT_CONFIG;
const { ankiConnect, jimaku, anilist, yomitan, jellyfin, discordPresence, ai, youtubeSubgen } =
INTEGRATIONS_DEFAULT_CONFIG;
const { subtitleStyle } = SUBTITLE_DEFAULT_CONFIG;
const { immersionTracking } = IMMERSION_DEFAULT_CONFIG;
const { stats } = STATS_DEFAULT_CONFIG;
export const DEFAULT_CONFIG: ResolvedConfig = {
subtitlePosition,
keybindings,
websocket,
annotationWebsocket,
logging,
texthooker,
controller,
ankiConnect,
shortcuts,
secondarySub,
subsync,
startupWarmups,
subtitleStyle,
auto_start_overlay,
jimaku,
anilist,
yomitan,
jellyfin,
discordPresence,
ai,
youtubeSubgen,
immersionTracking,
stats,
};
export const DEFAULT_ANKI_CONNECT_CONFIG = DEFAULT_CONFIG.ankiConnect;
export const RUNTIME_OPTION_REGISTRY = buildRuntimeOptionRegistry(DEFAULT_CONFIG);
export const CONFIG_OPTION_REGISTRY = [
...buildCoreConfigOptionRegistry(DEFAULT_CONFIG),
...buildSubtitleConfigOptionRegistry(DEFAULT_CONFIG),
...buildIntegrationConfigOptionRegistry(DEFAULT_CONFIG, RUNTIME_OPTION_REGISTRY),
...buildImmersionConfigOptionRegistry(DEFAULT_CONFIG),
...buildStatsConfigOptionRegistry(DEFAULT_CONFIG),
];
export { CONFIG_TEMPLATE_SECTIONS };
export function deepCloneConfig(config: ResolvedConfig): ResolvedConfig {
return JSON.parse(JSON.stringify(config)) as ResolvedConfig;
}
export function deepMergeRawConfig(base: RawConfig, patch: RawConfig): RawConfig {
const clone = JSON.parse(JSON.stringify(base)) as Record<string, unknown>;
const patchObject = patch as Record<string, unknown>;
const mergeInto = (target: Record<string, unknown>, source: Record<string, unknown>): void => {
for (const [key, value] of Object.entries(source)) {
if (
value !== null &&
typeof value === 'object' &&
!Array.isArray(value) &&
typeof target[key] === 'object' &&
target[key] !== null &&
!Array.isArray(target[key])
) {
mergeInto(target[key] as Record<string, unknown>, value as Record<string, unknown>);
} else {
target[key] = value;
}
}
};
mergeInto(clone, patchObject);
return clone as RawConfig;
}