docs: document Kiku/Lapis integration in config and anki pages

This commit is contained in:
2026-03-01 01:08:30 -08:00
parent 73e70b4395
commit c8b65a01f6
11 changed files with 174 additions and 11 deletions

View File

@@ -1,6 +1,6 @@
import test from 'node:test';
import assert from 'node:assert/strict';
import { hasExplicitCommand, parseArgs, shouldStartApp } from './args';
import { hasExplicitCommand, parseArgs, shouldRunSettingsOnlyStartup, shouldStartApp } from './args';
test('parseArgs parses booleans and value flags', () => {
const args = parseArgs([
@@ -60,6 +60,28 @@ test('hasExplicitCommand and shouldStartApp preserve command intent', () => {
assert.equal(hasExplicitCommand(refreshKnownWords), true);
assert.equal(shouldStartApp(refreshKnownWords), false);
const settings = parseArgs(['--settings']);
assert.equal(settings.settings, true);
assert.equal(hasExplicitCommand(settings), true);
assert.equal(shouldStartApp(settings), true);
assert.equal(shouldRunSettingsOnlyStartup(settings), true);
const settingsWithOverlay = parseArgs(['--settings', '--toggle-visible-overlay']);
assert.equal(settingsWithOverlay.settings, true);
assert.equal(settingsWithOverlay.toggleVisibleOverlay, true);
assert.equal(shouldRunSettingsOnlyStartup(settingsWithOverlay), false);
const yomitanAlias = parseArgs(['--yomitan']);
assert.equal(yomitanAlias.settings, true);
assert.equal(hasExplicitCommand(yomitanAlias), true);
assert.equal(shouldStartApp(yomitanAlias), true);
const help = parseArgs(['--help']);
assert.equal(help.help, true);
assert.equal(hasExplicitCommand(help), true);
assert.equal(shouldStartApp(help), false);
assert.equal(shouldRunSettingsOnlyStartup(help), false);
const anilistStatus = parseArgs(['--anilist-status']);
assert.equal(anilistStatus.anilistStatus, true);
assert.equal(hasExplicitCommand(anilistStatus), true);

View File

@@ -295,6 +295,7 @@ export function shouldStartApp(args: CliArgs): boolean {
args.start ||
args.toggle ||
args.toggleVisibleOverlay ||
args.settings ||
args.copySubtitle ||
args.copySubtitleMultiple ||
args.mineSentence ||
@@ -314,6 +315,50 @@ export function shouldStartApp(args: CliArgs): boolean {
return false;
}
export function shouldRunSettingsOnlyStartup(args: CliArgs): boolean {
return (
args.settings &&
!args.background &&
!args.start &&
!args.stop &&
!args.toggle &&
!args.toggleVisibleOverlay &&
!args.show &&
!args.hide &&
!args.showVisibleOverlay &&
!args.hideVisibleOverlay &&
!args.copySubtitle &&
!args.copySubtitleMultiple &&
!args.mineSentence &&
!args.mineSentenceMultiple &&
!args.updateLastCardFromClipboard &&
!args.refreshKnownWords &&
!args.toggleSecondarySub &&
!args.triggerFieldGrouping &&
!args.triggerSubsync &&
!args.markAudioCard &&
!args.openRuntimeOptions &&
!args.anilistStatus &&
!args.anilistLogout &&
!args.anilistSetup &&
!args.anilistRetryQueue &&
!args.jellyfin &&
!args.jellyfinLogin &&
!args.jellyfinLogout &&
!args.jellyfinLibraries &&
!args.jellyfinItems &&
!args.jellyfinSubtitles &&
!args.jellyfinPlay &&
!args.jellyfinRemoteAnnounce &&
!args.texthooker &&
!args.help &&
!args.autoStartOverlay &&
!args.generateConfig &&
!args.backupOverwrite &&
!args.debug
);
}
export function commandNeedsOverlayRuntime(args: CliArgs): boolean {
return (
args.toggle ||

View File

@@ -66,6 +66,55 @@ test('runAppReadyRuntime starts websocket in auto mode when plugin missing', asy
);
});
test('runAppReadyRuntime skips heavy startup when shouldSkipHeavyStartup returns true', async () => {
const { deps, calls } = makeDeps({
shouldSkipHeavyStartup: () => true,
reloadConfig: () => calls.push('reloadConfig'),
getResolvedConfig: () => {
calls.push('getResolvedConfig');
return {
websocket: { enabled: 'auto' },
secondarySub: {},
};
},
getConfigWarnings: () => {
calls.push('getConfigWarnings');
return [];
},
setLogLevel: (level, source) => calls.push(`setLogLevel:${level}:${source}`),
initRuntimeOptionsManager: () => calls.push('initRuntimeOptionsManager'),
startBackgroundWarmups: () => calls.push('startBackgroundWarmups'),
loadSubtitlePosition: () => calls.push('loadSubtitlePosition'),
resolveKeybindings: () => calls.push('resolveKeybindings'),
createMpvClient: () => calls.push('createMpvClient'),
logConfigWarning: () => calls.push('logConfigWarning'),
startJellyfinRemoteSession: async () => {
calls.push('startJellyfinRemoteSession');
},
createImmersionTracker: () => calls.push('createImmersionTracker'),
handleInitialArgs: () => calls.push('handleInitialArgs'),
});
await runAppReadyRuntime(deps);
assert.equal(calls.includes('reloadConfig'), false);
assert.equal(calls.includes('getResolvedConfig'), false);
assert.equal(calls.includes('getConfigWarnings'), false);
assert.equal(calls.includes('setLogLevel:warn:config'), false);
assert.equal(calls.includes('startBackgroundWarmups'), false);
assert.equal(calls.includes('loadSubtitlePosition'), false);
assert.equal(calls.includes('resolveKeybindings'), false);
assert.equal(calls.includes('createMpvClient'), false);
assert.equal(calls.includes('initRuntimeOptionsManager'), false);
assert.equal(calls.includes('createImmersionTracker'), false);
assert.equal(calls.includes('startJellyfinRemoteSession'), false);
assert.equal(calls.includes('logConfigWarning'), false);
assert.equal(calls.includes('handleInitialArgs'), true);
assert.equal(calls.includes('loadYomitanExtension'), true);
assert.equal(calls[0], 'loadYomitanExtension');
assert.equal(calls[calls.length - 1], 'handleInitialArgs');
});
test('runAppReadyRuntime skips Jellyfin remote startup when dependency is not wired', async () => {
const { deps, calls } = makeDeps({
startJellyfinRemoteSession: undefined,

View File

@@ -121,6 +121,7 @@ export interface AppReadyRuntimeDeps {
logDebug?: (message: string) => void;
onCriticalConfigErrors?: (errors: string[]) => void;
now?: () => number;
shouldSkipHeavyStartup?: () => boolean;
}
const REQUIRED_ANKI_FIELD_MAPPING_KEYS = [
@@ -169,6 +170,13 @@ export async function runAppReadyRuntime(deps: AppReadyRuntimeDeps): Promise<voi
const startupStartedAtMs = now();
deps.logDebug?.('App-ready critical path started.');
if (deps.shouldSkipHeavyStartup?.()) {
await deps.loadYomitanExtension();
deps.handleInitialArgs();
deps.logDebug?.(`App-ready critical path finished in ${now() - startupStartedAtMs}ms.`);
return;
}
deps.reloadConfig();
const config = deps.getResolvedConfig();
const criticalConfigErrors = getStartupCriticalConfigErrors(config);

View File

@@ -101,7 +101,12 @@ import { SubtitleTimingTracker } from './subtitle-timing-tracker';
import { RuntimeOptionsManager } from './runtime-options';
import { downloadToFile, isRemoteMediaPath, parseMediaInfo } from './jimaku/utils';
import { createLogger, setLogLevel, type LogLevelSource } from './logger';
import { commandNeedsOverlayRuntime, parseArgs, shouldStartApp } from './cli/args';
import {
commandNeedsOverlayRuntime,
parseArgs,
shouldRunSettingsOnlyStartup,
shouldStartApp,
} from './cli/args';
import type { CliArgs, CliCommandSource } from './cli/args';
import { printHelp } from './cli/help';
import {
@@ -2163,6 +2168,8 @@ const { reloadConfig: reloadConfigHandler, appReadyRuntimeRunner } = composeAppR
: configDerivedRuntime.shouldAutoInitializeOverlayRuntimeFromConfig(),
initializeOverlayRuntime: () => initializeOverlayRuntime(),
handleInitialArgs: () => handleInitialArgs(),
shouldSkipHeavyStartup: () =>
Boolean(appState.initialArgs && shouldRunSettingsOnlyStartup(appState.initialArgs)),
createImmersionTracker: () => {
ensureImmersionTrackerStarted();
},

View File

@@ -48,6 +48,7 @@ export interface AppReadyRuntimeDepsFactoryInput {
onCriticalConfigErrors?: AppReadyRuntimeDeps['onCriticalConfigErrors'];
logDebug?: AppReadyRuntimeDeps['logDebug'];
now?: AppReadyRuntimeDeps['now'];
shouldSkipHeavyStartup?: AppReadyRuntimeDeps['shouldSkipHeavyStartup'];
}
export function createAppLifecycleRuntimeDeps(
@@ -103,6 +104,7 @@ export function createAppReadyRuntimeDeps(
onCriticalConfigErrors: params.onCriticalConfigErrors,
logDebug: params.logDebug,
now: params.now,
shouldSkipHeavyStartup: params.shouldSkipHeavyStartup,
};
}

View File

@@ -31,5 +31,6 @@ export function createBuildAppReadyRuntimeMainDepsHandler(deps: AppReadyRuntimeD
onCriticalConfigErrors: deps.onCriticalConfigErrors,
logDebug: deps.logDebug,
now: deps.now,
shouldSkipHeavyStartup: deps.shouldSkipHeavyStartup,
});
}