Windows update (#49)

This commit is contained in:
2026-04-11 21:45:52 -07:00
committed by GitHub
parent 49e46e6b9b
commit 52bab1d611
168 changed files with 9732 additions and 1422 deletions

View File

@@ -8,12 +8,41 @@ import type {
import type {
ControllerConfigUpdate,
ControllerPreferenceUpdate,
SessionActionDispatchRequest,
SubsyncManualRunRequest,
} from '../../types/runtime';
import type { RuntimeOptionId, RuntimeOptionValue } from '../../types/runtime-options';
import type { SessionActionId, SessionActionPayload } from '../../types/session-bindings';
import type { SubtitlePosition } from '../../types/subtitle';
import { OVERLAY_HOSTED_MODALS, type OverlayHostedModal } from './contracts';
const SESSION_ACTION_IDS: SessionActionId[] = [
'toggleStatsOverlay',
'toggleVisibleOverlay',
'copySubtitle',
'copySubtitleMultiple',
'updateLastCardFromClipboard',
'triggerFieldGrouping',
'triggerSubsync',
'mineSentence',
'mineSentenceMultiple',
'toggleSecondarySub',
'markAudioCard',
'toggleSubtitleSidebar',
'openRuntimeOptions',
'openSessionHelp',
'openControllerSelect',
'openControllerDebug',
'openJimaku',
'openYoutubePicker',
'openPlaylistBrowser',
'replayCurrentSubtitle',
'playNextSubtitle',
'shiftSubDelayPrevLine',
'shiftSubDelayNextLine',
'cycleRuntimeOption',
];
const RUNTIME_OPTION_IDS: RuntimeOptionId[] = [
'anki.autoUpdateNewCards',
'subtitle.annotation.nPlusOne',
@@ -35,6 +64,43 @@ function isInteger(value: unknown): value is number {
return typeof value === 'number' && Number.isInteger(value);
}
function isSessionActionId(value: unknown): value is SessionActionId {
return typeof value === 'string' && SESSION_ACTION_IDS.includes(value as SessionActionId);
}
function parseSessionActionPayload(
actionId: SessionActionId,
value: unknown,
): SessionActionPayload | undefined | null {
if (actionId === 'copySubtitleMultiple' || actionId === 'mineSentenceMultiple') {
if (value === undefined) return undefined;
if (!isObject(value)) return null;
const keys = Object.keys(value);
if (keys.some((key) => key !== 'count')) return null;
if (value.count === undefined) return null;
if (!isInteger(value.count) || value.count < 1) return null;
return { count: value.count };
}
if (actionId === 'cycleRuntimeOption') {
if (!isObject(value)) return null;
const keys = Object.keys(value);
if (keys.some((key) => key !== 'runtimeOptionId' && key !== 'direction')) return null;
if (typeof value.runtimeOptionId !== 'string' || value.runtimeOptionId.trim().length === 0) {
return null;
}
if (value.direction !== 1 && value.direction !== -1) {
return null;
}
return {
runtimeOptionId: value.runtimeOptionId,
direction: value.direction,
};
}
return value === undefined ? undefined : null;
}
export function parseOverlayHostedModal(value: unknown): OverlayHostedModal | null {
if (typeof value !== 'string') return null;
return OVERLAY_HOSTED_MODALS.includes(value as OverlayHostedModal)
@@ -182,6 +248,17 @@ export function parseRuntimeOptionValue(value: unknown): RuntimeOptionValue | nu
: null;
}
export function parseSessionActionDispatchRequest(
value: unknown,
): SessionActionDispatchRequest | null {
if (!isObject(value)) return null;
if (!isSessionActionId(value.actionId)) return null;
const payload = parseSessionActionPayload(value.actionId, value.payload);
if (payload === null) return null;
return payload === undefined ? { actionId: value.actionId } : { actionId: value.actionId, payload };
}
export function parseMpvCommand(value: unknown): Array<string | number> | null {
if (!Array.isArray(value)) return null;
return value.every((entry) => typeof entry === 'string' || typeof entry === 'number')