refactor(ipc): remove dead preload methods and the ASS line broadcast (#293)

Drop 16 overlay preload methods with no caller (onSubtitleAss,
getCurrentSubtitleAss, onVisibility, getOverlayVisibility,
openYomitanSettings, quitApp, toggleOverlay, getMecabStatus,
setMecabEnabled, getKeybindings, saveControllerPreference,
getAnkiConnectStatus, setAnkiConnectEnabled, clearAnkiConnectHistory,
cycleRuntimeOption, appendClipboardVideoToQueue) along with their
IPC_CHANNELS keys, handlers, validators and dep plumbing.

With both ASS consumers gone, stop observing sub-text/ass in mpv and
remove the subtitle-ass-change event chain and appState.currentSubAssText.
The main-process functions these wrapped stay; tray, CLI, shortcuts and
session actions still use them.
This commit is contained in:
sudacode authored and GitHub committed 2026-10-12 22:11:17 -07:00
1 parent 8fc20b2499
commit 7909fcd2cf
32 files changed
+8 -876

No files matched your search

+4
View File
@@ -0,0 +1,4 @@
type: internal
area: overlay
- Removed 16 overlay preload/IPC methods that nothing called, plus their main-process handlers. SubMiner no longer observes mpv's `sub-text/ass` or sends every ASS line to overlay windows that never listened.
-13
View File
@@ -32,8 +32,6 @@ test('anki/jimaku IPC handlers reject malformed invoke payloads', async () => {
let previewCalls = 0; let previewCalls = 0;
registerAnkiJimakuIpcHandlers( registerAnkiJimakuIpcHandlers(
{ {
setAnkiConnectEnabled: () => {},
clearAnkiHistory: () => {},
refreshKnownWords: async () => {}, refreshKnownWords: async () => {},
respondFieldGrouping: () => {}, respondFieldGrouping: () => {},
buildKikuMergePreview: async () => { buildKikuMergePreview: async () => {
@@ -138,8 +136,6 @@ test('tsukihime downloads always route Japanese as primary', async () => {
const secondaryLoads: string[] = []; const secondaryLoads: string[] = [];
registerAnkiJimakuIpcHandlers( registerAnkiJimakuIpcHandlers(
{ {
setAnkiConnectEnabled: () => {},
clearAnkiHistory: () => {},
refreshKnownWords: async () => {}, refreshKnownWords: async () => {},
respondFieldGrouping: () => {}, respondFieldGrouping: () => {},
buildKikuMergePreview: async () => ({ ok: true }), buildKikuMergePreview: async () => ({ ok: true }),
@@ -205,13 +201,8 @@ test('tsukihime downloads always route Japanese as primary', async () => {
test('anki/jimaku IPC command handlers ignore malformed payloads', () => { test('anki/jimaku IPC command handlers ignore malformed payloads', () => {
const { registrar, onHandlers } = createFakeRegistrar(); const { registrar, onHandlers } = createFakeRegistrar();
const fieldGroupingChoices: unknown[] = []; const fieldGroupingChoices: unknown[] = [];
const enabledStates: boolean[] = [];
registerAnkiJimakuIpcHandlers( registerAnkiJimakuIpcHandlers(
{ {
setAnkiConnectEnabled: (enabled) => {
enabledStates.push(enabled);
},
clearAnkiHistory: () => {},
refreshKnownWords: async () => {}, refreshKnownWords: async () => {},
respondFieldGrouping: (choice) => { respondFieldGrouping: (choice) => {
fieldGroupingChoices.push(choice); fieldGroupingChoices.push(choice);
@@ -241,10 +232,6 @@ test('anki/jimaku IPC command handlers ignore malformed payloads', () => {
registrar, registrar,
); );
onHandlers.get(IPC_CHANNELS.command.setAnkiConnectEnabled)!({}, 'true');
onHandlers.get(IPC_CHANNELS.command.setAnkiConnectEnabled)!({}, true);
assert.deepEqual(enabledStates, [true]);
onHandlers.get(IPC_CHANNELS.command.kikuFieldGroupingRespond)!({}, null); onHandlers.get(IPC_CHANNELS.command.kikuFieldGroupingRespond)!({}, null);
onHandlers.get(IPC_CHANNELS.command.kikuFieldGroupingRespond)!( onHandlers.get(IPC_CHANNELS.command.kikuFieldGroupingRespond)!(
{}, {},
-11
View File
@@ -40,8 +40,6 @@ const { ipcMain } = electron;
const logger = createLogger('main:anki-jimaku-ipc'); const logger = createLogger('main:anki-jimaku-ipc');
export interface AnkiJimakuIpcDeps { export interface AnkiJimakuIpcDeps {
setAnkiConnectEnabled: (enabled: boolean) => void;
clearAnkiHistory: () => void;
refreshKnownWords: () => Promise<void> | void; refreshKnownWords: () => Promise<void> | void;
respondFieldGrouping: (choice: KikuFieldGroupingChoice) => void; respondFieldGrouping: (choice: KikuFieldGroupingChoice) => void;
buildKikuMergePreview: (request: KikuMergePreviewRequest) => Promise<KikuMergePreviewResponse>; buildKikuMergePreview: (request: KikuMergePreviewRequest) => Promise<KikuMergePreviewResponse>;
@@ -77,15 +75,6 @@ export function registerAnkiJimakuIpcHandlers(
deps: AnkiJimakuIpcDeps, deps: AnkiJimakuIpcDeps,
ipc: IpcMainRegistrar = ipcMain, ipc: IpcMainRegistrar = ipcMain,
): void { ): void {
ipc.on(IPC_CHANNELS.command.setAnkiConnectEnabled, (_event: unknown, enabled: unknown) => {
if (typeof enabled !== 'boolean') return;
deps.setAnkiConnectEnabled(enabled);
});
ipc.on(IPC_CHANNELS.command.clearAnkiConnectHistory, () => {
deps.clearAnkiHistory();
});
ipc.on(IPC_CHANNELS.command.refreshKnownWords, async () => { ipc.on(IPC_CHANNELS.command.refreshKnownWords, async () => {
await deps.refreshKnownWords(); await deps.refreshKnownWords();
}); });
+1 -54
View File
@@ -8,8 +8,6 @@ interface RuntimeHarness {
state: { state: {
ankiIntegration: unknown; ankiIntegration: unknown;
fieldGroupingResolver: ((choice: unknown) => void) | null; fieldGroupingResolver: ((choice: unknown) => void) | null;
patches: boolean[];
broadcasts: number;
fetchCalls: Array<{ endpoint: string; query?: Record<string, unknown> }>; fetchCalls: Array<{ endpoint: string; query?: Record<string, unknown> }>;
tsukihimeFetchCalls: Array<{ endpoint: string; query?: Record<string, unknown> }>; tsukihimeFetchCalls: Array<{ endpoint: string; query?: Record<string, unknown> }>;
sentCommands: Array<{ command: (string | number)[] }>; sentCommands: Array<{ command: (string | number)[] }>;
@@ -20,8 +18,6 @@ function createHarness(): RuntimeHarness {
const state = { const state = {
ankiIntegration: null as unknown, ankiIntegration: null as unknown,
fieldGroupingResolver: null as ((choice: unknown) => void) | null, fieldGroupingResolver: null as ((choice: unknown) => void) | null,
patches: [] as boolean[],
broadcasts: 0,
fetchCalls: [] as Array<{ fetchCalls: [] as Array<{
endpoint: string; endpoint: string;
query?: Record<string, unknown>; query?: Record<string, unknown>;
@@ -34,11 +30,7 @@ function createHarness(): RuntimeHarness {
}; };
const options: AnkiJimakuIpcRuntimeOptions = { const options: AnkiJimakuIpcRuntimeOptions = {
patchAnkiConnectEnabled: (enabled) => {
state.patches.push(enabled);
},
getResolvedConfig: () => ({ tsukihime: { maxSearchResults: 2 } }), getResolvedConfig: () => ({ tsukihime: { maxSearchResults: 2 } }),
getRuntimeOptionsManager: () => null,
tsukihimeFetchJson: async (endpoint, query) => { tsukihimeFetchJson: async (endpoint, query) => {
state.tsukihimeFetchCalls.push({ state.tsukihimeFetchCalls.push({
endpoint, endpoint,
@@ -76,7 +68,6 @@ function createHarness(): RuntimeHarness {
} as never, } as never,
}; };
}, },
getSubtitleTimingTracker: () => null,
getMpvClient: () => ({ getMpvClient: () => ({
connected: true, connected: true,
send: (payload) => { send: (payload) => {
@@ -99,20 +90,6 @@ function createHarness(): RuntimeHarness {
}, },
}), }),
getAnkiIntegration: () => state.ankiIntegration as never, getAnkiIntegration: () => state.ankiIntegration as never,
setAnkiIntegration: (integration) => {
state.ankiIntegration = integration;
},
getKnownWordCacheStatePath: () => '/tmp/subminer-known-words-cache.json',
showDesktopNotification: () => {},
createFieldGroupingCallback: () => async () => ({
keepNoteId: 1,
deleteNoteId: 2,
deleteDuplicate: false,
cancelled: false,
}),
broadcastRuntimeOptionsChanged: () => {
state.broadcasts += 1;
},
getFieldGroupingResolver: () => state.fieldGroupingResolver as never, getFieldGroupingResolver: () => state.fieldGroupingResolver as never,
setFieldGroupingResolver: (resolver) => { setFieldGroupingResolver: (resolver) => {
state.fieldGroupingResolver = resolver as never; state.fieldGroupingResolver = resolver as never;
@@ -169,51 +146,21 @@ test('refreshKnownWords throws when integration is unavailable', async () => {
); );
}); });
test('setAnkiConnectEnabled disables active integration and broadcasts changes', () => { test('respondFieldGrouping resolves and clears the pending resolver', () => {
const { registered, state } = createHarness(); const { registered, state } = createHarness();
let destroyed = 0;
state.ankiIntegration = {
destroy: () => {
destroyed += 1;
},
};
registered.setAnkiConnectEnabled!(false);
assert.deepEqual(state.patches, [false]);
assert.equal(destroyed, 1);
assert.equal(state.ankiIntegration, null);
assert.equal(state.broadcasts, 1);
});
test('clearAnkiHistory and respondFieldGrouping execute runtime callbacks', () => {
const { registered, state, options } = createHarness();
let cleaned = 0;
let resolvedChoice: unknown = null; let resolvedChoice: unknown = null;
state.fieldGroupingResolver = (choice) => { state.fieldGroupingResolver = (choice) => {
resolvedChoice = choice; resolvedChoice = choice;
}; };
const originalGetTracker = options.getSubtitleTimingTracker;
options.getSubtitleTimingTracker = () =>
({
cleanup: () => {
cleaned += 1;
},
}) as never;
const choice = { const choice = {
keepNoteId: 10, keepNoteId: 10,
deleteNoteId: 11, deleteNoteId: 11,
deleteDuplicate: true, deleteDuplicate: true,
cancelled: false, cancelled: false,
}; };
registered.clearAnkiHistory!();
registered.respondFieldGrouping!(choice); registered.respondFieldGrouping!(choice);
options.getSubtitleTimingTracker = originalGetTracker;
assert.equal(cleaned, 1);
assert.deepEqual(resolvedChoice, choice); assert.deepEqual(resolvedChoice, choice);
assert.equal(state.fieldGroupingResolver, null); assert.equal(state.fieldGroupingResolver, null);
}); });
+1 -86
View File
@@ -1,19 +1,14 @@
import * as fs from 'fs'; import * as fs from 'fs';
import { AnkiIntegration } from '../../anki-integration'; import type { AnkiIntegration } from '../../anki-integration';
import { mergeAiConfig } from '../../ai/config';
import { import {
AiConfig,
TsukihimeApiResponse, TsukihimeApiResponse,
TsukihimeConfig, TsukihimeConfig,
AnkiConnectConfig,
JimakuApiResponse, JimakuApiResponse,
JimakuEntry, JimakuEntry,
JimakuFileEntry, JimakuFileEntry,
JimakuLanguagePreference, JimakuLanguagePreference,
JimakuMediaInfo, JimakuMediaInfo,
KikuFieldGroupingChoice, KikuFieldGroupingChoice,
KikuFieldGroupingRequestData,
OverlayNotificationPayload,
} from '../../types'; } from '../../types';
import { sortJimakuFiles } from '../../jimaku/utils'; import { sortJimakuFiles } from '../../jimaku/utils';
import { import {
@@ -35,41 +30,13 @@ interface MpvClientLike {
request?: (command: unknown[]) => Promise<{ data?: unknown }>; request?: (command: unknown[]) => Promise<{ data?: unknown }>;
} }
interface RuntimeOptionsManagerLike {
getEffectiveAnkiConnectConfig: (config?: AnkiConnectConfig) => AnkiConnectConfig;
}
interface SubtitleTimingTrackerLike {
cleanup: () => void;
}
export interface AnkiJimakuIpcRuntimeOptions { export interface AnkiJimakuIpcRuntimeOptions {
patchAnkiConnectEnabled: (enabled: boolean) => void;
getResolvedConfig: () => { getResolvedConfig: () => {
ankiConnect?: AnkiConnectConfig;
ai?: AiConfig;
tsukihime?: TsukihimeConfig; tsukihime?: TsukihimeConfig;
secondarySub?: { secondarySubLanguages?: string[] }; secondarySub?: { secondarySubLanguages?: string[] };
}; };
getRuntimeOptionsManager: () => RuntimeOptionsManagerLike | null;
getSubtitleTimingTracker: () => SubtitleTimingTrackerLike | null;
getMpvClient: () => MpvClientLike | null; getMpvClient: () => MpvClientLike | null;
getAnkiIntegration: () => AnkiIntegration | null; getAnkiIntegration: () => AnkiIntegration | null;
setAnkiIntegration: (integration: AnkiIntegration | null) => void;
getKnownWordCacheStatePath: () => string;
getCachedMediaPath?: (
currentVideoPath: string,
kind: 'audio' | 'video',
) => Promise<string | null>;
shouldRequireRemoteMediaCache?: () => boolean;
getYoutubeMediaSourceUrl?: () => Promise<string | null | undefined> | string | null | undefined;
showDesktopNotification: (title: string, options: { body?: string; icon?: string }) => void;
showOverlayNotification?: (payload: OverlayNotificationPayload) => void;
dismissOverlayNotification?: (id: string) => void;
createFieldGroupingCallback: () => (
data: KikuFieldGroupingRequestData,
) => Promise<KikuFieldGroupingChoice>;
broadcastRuntimeOptionsChanged: () => void;
getFieldGroupingResolver: () => ((choice: KikuFieldGroupingChoice) => void) | null; getFieldGroupingResolver: () => ((choice: KikuFieldGroupingChoice) => void) | null;
setFieldGroupingResolver: (resolver: ((choice: KikuFieldGroupingChoice) => void) | null) => void; setFieldGroupingResolver: (resolver: ((choice: KikuFieldGroupingChoice) => void) | null) => void;
parseMediaInfo: (mediaPath: string | null) => JimakuMediaInfo; parseMediaInfo: (mediaPath: string | null) => JimakuMediaInfo;
@@ -135,58 +102,6 @@ export function registerAnkiJimakuIpcRuntime(
registerHandlers: RegisterAnkiJimakuIpcRuntimeHandler, registerHandlers: RegisterAnkiJimakuIpcRuntimeHandler,
): void { ): void {
registerHandlers({ registerHandlers({
setAnkiConnectEnabled: (enabled) => {
options.patchAnkiConnectEnabled(enabled);
const config = options.getResolvedConfig();
const subtitleTimingTracker = options.getSubtitleTimingTracker();
const mpvClient = options.getMpvClient();
const ankiIntegration = options.getAnkiIntegration();
if (enabled && !ankiIntegration && subtitleTimingTracker && mpvClient) {
const runtimeOptionsManager = options.getRuntimeOptionsManager();
const effectiveAnkiConfig = runtimeOptionsManager
? runtimeOptionsManager.getEffectiveAnkiConnectConfig(config.ankiConnect)
: config.ankiConnect;
const integration = new AnkiIntegration(
effectiveAnkiConfig as never,
subtitleTimingTracker as never,
mpvClient as never,
(text: string) => {
if (mpvClient) {
mpvClient.send({
command: ['show-text', text, '3000'],
});
}
},
options.showDesktopNotification,
options.createFieldGroupingCallback(),
options.getKnownWordCacheStatePath(),
mergeAiConfig(config.ai, config.ankiConnect?.ai) as AiConfig,
undefined,
options.showOverlayNotification,
options.getCachedMediaPath,
options.shouldRequireRemoteMediaCache,
options.getYoutubeMediaSourceUrl,
options.dismissOverlayNotification,
);
integration.start();
options.setAnkiIntegration(integration);
logger.info('AnkiConnect integration enabled');
} else if (!enabled && ankiIntegration) {
ankiIntegration.destroy();
options.setAnkiIntegration(null);
logger.info('AnkiConnect integration disabled');
}
options.broadcastRuntimeOptionsChanged();
},
clearAnkiHistory: () => {
const subtitleTimingTracker = options.getSubtitleTimingTracker();
if (subtitleTimingTracker) {
subtitleTimingTracker.cleanup();
logger.info('AnkiConnect subtitle timing history cleared');
}
},
refreshKnownWords: async () => { refreshKnownWords: async () => {
const integration = options.getAnkiIntegration(); const integration = options.getAnkiIntegration();
if (!integration) { if (!integration) {
-213
View File
@@ -114,23 +114,15 @@ function createSubtitleSidebarSnapshotFixture(): SubtitleSidebarSnapshot {
function createRegisterIpcDeps(overrides: Partial<IpcServiceDeps> = {}): IpcServiceDeps { function createRegisterIpcDeps(overrides: Partial<IpcServiceDeps> = {}): IpcServiceDeps {
return { return {
onOverlayModalClosed: () => {}, onOverlayModalClosed: () => {},
openYomitanSettings: () => {},
quitApp: () => {},
toggleDevTools: () => {}, toggleDevTools: () => {},
getVisibleOverlayVisibility: () => false,
toggleVisibleOverlay: () => {},
tokenizeCurrentSubtitle: async () => null, tokenizeCurrentSubtitle: async () => null,
getCurrentSubtitleRaw: () => '', getCurrentSubtitleRaw: () => '',
getCurrentSubtitleAss: () => '',
getSubtitleSidebarSnapshot: async () => createSubtitleSidebarSnapshotFixture(), getSubtitleSidebarSnapshot: async () => createSubtitleSidebarSnapshotFixture(),
getPlaybackPaused: () => false, getPlaybackPaused: () => false,
getSubtitlePosition: () => null, getSubtitlePosition: () => null,
getSubtitleStyle: () => null, getSubtitleStyle: () => null,
saveSubtitlePosition: () => {}, saveSubtitlePosition: () => {},
getMecabStatus: () => ({ available: false, enabled: false, path: null }),
setMecabEnabled: () => {},
handleMpvCommand: () => {}, handleMpvCommand: () => {},
getKeybindings: () => [],
getSessionBindings: () => [], getSessionBindings: () => [],
getConfiguredShortcuts: () => ({}), getConfiguredShortcuts: () => ({}),
dispatchSessionAction: async () => {}, dispatchSessionAction: async () => {},
@@ -139,23 +131,19 @@ function createRegisterIpcDeps(overrides: Partial<IpcServiceDeps> = {}): IpcServ
getOverlayNotificationPosition: () => 'top-right', getOverlayNotificationPosition: () => 'top-right',
getControllerConfig: () => createControllerConfigFixture(), getControllerConfig: () => createControllerConfigFixture(),
saveControllerConfig: async () => {}, saveControllerConfig: async () => {},
saveControllerPreference: async () => {},
getSecondarySubMode: () => 'hover', getSecondarySubMode: () => 'hover',
getCurrentSecondarySub: () => '', getCurrentSecondarySub: () => '',
focusMainWindow: () => {}, focusMainWindow: () => {},
activatePlaybackWindowForOverlayInteraction: () => false, activatePlaybackWindowForOverlayInteraction: () => false,
runSubsyncManual: async () => ({ ok: true, message: 'ok' }), runSubsyncManual: async () => ({ ok: true, message: 'ok' }),
getAnkiConnectStatus: () => false,
getRuntimeOptions: () => [], getRuntimeOptions: () => [],
setRuntimeOption: () => ({ ok: true }), setRuntimeOption: () => ({ ok: true }),
cycleRuntimeOption: () => ({ ok: true }),
reportOverlayContentBounds: () => {}, reportOverlayContentBounds: () => {},
getAnilistStatus: () => ({}), getAnilistStatus: () => ({}),
clearAnilistToken: () => {}, clearAnilistToken: () => {},
openAnilistSetup: () => {}, openAnilistSetup: () => {},
getAnilistQueueStatus: () => ({}), getAnilistQueueStatus: () => ({}),
retryAnilistQueueNow: async () => ({ ok: true, message: 'ok' }), retryAnilistQueueNow: async () => ({ ok: true, message: 'ok' }),
appendClipboardVideoToQueue: () => ({ ok: true, message: 'ok' }),
getPlaylistBrowserSnapshot: async () => ({ getPlaylistBrowserSnapshot: async () => ({
directoryPath: null, directoryPath: null,
directoryAvailable: false, directoryAvailable: false,
@@ -312,26 +300,17 @@ test('registerIpcHandlers skips AniList update when manual mark watched has no a
test('registerIpcHandlers rejects malformed runtime-option payloads', async () => { test('registerIpcHandlers rejects malformed runtime-option payloads', async () => {
const { registrar, handlers } = createFakeIpcRegistrar(); const { registrar, handlers } = createFakeIpcRegistrar();
const calls: Array<{ id: string; value: unknown }> = []; const calls: Array<{ id: string; value: unknown }> = [];
const cycles: Array<{ id: string; direction: 1 | -1 }> = [];
registerIpcHandlers( registerIpcHandlers(
{ {
onOverlayModalClosed: () => {}, onOverlayModalClosed: () => {},
openYomitanSettings: () => {},
quitApp: () => {},
toggleDevTools: () => {}, toggleDevTools: () => {},
getVisibleOverlayVisibility: () => false,
toggleVisibleOverlay: () => {},
tokenizeCurrentSubtitle: async () => null, tokenizeCurrentSubtitle: async () => null,
getCurrentSubtitleRaw: () => '', getCurrentSubtitleRaw: () => '',
getCurrentSubtitleAss: () => '',
getPlaybackPaused: () => null, getPlaybackPaused: () => null,
getSubtitlePosition: () => null, getSubtitlePosition: () => null,
getSubtitleStyle: () => null, getSubtitleStyle: () => null,
saveSubtitlePosition: () => {}, saveSubtitlePosition: () => {},
getMecabStatus: () => ({ available: false, enabled: false, path: null }),
setMecabEnabled: () => {},
handleMpvCommand: () => {}, handleMpvCommand: () => {},
getKeybindings: () => [],
getSessionBindings: () => [], getSessionBindings: () => [],
getConfiguredShortcuts: () => ({}), getConfiguredShortcuts: () => ({}),
dispatchSessionAction: async () => {}, dispatchSessionAction: async () => {},
@@ -340,21 +319,15 @@ test('registerIpcHandlers rejects malformed runtime-option payloads', async () =
getOverlayNotificationPosition: () => 'top-right', getOverlayNotificationPosition: () => 'top-right',
getControllerConfig: () => createControllerConfigFixture(), getControllerConfig: () => createControllerConfigFixture(),
saveControllerConfig: () => {}, saveControllerConfig: () => {},
saveControllerPreference: () => {},
getSecondarySubMode: () => 'hover', getSecondarySubMode: () => 'hover',
getCurrentSecondarySub: () => '', getCurrentSecondarySub: () => '',
focusMainWindow: () => {}, focusMainWindow: () => {},
runSubsyncManual: async () => ({ ok: true, message: 'ok' }), runSubsyncManual: async () => ({ ok: true, message: 'ok' }),
getAnkiConnectStatus: () => false,
getRuntimeOptions: () => [], getRuntimeOptions: () => [],
setRuntimeOption: (id, value) => { setRuntimeOption: (id, value) => {
calls.push({ id, value }); calls.push({ id, value });
return { ok: true }; return { ok: true };
}, },
cycleRuntimeOption: (id, direction) => {
cycles.push({ id, direction });
return { ok: true };
},
getSubtitleSidebarSnapshot: async () => createSubtitleSidebarSnapshotFixture(), getSubtitleSidebarSnapshot: async () => createSubtitleSidebarSnapshotFixture(),
reportOverlayContentBounds: () => {}, reportOverlayContentBounds: () => {},
getAnilistStatus: () => ({}), getAnilistStatus: () => ({}),
@@ -362,7 +335,6 @@ test('registerIpcHandlers rejects malformed runtime-option payloads', async () =
openAnilistSetup: () => {}, openAnilistSetup: () => {},
getAnilistQueueStatus: () => ({}), getAnilistQueueStatus: () => ({}),
retryAnilistQueueNow: async () => ({ ok: true, message: 'ok' }), retryAnilistQueueNow: async () => ({ ok: true, message: 'ok' }),
appendClipboardVideoToQueue: () => ({ ok: true, message: 'ok' }),
getPlaylistBrowserSnapshot: async () => ({ getPlaylistBrowserSnapshot: async () => ({
directoryPath: null, directoryPath: null,
directoryAvailable: false, directoryAvailable: false,
@@ -399,16 +371,6 @@ test('registerIpcHandlers rejects malformed runtime-option payloads', async () =
{ id: 'subtitle.annotation.jlpt', value: false }, { id: 'subtitle.annotation.jlpt', value: false },
]); ]);
const cycleHandler = handlers.handle.get(IPC_CHANNELS.request.cycleRuntimeOption);
assert.ok(cycleHandler);
const invalidDirection = await cycleHandler!({}, 'anki.kikuFieldGrouping', 2);
assert.deepEqual(invalidDirection, {
ok: false,
error: 'Invalid runtime option cycle direction',
});
await cycleHandler!({}, 'anki.kikuFieldGrouping', -1);
assert.deepEqual(cycles, [{ id: 'anki.kikuFieldGrouping', direction: -1 }]);
const getPlaybackPausedHandler = handlers.handle.get(IPC_CHANNELS.request.getPlaybackPaused); const getPlaybackPausedHandler = handlers.handle.get(IPC_CHANNELS.request.getPlaybackPaused);
assert.ok(getPlaybackPausedHandler); assert.ok(getPlaybackPausedHandler);
assert.equal(getPlaybackPausedHandler!({}), null); assert.equal(getPlaybackPausedHandler!({}), null);
@@ -609,7 +571,6 @@ test('registerIpcHandlers records yomitan lookup when subtitle context recording
test('registerIpcHandlers ignores malformed fire-and-forget payloads', () => { test('registerIpcHandlers ignores malformed fire-and-forget payloads', () => {
const { registrar, handlers } = createFakeIpcRegistrar(); const { registrar, handlers } = createFakeIpcRegistrar();
const saves: unknown[] = []; const saves: unknown[] = [];
const controllerSaves: unknown[] = [];
const closedModals: unknown[] = []; const closedModals: unknown[] = [];
const openedModals: unknown[] = []; const openedModals: unknown[] = [];
registerIpcHandlers( registerIpcHandlers(
@@ -620,14 +581,9 @@ test('registerIpcHandlers ignores malformed fire-and-forget payloads', () => {
onOverlayModalOpened: (modal) => { onOverlayModalOpened: (modal) => {
openedModals.push(modal); openedModals.push(modal);
}, },
openYomitanSettings: () => {},
quitApp: () => {},
toggleDevTools: () => {}, toggleDevTools: () => {},
getVisibleOverlayVisibility: () => false,
toggleVisibleOverlay: () => {},
tokenizeCurrentSubtitle: async () => null, tokenizeCurrentSubtitle: async () => null,
getCurrentSubtitleRaw: () => '', getCurrentSubtitleRaw: () => '',
getCurrentSubtitleAss: () => '',
getSubtitleSidebarSnapshot: async () => createSubtitleSidebarSnapshotFixture(), getSubtitleSidebarSnapshot: async () => createSubtitleSidebarSnapshotFixture(),
getPlaybackPaused: () => false, getPlaybackPaused: () => false,
getSubtitlePosition: () => null, getSubtitlePosition: () => null,
@@ -635,10 +591,7 @@ test('registerIpcHandlers ignores malformed fire-and-forget payloads', () => {
saveSubtitlePosition: (position) => { saveSubtitlePosition: (position) => {
saves.push(position); saves.push(position);
}, },
getMecabStatus: () => ({ available: false, enabled: false, path: null }),
setMecabEnabled: () => {},
handleMpvCommand: () => {}, handleMpvCommand: () => {},
getKeybindings: () => [],
getSessionBindings: () => [], getSessionBindings: () => [],
getConfiguredShortcuts: () => ({}), getConfiguredShortcuts: () => ({}),
dispatchSessionAction: async () => {}, dispatchSessionAction: async () => {},
@@ -647,24 +600,18 @@ test('registerIpcHandlers ignores malformed fire-and-forget payloads', () => {
getOverlayNotificationPosition: () => 'top-right', getOverlayNotificationPosition: () => 'top-right',
getControllerConfig: () => createControllerConfigFixture(), getControllerConfig: () => createControllerConfigFixture(),
saveControllerConfig: () => {}, saveControllerConfig: () => {},
saveControllerPreference: (update) => {
controllerSaves.push(update);
},
getSecondarySubMode: () => 'hover', getSecondarySubMode: () => 'hover',
getCurrentSecondarySub: () => '', getCurrentSecondarySub: () => '',
focusMainWindow: () => {}, focusMainWindow: () => {},
runSubsyncManual: async () => ({ ok: true, message: 'ok' }), runSubsyncManual: async () => ({ ok: true, message: 'ok' }),
getAnkiConnectStatus: () => false,
getRuntimeOptions: () => [], getRuntimeOptions: () => [],
setRuntimeOption: () => ({ ok: true }), setRuntimeOption: () => ({ ok: true }),
cycleRuntimeOption: () => ({ ok: true }),
reportOverlayContentBounds: () => {}, reportOverlayContentBounds: () => {},
getAnilistStatus: () => ({}), getAnilistStatus: () => ({}),
clearAnilistToken: () => {}, clearAnilistToken: () => {},
openAnilistSetup: () => {}, openAnilistSetup: () => {},
getAnilistQueueStatus: () => ({}), getAnilistQueueStatus: () => ({}),
retryAnilistQueueNow: async () => ({ ok: true, message: 'ok' }), retryAnilistQueueNow: async () => ({ ok: true, message: 'ok' }),
appendClipboardVideoToQueue: () => ({ ok: true, message: 'ok' }),
getPlaylistBrowserSnapshot: async () => ({ getPlaylistBrowserSnapshot: async () => ({
directoryPath: null, directoryPath: null,
directoryAvailable: false, directoryAvailable: false,
@@ -698,96 +645,6 @@ test('registerIpcHandlers ignores malformed fire-and-forget payloads', () => {
assert.deepEqual(openedModals, ['subsync', 'runtime-options']); assert.deepEqual(openedModals, ['subsync', 'runtime-options']);
}); });
test('registerIpcHandlers awaits saveControllerPreference through request-response IPC', async () => {
const { registrar, handlers } = createFakeIpcRegistrar();
const controllerSaves: unknown[] = [];
registerIpcHandlers(
{
onOverlayModalClosed: () => {},
openYomitanSettings: () => {},
quitApp: () => {},
toggleDevTools: () => {},
getVisibleOverlayVisibility: () => false,
toggleVisibleOverlay: () => {},
tokenizeCurrentSubtitle: async () => null,
getCurrentSubtitleRaw: () => '',
getCurrentSubtitleAss: () => '',
getSubtitleSidebarSnapshot: async () => createSubtitleSidebarSnapshotFixture(),
getPlaybackPaused: () => false,
getSubtitlePosition: () => null,
getSubtitleStyle: () => null,
saveSubtitlePosition: () => {},
getMecabStatus: () => ({ available: false, enabled: false, path: null }),
setMecabEnabled: () => {},
handleMpvCommand: () => {},
getKeybindings: () => [],
getSessionBindings: () => [],
getConfiguredShortcuts: () => ({}),
dispatchSessionAction: async () => {},
getStatsToggleKey: () => 'Backquote',
getMarkWatchedKey: () => 'KeyW',
getOverlayNotificationPosition: () => 'top-right',
getControllerConfig: () => createControllerConfigFixture(),
saveControllerConfig: async () => {},
saveControllerPreference: async (update) => {
await Promise.resolve();
controllerSaves.push(update);
},
getSecondarySubMode: () => 'hover',
getCurrentSecondarySub: () => '',
focusMainWindow: () => {},
runSubsyncManual: async () => ({ ok: true, message: 'ok' }),
getAnkiConnectStatus: () => false,
getRuntimeOptions: () => [],
setRuntimeOption: () => ({ ok: true }),
cycleRuntimeOption: () => ({ ok: true }),
reportOverlayContentBounds: () => {},
getAnilistStatus: () => ({}),
clearAnilistToken: () => {},
openAnilistSetup: () => {},
getAnilistQueueStatus: () => ({}),
retryAnilistQueueNow: async () => ({ ok: true, message: 'ok' }),
appendClipboardVideoToQueue: () => ({ ok: true, message: 'ok' }),
getPlaylistBrowserSnapshot: async () => ({
directoryPath: null,
directoryAvailable: false,
directoryStatus: '',
directoryItems: [],
playlistItems: [],
playingIndex: null,
currentFilePath: null,
}),
appendPlaylistBrowserFile: async () => ({ ok: true, message: 'ok' }),
playPlaylistBrowserIndex: async () => ({ ok: true, message: 'ok' }),
removePlaylistBrowserIndex: async () => ({ ok: true, message: 'ok' }),
movePlaylistBrowserIndex: async () => ({ ok: true, message: 'ok' }),
onYoutubePickerResolve: async () => ({ ok: true, message: 'ok' }),
},
registrar,
);
const saveHandler = handlers.handle.get(IPC_CHANNELS.command.saveControllerPreference);
assert.ok(saveHandler);
await assert.rejects(async () => {
await saveHandler!({}, { preferredGamepadId: 12 });
}, /Invalid controller preference payload/);
await saveHandler!(
{},
{
preferredGamepadId: 'pad-1',
preferredGamepadLabel: 'Pad 1',
},
);
assert.deepEqual(controllerSaves, [
{
preferredGamepadId: 'pad-1',
preferredGamepadLabel: 'Pad 1',
},
]);
});
test('registerIpcHandlers accepts per-controller profile config updates', async () => { test('registerIpcHandlers accepts per-controller profile config updates', async () => {
const { registrar, handlers } = createFakeIpcRegistrar(); const { registrar, handlers } = createFakeIpcRegistrar();
const controllerSaves: unknown[] = []; const controllerSaves: unknown[] = [];
@@ -975,76 +832,6 @@ test('registerIpcHandlers forwards valid overlay notification actions', () => {
]); ]);
}); });
test('registerIpcHandlers rejects malformed controller preference payloads', async () => {
const { registrar, handlers } = createFakeIpcRegistrar();
registerIpcHandlers(
{
onOverlayModalClosed: () => {},
openYomitanSettings: () => {},
quitApp: () => {},
toggleDevTools: () => {},
getVisibleOverlayVisibility: () => false,
toggleVisibleOverlay: () => {},
tokenizeCurrentSubtitle: async () => null,
getCurrentSubtitleRaw: () => '',
getCurrentSubtitleAss: () => '',
getSubtitleSidebarSnapshot: async () => createSubtitleSidebarSnapshotFixture(),
getPlaybackPaused: () => false,
getSubtitlePosition: () => null,
getSubtitleStyle: () => null,
saveSubtitlePosition: () => {},
getMecabStatus: () => ({ available: false, enabled: false, path: null }),
setMecabEnabled: () => {},
handleMpvCommand: () => {},
getKeybindings: () => [],
getSessionBindings: () => [],
getConfiguredShortcuts: () => ({}),
dispatchSessionAction: async () => {},
getStatsToggleKey: () => 'Backquote',
getMarkWatchedKey: () => 'KeyW',
getOverlayNotificationPosition: () => 'top-right',
getControllerConfig: () => createControllerConfigFixture(),
saveControllerConfig: async () => {},
saveControllerPreference: async () => {},
getSecondarySubMode: () => 'hover',
getCurrentSecondarySub: () => '',
focusMainWindow: () => {},
runSubsyncManual: async () => ({ ok: true, message: 'ok' }),
getAnkiConnectStatus: () => false,
getRuntimeOptions: () => [],
setRuntimeOption: () => ({ ok: true }),
cycleRuntimeOption: () => ({ ok: true }),
reportOverlayContentBounds: () => {},
getAnilistStatus: () => ({}),
clearAnilistToken: () => {},
openAnilistSetup: () => {},
getAnilistQueueStatus: () => ({}),
retryAnilistQueueNow: async () => ({ ok: true, message: 'ok' }),
appendClipboardVideoToQueue: () => ({ ok: true, message: 'ok' }),
getPlaylistBrowserSnapshot: async () => ({
directoryPath: null,
directoryAvailable: false,
directoryStatus: '',
directoryItems: [],
playlistItems: [],
playingIndex: null,
currentFilePath: null,
}),
appendPlaylistBrowserFile: async () => ({ ok: true, message: 'ok' }),
playPlaylistBrowserIndex: async () => ({ ok: true, message: 'ok' }),
removePlaylistBrowserIndex: async () => ({ ok: true, message: 'ok' }),
movePlaylistBrowserIndex: async () => ({ ok: true, message: 'ok' }),
onYoutubePickerResolve: async () => ({ ok: true, message: 'ok' }),
},
registrar,
);
const saveHandler = handlers.handle.get(IPC_CHANNELS.command.saveControllerPreference);
await assert.rejects(async () => {
await saveHandler!({}, { preferredGamepadId: 12 });
}, /Invalid controller preference payload/);
});
test('registerIpcHandlers exposes character dictionary selection handlers', async () => { test('registerIpcHandlers exposes character dictionary selection handlers', async () => {
const { registrar, handlers } = createFakeIpcRegistrar(); const { registrar, handlers } = createFakeIpcRegistrar();
const calls: number[] = []; const calls: number[] = [];
-124
View File
@@ -7,7 +7,6 @@ import type {
ControllerConfigUpdate, ControllerConfigUpdate,
PlaylistBrowserMutationResult, PlaylistBrowserMutationResult,
PlaylistBrowserSnapshot, PlaylistBrowserSnapshot,
ControllerPreferenceUpdate,
ResolvedControllerConfig, ResolvedControllerConfig,
RuntimeOptionId, RuntimeOptionId,
RuntimeOptionValue, RuntimeOptionValue,
@@ -34,10 +33,8 @@ import { IPC_CHANNELS, type OverlayHostedModal } from '../../shared/ipc/contract
import { import {
parseMpvCommand, parseMpvCommand,
parseControllerConfigUpdate, parseControllerConfigUpdate,
parseControllerPreferenceUpdate,
parseOptionalForwardingOptions, parseOptionalForwardingOptions,
parseOverlayHostedModal, parseOverlayHostedModal,
parseRuntimeOptionDirection,
parseRuntimeOptionId, parseRuntimeOptionId,
parseRuntimeOptionValue, parseRuntimeOptionValue,
parseSessionActionDispatchRequest, parseSessionActionDispatchRequest,
@@ -71,14 +68,9 @@ export interface IpcServiceDeps {
actionId: string, actionId: string,
noteId?: number, noteId?: number,
) => void | Promise<void>; ) => void | Promise<void>;
openYomitanSettings: () => void;
quitApp: () => void;
toggleDevTools: () => void; toggleDevTools: () => void;
getVisibleOverlayVisibility: () => boolean;
toggleVisibleOverlay: () => void;
tokenizeCurrentSubtitle: () => Promise<unknown>; tokenizeCurrentSubtitle: () => Promise<unknown>;
getCurrentSubtitleRaw: () => string; getCurrentSubtitleRaw: () => string;
getCurrentSubtitleAss: () => string;
getSubtitleSidebarSnapshot?: () => Promise<SubtitleSidebarSnapshot>; getSubtitleSidebarSnapshot?: () => Promise<SubtitleSidebarSnapshot>;
getSubtitleSidebarPlaybackState?: () => SubtitleSidebarPlaybackState; getSubtitleSidebarPlaybackState?: () => SubtitleSidebarPlaybackState;
getSubtitleSidebarOpen?: () => boolean; getSubtitleSidebarOpen?: () => boolean;
@@ -86,14 +78,7 @@ export interface IpcServiceDeps {
getSubtitlePosition: () => unknown; getSubtitlePosition: () => unknown;
getSubtitleStyle: () => unknown; getSubtitleStyle: () => unknown;
saveSubtitlePosition: (position: SubtitlePosition) => void; saveSubtitlePosition: (position: SubtitlePosition) => void;
getMecabStatus: () => {
available: boolean;
enabled: boolean;
path: string | null;
};
setMecabEnabled: (enabled: boolean) => void;
handleMpvCommand: (command: Array<string | number>) => void; handleMpvCommand: (command: Array<string | number>) => void;
getKeybindings: () => unknown;
getMpvInputBindings?: () => Promise<MpvInputBindingsSnapshot>; getMpvInputBindings?: () => Promise<MpvInputBindingsSnapshot>;
getSessionBindings?: () => CompiledSessionBinding[] | Promise<CompiledSessionBinding[]>; getSessionBindings?: () => CompiledSessionBinding[] | Promise<CompiledSessionBinding[]>;
getConfiguredShortcuts: () => unknown; getConfiguredShortcuts: () => unknown;
@@ -103,7 +88,6 @@ export interface IpcServiceDeps {
getOverlayNotificationPosition: () => string; getOverlayNotificationPosition: () => string;
getControllerConfig: () => ResolvedControllerConfig; getControllerConfig: () => ResolvedControllerConfig;
saveControllerConfig: (update: ControllerConfigUpdate) => void | Promise<void>; saveControllerConfig: (update: ControllerConfigUpdate) => void | Promise<void>;
saveControllerPreference: (update: ControllerPreferenceUpdate) => void | Promise<void>;
getSecondarySubMode: () => unknown; getSecondarySubMode: () => unknown;
getCurrentSecondarySub: () => string; getCurrentSecondarySub: () => string;
focusMainWindow: () => void; focusMainWindow: () => void;
@@ -125,10 +109,8 @@ export interface IpcServiceDeps {
resolveMediaTimingReview?: ( resolveMediaTimingReview?: (
request: MediaTimingReviewResolveRequest, request: MediaTimingReviewResolveRequest,
) => MediaTimingReviewActionResult | Promise<MediaTimingReviewActionResult>; ) => MediaTimingReviewActionResult | Promise<MediaTimingReviewActionResult>;
getAnkiConnectStatus: () => boolean;
getRuntimeOptions: () => unknown; getRuntimeOptions: () => unknown;
setRuntimeOption: (id: RuntimeOptionId, value: RuntimeOptionValue) => unknown; setRuntimeOption: (id: RuntimeOptionId, value: RuntimeOptionValue) => unknown;
cycleRuntimeOption: (id: RuntimeOptionId, direction: 1 | -1) => unknown;
reportOverlayContentBounds: ( reportOverlayContentBounds: (
payload: unknown, payload: unknown,
senderWindow: ElectronBrowserWindow | null, senderWindow: ElectronBrowserWindow | null,
@@ -149,7 +131,6 @@ export interface IpcServiceDeps {
getCharacterDictionaryManagerSnapshot?: () => Promise<unknown>; getCharacterDictionaryManagerSnapshot?: () => Promise<unknown>;
removeCharacterDictionaryManagedEntry?: (mediaId: number) => Promise<unknown>; removeCharacterDictionaryManagedEntry?: (mediaId: number) => Promise<unknown>;
moveCharacterDictionaryManagedEntry?: (mediaId: number, direction: 1 | -1) => Promise<unknown>; moveCharacterDictionaryManagedEntry?: (mediaId: number, direction: 1 | -1) => Promise<unknown>;
appendClipboardVideoToQueue: () => { ok: boolean; message: string };
getChangelogSnapshot?: (options?: { refresh?: boolean }) => Promise<ChangelogSnapshot>; getChangelogSnapshot?: (options?: { refresh?: boolean }) => Promise<ChangelogSnapshot>;
getPlaylistBrowserSnapshot: () => Promise<PlaylistBrowserSnapshot>; getPlaylistBrowserSnapshot: () => Promise<PlaylistBrowserSnapshot>;
appendPlaylistBrowserFile: (filePath: string) => Promise<PlaylistBrowserMutationResult>; appendPlaylistBrowserFile: (filePath: string) => Promise<PlaylistBrowserMutationResult>;
@@ -174,15 +155,6 @@ interface WindowLike {
}; };
} }
interface MecabTokenizerLike {
getStatus: () => {
available: boolean;
enabled: boolean;
path: string | null;
};
setEnabled: (enabled: boolean) => void;
}
interface MpvClientLike { interface MpvClientLike {
currentSecondarySubText?: string; currentSecondarySubText?: string;
} }
@@ -342,7 +314,6 @@ function parseMediaTimingReviewResolveRequest(
export interface IpcDepsRuntimeOptions { export interface IpcDepsRuntimeOptions {
getMainWindow: () => WindowLike | null; getMainWindow: () => WindowLike | null;
getVisibleOverlayVisibility: () => boolean;
onOverlayModalClosed: ( onOverlayModalClosed: (
modal: OverlayHostedModal, modal: OverlayHostedModal,
senderWindow: ElectronBrowserWindow | null, senderWindow: ElectronBrowserWindow | null,
@@ -364,12 +335,8 @@ export interface IpcDepsRuntimeOptions {
actionId: string, actionId: string,
noteId?: number, noteId?: number,
) => void | Promise<void>; ) => void | Promise<void>;
openYomitanSettings: () => void;
quitApp: () => void;
toggleVisibleOverlay: () => void;
tokenizeCurrentSubtitle: () => Promise<unknown>; tokenizeCurrentSubtitle: () => Promise<unknown>;
getCurrentSubtitleRaw: () => string; getCurrentSubtitleRaw: () => string;
getCurrentSubtitleAss: () => string;
getSubtitleSidebarSnapshot?: () => Promise<SubtitleSidebarSnapshot>; getSubtitleSidebarSnapshot?: () => Promise<SubtitleSidebarSnapshot>;
getSubtitleSidebarPlaybackState?: () => SubtitleSidebarPlaybackState; getSubtitleSidebarPlaybackState?: () => SubtitleSidebarPlaybackState;
getSubtitleSidebarOpen?: () => boolean; getSubtitleSidebarOpen?: () => boolean;
@@ -377,9 +344,7 @@ export interface IpcDepsRuntimeOptions {
getSubtitlePosition: () => unknown; getSubtitlePosition: () => unknown;
getSubtitleStyle: () => unknown; getSubtitleStyle: () => unknown;
saveSubtitlePosition: (position: SubtitlePosition) => void; saveSubtitlePosition: (position: SubtitlePosition) => void;
getMecabTokenizer: () => MecabTokenizerLike | null;
handleMpvCommand: (command: Array<string | number>) => void; handleMpvCommand: (command: Array<string | number>) => void;
getKeybindings: () => unknown;
getMpvInputBindings?: () => Promise<MpvInputBindingsSnapshot>; getMpvInputBindings?: () => Promise<MpvInputBindingsSnapshot>;
getSessionBindings?: () => CompiledSessionBinding[] | Promise<CompiledSessionBinding[]>; getSessionBindings?: () => CompiledSessionBinding[] | Promise<CompiledSessionBinding[]>;
getConfiguredShortcuts: () => unknown; getConfiguredShortcuts: () => unknown;
@@ -389,7 +354,6 @@ export interface IpcDepsRuntimeOptions {
getOverlayNotificationPosition: () => string; getOverlayNotificationPosition: () => string;
getControllerConfig: () => ResolvedControllerConfig; getControllerConfig: () => ResolvedControllerConfig;
saveControllerConfig: (update: ControllerConfigUpdate) => void | Promise<void>; saveControllerConfig: (update: ControllerConfigUpdate) => void | Promise<void>;
saveControllerPreference: (update: ControllerPreferenceUpdate) => void | Promise<void>;
getSecondarySubMode: () => unknown; getSecondarySubMode: () => unknown;
getMpvClient: () => MpvClientLike | null; getMpvClient: () => MpvClientLike | null;
focusMainWindow: () => void; focusMainWindow: () => void;
@@ -403,10 +367,8 @@ export interface IpcDepsRuntimeOptions {
getMediaTimingReviewWaveform?: IpcServiceDeps['getMediaTimingReviewWaveform']; getMediaTimingReviewWaveform?: IpcServiceDeps['getMediaTimingReviewWaveform'];
stopMediaTimingReviewPreview?: IpcServiceDeps['stopMediaTimingReviewPreview']; stopMediaTimingReviewPreview?: IpcServiceDeps['stopMediaTimingReviewPreview'];
resolveMediaTimingReview?: IpcServiceDeps['resolveMediaTimingReview']; resolveMediaTimingReview?: IpcServiceDeps['resolveMediaTimingReview'];
getAnkiConnectStatus: () => boolean;
getRuntimeOptions: () => unknown; getRuntimeOptions: () => unknown;
setRuntimeOption: (id: RuntimeOptionId, value: RuntimeOptionValue) => unknown; setRuntimeOption: (id: RuntimeOptionId, value: RuntimeOptionValue) => unknown;
cycleRuntimeOption: (id: RuntimeOptionId, direction: 1 | -1) => unknown;
reportOverlayContentBounds: (payload: unknown) => void; reportOverlayContentBounds: (payload: unknown) => void;
getAnilistStatus: () => unknown; getAnilistStatus: () => unknown;
clearAnilistToken: () => void; clearAnilistToken: () => void;
@@ -424,7 +386,6 @@ export interface IpcDepsRuntimeOptions {
getCharacterDictionaryManagerSnapshot?: () => Promise<unknown>; getCharacterDictionaryManagerSnapshot?: () => Promise<unknown>;
removeCharacterDictionaryManagedEntry?: (mediaId: number) => Promise<unknown>; removeCharacterDictionaryManagedEntry?: (mediaId: number) => Promise<unknown>;
moveCharacterDictionaryManagedEntry?: (mediaId: number, direction: 1 | -1) => Promise<unknown>; moveCharacterDictionaryManagedEntry?: (mediaId: number, direction: 1 | -1) => Promise<unknown>;
appendClipboardVideoToQueue: () => { ok: boolean; message: string };
getChangelogSnapshot?: (options?: { refresh?: boolean }) => Promise<ChangelogSnapshot>; getChangelogSnapshot?: (options?: { refresh?: boolean }) => Promise<ChangelogSnapshot>;
getPlaylistBrowserSnapshot: () => Promise<PlaylistBrowserSnapshot>; getPlaylistBrowserSnapshot: () => Promise<PlaylistBrowserSnapshot>;
appendPlaylistBrowserFile: (filePath: string) => Promise<PlaylistBrowserMutationResult>; appendPlaylistBrowserFile: (filePath: string) => Promise<PlaylistBrowserMutationResult>;
@@ -444,19 +405,14 @@ export function createIpcDepsRuntime(options: IpcDepsRuntimeOptions): IpcService
onOverlayMouseInteractionChanged: options.onOverlayMouseInteractionChanged, onOverlayMouseInteractionChanged: options.onOverlayMouseInteractionChanged,
onOverlayInteractiveHint: options.onOverlayInteractiveHint, onOverlayInteractiveHint: options.onOverlayInteractiveHint,
handleOverlayNotificationAction: options.handleOverlayNotificationAction, handleOverlayNotificationAction: options.handleOverlayNotificationAction,
openYomitanSettings: options.openYomitanSettings,
recordSubtitleMiningContext: options.recordSubtitleMiningContext, recordSubtitleMiningContext: options.recordSubtitleMiningContext,
quitApp: options.quitApp,
toggleDevTools: () => { toggleDevTools: () => {
const mainWindow = options.getMainWindow(); const mainWindow = options.getMainWindow();
if (!mainWindow || mainWindow.isDestroyed()) return; if (!mainWindow || mainWindow.isDestroyed()) return;
mainWindow.webContents.toggleDevTools(); mainWindow.webContents.toggleDevTools();
}, },
getVisibleOverlayVisibility: options.getVisibleOverlayVisibility,
toggleVisibleOverlay: options.toggleVisibleOverlay,
tokenizeCurrentSubtitle: options.tokenizeCurrentSubtitle, tokenizeCurrentSubtitle: options.tokenizeCurrentSubtitle,
getCurrentSubtitleRaw: options.getCurrentSubtitleRaw, getCurrentSubtitleRaw: options.getCurrentSubtitleRaw,
getCurrentSubtitleAss: options.getCurrentSubtitleAss,
getSubtitleSidebarSnapshot: options.getSubtitleSidebarSnapshot, getSubtitleSidebarSnapshot: options.getSubtitleSidebarSnapshot,
getSubtitleSidebarPlaybackState: options.getSubtitleSidebarPlaybackState, getSubtitleSidebarPlaybackState: options.getSubtitleSidebarPlaybackState,
getSubtitleSidebarOpen: options.getSubtitleSidebarOpen ?? (() => false), getSubtitleSidebarOpen: options.getSubtitleSidebarOpen ?? (() => false),
@@ -464,19 +420,7 @@ export function createIpcDepsRuntime(options: IpcDepsRuntimeOptions): IpcService
getSubtitlePosition: options.getSubtitlePosition, getSubtitlePosition: options.getSubtitlePosition,
getSubtitleStyle: options.getSubtitleStyle, getSubtitleStyle: options.getSubtitleStyle,
saveSubtitlePosition: options.saveSubtitlePosition, saveSubtitlePosition: options.saveSubtitlePosition,
getMecabStatus: () => {
const mecabTokenizer = options.getMecabTokenizer();
return mecabTokenizer
? mecabTokenizer.getStatus()
: { available: false, enabled: false, path: null };
},
setMecabEnabled: (enabled) => {
const mecabTokenizer = options.getMecabTokenizer();
if (!mecabTokenizer) return;
mecabTokenizer.setEnabled(enabled);
},
handleMpvCommand: options.handleMpvCommand, handleMpvCommand: options.handleMpvCommand,
getKeybindings: options.getKeybindings,
getMpvInputBindings: options.getMpvInputBindings, getMpvInputBindings: options.getMpvInputBindings,
getSessionBindings: options.getSessionBindings ?? (() => []), getSessionBindings: options.getSessionBindings ?? (() => []),
getConfiguredShortcuts: options.getConfiguredShortcuts, getConfiguredShortcuts: options.getConfiguredShortcuts,
@@ -486,7 +430,6 @@ export function createIpcDepsRuntime(options: IpcDepsRuntimeOptions): IpcService
getOverlayNotificationPosition: options.getOverlayNotificationPosition, getOverlayNotificationPosition: options.getOverlayNotificationPosition,
getControllerConfig: options.getControllerConfig, getControllerConfig: options.getControllerConfig,
saveControllerConfig: options.saveControllerConfig, saveControllerConfig: options.saveControllerConfig,
saveControllerPreference: options.saveControllerPreference,
getSecondarySubMode: options.getSecondarySubMode, getSecondarySubMode: options.getSecondarySubMode,
getCurrentSecondarySub: () => options.getMpvClient()?.currentSecondarySubText || '', getCurrentSecondarySub: () => options.getMpvClient()?.currentSecondarySubText || '',
focusMainWindow: () => { focusMainWindow: () => {
@@ -503,10 +446,8 @@ export function createIpcDepsRuntime(options: IpcDepsRuntimeOptions): IpcService
getMediaTimingReviewWaveform: options.getMediaTimingReviewWaveform, getMediaTimingReviewWaveform: options.getMediaTimingReviewWaveform,
stopMediaTimingReviewPreview: options.stopMediaTimingReviewPreview, stopMediaTimingReviewPreview: options.stopMediaTimingReviewPreview,
resolveMediaTimingReview: options.resolveMediaTimingReview, resolveMediaTimingReview: options.resolveMediaTimingReview,
getAnkiConnectStatus: options.getAnkiConnectStatus,
getRuntimeOptions: options.getRuntimeOptions, getRuntimeOptions: options.getRuntimeOptions,
setRuntimeOption: options.setRuntimeOption, setRuntimeOption: options.setRuntimeOption,
cycleRuntimeOption: options.cycleRuntimeOption,
reportOverlayContentBounds: (payload, senderWindow) => { reportOverlayContentBounds: (payload, senderWindow) => {
const mainWindow = options.getMainWindow(); const mainWindow = options.getMainWindow();
if (!mainWindow || mainWindow.isDestroyed()) return; if (!mainWindow || mainWindow.isDestroyed()) return;
@@ -553,7 +494,6 @@ export function createIpcDepsRuntime(options: IpcDepsRuntimeOptions): IpcService
message: 'Character dictionary manager unavailable.', message: 'Character dictionary manager unavailable.',
entries: [], entries: [],
})), })),
appendClipboardVideoToQueue: options.appendClipboardVideoToQueue,
getChangelogSnapshot: options.getChangelogSnapshot, getChangelogSnapshot: options.getChangelogSnapshot,
getPlaylistBrowserSnapshot: options.getPlaylistBrowserSnapshot, getPlaylistBrowserSnapshot: options.getPlaylistBrowserSnapshot,
appendPlaylistBrowserFile: options.appendPlaylistBrowserFile, appendPlaylistBrowserFile: options.appendPlaylistBrowserFile,
@@ -680,10 +620,6 @@ export function registerIpcHandlers(deps: IpcServiceDeps, ipc: IpcMainRegistrar
}, },
); );
ipc.on(IPC_CHANNELS.command.openYomitanSettings, () => {
deps.openYomitanSettings();
});
ipc.on(IPC_CHANNELS.command.recordYomitanLookup, (_event: unknown, payload: unknown) => { ipc.on(IPC_CHANNELS.command.recordYomitanLookup, (_event: unknown, payload: unknown) => {
try { try {
deps.recordSubtitleMiningContext?.(parseSubtitleMiningContext(payload)); deps.recordSubtitleMiningContext?.(parseSubtitleMiningContext(payload));
@@ -711,22 +647,10 @@ export function registerIpcHandlers(deps: IpcServiceDeps, ipc: IpcMainRegistrar
return marked; return marked;
}); });
ipc.on(IPC_CHANNELS.command.quitApp, () => {
deps.quitApp();
});
ipc.on(IPC_CHANNELS.command.toggleDevTools, () => { ipc.on(IPC_CHANNELS.command.toggleDevTools, () => {
deps.toggleDevTools(); deps.toggleDevTools();
}); });
ipc.on(IPC_CHANNELS.command.toggleOverlay, () => {
deps.toggleVisibleOverlay();
});
ipc.handle(IPC_CHANNELS.request.getVisibleOverlayVisibility, () => {
return deps.getVisibleOverlayVisibility();
});
ipc.handle(IPC_CHANNELS.request.getCurrentSubtitle, async () => { ipc.handle(IPC_CHANNELS.request.getCurrentSubtitle, async () => {
return await deps.tokenizeCurrentSubtitle(); return await deps.tokenizeCurrentSubtitle();
}); });
@@ -735,10 +659,6 @@ export function registerIpcHandlers(deps: IpcServiceDeps, ipc: IpcMainRegistrar
return deps.getCurrentSubtitleRaw(); return deps.getCurrentSubtitleRaw();
}); });
ipc.handle(IPC_CHANNELS.request.getCurrentSubtitleAss, () => {
return deps.getCurrentSubtitleAss();
});
ipc.handle(IPC_CHANNELS.request.getSubtitleSidebarSnapshot, async () => { ipc.handle(IPC_CHANNELS.request.getSubtitleSidebarSnapshot, async () => {
if (!deps.getSubtitleSidebarSnapshot) { if (!deps.getSubtitleSidebarSnapshot) {
throw new Error('Subtitle sidebar snapshot is unavailable.'); throw new Error('Subtitle sidebar snapshot is unavailable.');
@@ -775,17 +695,6 @@ export function registerIpcHandlers(deps: IpcServiceDeps, ipc: IpcMainRegistrar
deps.saveSubtitlePosition(parsedPosition); deps.saveSubtitlePosition(parsedPosition);
}); });
ipc.handle(
IPC_CHANNELS.command.saveControllerPreference,
async (_event: unknown, update: unknown) => {
const parsedUpdate = parseControllerPreferenceUpdate(update);
if (!parsedUpdate) {
throw new Error('Invalid controller preference payload');
}
await deps.saveControllerPreference(parsedUpdate);
},
);
ipc.handle( ipc.handle(
IPC_CHANNELS.command.saveControllerConfig, IPC_CHANNELS.command.saveControllerConfig,
async (_event: unknown, update: unknown) => { async (_event: unknown, update: unknown) => {
@@ -797,15 +706,6 @@ export function registerIpcHandlers(deps: IpcServiceDeps, ipc: IpcMainRegistrar
}, },
); );
ipc.handle(IPC_CHANNELS.request.getMecabStatus, () => {
return deps.getMecabStatus();
});
ipc.on(IPC_CHANNELS.command.setMecabEnabled, (_event: unknown, enabled: unknown) => {
if (typeof enabled !== 'boolean') return;
deps.setMecabEnabled(enabled);
});
ipc.on(IPC_CHANNELS.command.mpvCommand, (_event: unknown, command: unknown) => { ipc.on(IPC_CHANNELS.command.mpvCommand, (_event: unknown, command: unknown) => {
const parsedCommand = parseMpvCommand(command); const parsedCommand = parseMpvCommand(command);
if (!parsedCommand) return; if (!parsedCommand) return;
@@ -823,10 +723,6 @@ export function registerIpcHandlers(deps: IpcServiceDeps, ipc: IpcMainRegistrar
}, },
); );
ipc.handle(IPC_CHANNELS.request.getKeybindings, () => {
return deps.getKeybindings();
});
ipc.handle(IPC_CHANNELS.request.getMpvInputBindings, () => { ipc.handle(IPC_CHANNELS.request.getMpvInputBindings, () => {
return deps.getMpvInputBindings?.() ?? { keys: [], blockedKeys: [] }; return deps.getMpvInputBindings?.() ?? { keys: [], blockedKeys: [] };
}); });
@@ -879,10 +775,6 @@ export function registerIpcHandlers(deps: IpcServiceDeps, ipc: IpcMainRegistrar
return await deps.runSubsyncManual(parsedRequest); return await deps.runSubsyncManual(parsedRequest);
}); });
ipc.handle(IPC_CHANNELS.request.getAnkiConnectStatus, () => {
return deps.getAnkiConnectStatus();
});
ipc.handle(IPC_CHANNELS.request.getRuntimeOptions, () => { ipc.handle(IPC_CHANNELS.request.getRuntimeOptions, () => {
return deps.getRuntimeOptions(); return deps.getRuntimeOptions();
}); });
@@ -899,18 +791,6 @@ export function registerIpcHandlers(deps: IpcServiceDeps, ipc: IpcMainRegistrar
return deps.setRuntimeOption(parsedId, parsedValue); return deps.setRuntimeOption(parsedId, parsedValue);
}); });
ipc.handle(IPC_CHANNELS.request.cycleRuntimeOption, (_event, id: unknown, direction: unknown) => {
const parsedId = parseRuntimeOptionId(id);
if (!parsedId) {
return { ok: false, error: 'Invalid runtime option id' };
}
const parsedDirection = parseRuntimeOptionDirection(direction);
if (!parsedDirection) {
return { ok: false, error: 'Invalid runtime option cycle direction' };
}
return deps.cycleRuntimeOption(parsedId, parsedDirection);
});
ipc.on(IPC_CHANNELS.command.reportOverlayContentBounds, (event: unknown, payload: unknown) => { ipc.on(IPC_CHANNELS.command.reportOverlayContentBounds, (event: unknown, payload: unknown) => {
const senderWindow = const senderWindow =
electron.BrowserWindow?.fromWebContents((event as IpcMainEvent).sender) ?? null; electron.BrowserWindow?.fromWebContents((event as IpcMainEvent).sender) ?? null;
@@ -1020,10 +900,6 @@ export function registerIpcHandlers(deps: IpcServiceDeps, ipc: IpcMainRegistrar
}, },
); );
ipc.handle(IPC_CHANNELS.request.appendClipboardVideoToQueue, () => {
return deps.appendClipboardVideoToQueue();
});
ipc.handle(IPC_CHANNELS.request.getChangelogSnapshot, async (_event, payload: unknown) => { ipc.handle(IPC_CHANNELS.request.getChangelogSnapshot, async (_event, payload: unknown) => {
const refresh = const refresh =
typeof payload === 'object' && payload !== null && 'refresh' in payload typeof payload === 'object' && payload !== null && 'refresh' in payload
-6
View File
@@ -20,7 +20,6 @@ import {
MPV_REQUEST_ID_SUB_SHADOW_OFFSET, MPV_REQUEST_ID_SUB_SHADOW_OFFSET,
MPV_REQUEST_ID_SUB_SPACING, MPV_REQUEST_ID_SUB_SPACING,
MPV_REQUEST_ID_SUBTEXT, MPV_REQUEST_ID_SUBTEXT,
MPV_REQUEST_ID_SUBTEXT_ASS,
MPV_REQUEST_ID_SUB_USE_MARGINS, MPV_REQUEST_ID_SUB_USE_MARGINS,
MPV_REQUEST_ID_PAUSE, MPV_REQUEST_ID_PAUSE,
} from './mpv-protocol'; } from './mpv-protocol';
@@ -54,7 +53,6 @@ const MPV_SUBTITLE_PROPERTY_OBSERVATIONS: string[] = [
'sub-scale-by-window', 'sub-scale-by-window',
'osd-height', 'osd-height',
'osd-dimensions', 'osd-dimensions',
'sub-text/ass',
'sub-border-size', 'sub-border-size',
'sub-shadow-offset', 'sub-shadow-offset',
'sub-ass-override', 'sub-ass-override',
@@ -76,10 +74,6 @@ const MPV_INITIAL_PROPERTY_REQUESTS: Array<MpvProtocolCommand> = [
command: ['get_property', 'sub-text'], command: ['get_property', 'sub-text'],
request_id: MPV_REQUEST_ID_SUBTEXT, request_id: MPV_REQUEST_ID_SUBTEXT,
}, },
{
command: ['get_property', 'sub-text/ass'],
request_id: MPV_REQUEST_ID_SUBTEXT_ASS,
},
{ {
command: ['get_property', 'path'], command: ['get_property', 'path'],
request_id: MPV_REQUEST_ID_PATH, request_id: MPV_REQUEST_ID_PATH,
-23
View File
@@ -59,7 +59,6 @@ function createDeps(overrides: Partial<MpvProtocolHandleMessageDeps> = {}): {
getSubtitleMetrics: () => metrics, getSubtitleMetrics: () => metrics,
isVisibleOverlayVisible: () => false, isVisibleOverlayVisible: () => false,
emitSubtitleChange: (payload) => state.events.push(payload), emitSubtitleChange: (payload) => state.events.push(payload),
emitSubtitleAssChange: (payload) => state.events.push(payload),
emitSubtitleTiming: (payload) => state.events.push(payload), emitSubtitleTiming: (payload) => state.events.push(payload),
emitSecondarySubtitleChange: (payload) => state.events.push(payload), emitSecondarySubtitleChange: (payload) => state.events.push(payload),
emitSubtitleTrackChange: (payload) => state.events.push(payload), emitSubtitleTrackChange: (payload) => state.events.push(payload),
@@ -132,28 +131,6 @@ test('dispatchMpvProtocolMessage emits subtitle text on property change', async
assert.deepEqual(state.events, [{ text: '字幕', isOverlayVisible: false }]); assert.deepEqual(state.events, [{ text: '字幕', isOverlayVisible: false }]);
}); });
test('dispatchMpvProtocolMessage emits ASS subtitle text from the current mpv property', async () => {
const { deps, state } = createDeps();
await dispatchMpvProtocolMessage(
{ event: 'property-change', name: 'sub-text/ass', data: '{\\b1}字幕' },
deps,
);
assert.deepEqual(state.events, [{ text: '{\\b1}字幕' }]);
});
test('dispatchMpvProtocolMessage emits ASS subtitle text from the legacy mpv property', async () => {
const { deps, state } = createDeps();
await dispatchMpvProtocolMessage(
{ event: 'property-change', name: 'sub-text-ass', data: '{\\b1}字幕' },
deps,
);
assert.deepEqual(state.events, [{ text: '{\\b1}字幕' }]);
});
test('dispatchMpvProtocolMessage emits subtitle track changes', async () => { test('dispatchMpvProtocolMessage emits subtitle track changes', async () => {
const { deps, state } = createDeps({ const { deps, state } = createDeps({
emitSubtitleTrackChange: (payload) => state.events.push(payload), emitSubtitleTrackChange: (payload) => state.events.push(payload),
-6
View File
@@ -24,7 +24,6 @@ export const MPV_REQUEST_ID_SUB_FONT = 111;
export const MPV_REQUEST_ID_SUB_SCALE_BY_WINDOW = 112; export const MPV_REQUEST_ID_SUB_SCALE_BY_WINDOW = 112;
export const MPV_REQUEST_ID_OSD_HEIGHT = 113; export const MPV_REQUEST_ID_OSD_HEIGHT = 113;
export const MPV_REQUEST_ID_OSD_DIMENSIONS = 114; export const MPV_REQUEST_ID_OSD_DIMENSIONS = 114;
export const MPV_REQUEST_ID_SUBTEXT_ASS = 115;
export const MPV_REQUEST_ID_SUB_SPACING = 116; export const MPV_REQUEST_ID_SUB_SPACING = 116;
export const MPV_REQUEST_ID_SUB_BOLD = 117; export const MPV_REQUEST_ID_SUB_BOLD = 117;
export const MPV_REQUEST_ID_SUB_ITALIC = 118; export const MPV_REQUEST_ID_SUB_ITALIC = 118;
@@ -52,7 +51,6 @@ export interface MpvProtocolHandleMessageDeps {
getSubtitleMetrics: () => MpvSubtitleRenderMetrics; getSubtitleMetrics: () => MpvSubtitleRenderMetrics;
isVisibleOverlayVisible: () => boolean; isVisibleOverlayVisible: () => boolean;
emitSubtitleChange: (payload: { text: string; isOverlayVisible: boolean }) => void; emitSubtitleChange: (payload: { text: string; isOverlayVisible: boolean }) => void;
emitSubtitleAssChange: (payload: { text: string }) => void;
emitSubtitleTiming: (payload: { text: string; start: number; end: number }) => void; emitSubtitleTiming: (payload: { text: string; start: number; end: number }) => void;
emitSecondarySubtitleChange: (payload: { text: string }) => void; emitSecondarySubtitleChange: (payload: { text: string }) => void;
emitSubtitleTrackChange: (payload: { sid: number | null }) => void; emitSubtitleTrackChange: (payload: { sid: number | null }) => void;
@@ -253,8 +251,6 @@ export async function dispatchMpvProtocolMessage(
isOverlayVisible: overlayVisible, isOverlayVisible: overlayVisible,
}); });
deps.setCurrentSubText(nextSubText); deps.setCurrentSubText(nextSubText);
} else if (msg.name === 'sub-text/ass' || msg.name === 'sub-text-ass') {
deps.emitSubtitleAssChange({ text: (msg.data as string) || '' });
} else if (msg.name === 'sub-start') { } else if (msg.name === 'sub-start') {
deps.setCurrentSubStart((msg.data as number) || 0); deps.setCurrentSubStart((msg.data as number) || 0);
deps.emitSubtitleTiming({ deps.emitSubtitleTiming({
@@ -468,8 +464,6 @@ export async function dispatchMpvProtocolMessage(
text: nextSubText, text: nextSubText,
isOverlayVisible: deps.isVisibleOverlayVisible(), isOverlayVisible: deps.isVisibleOverlayVisible(),
}); });
} else if (msg.request_id === MPV_REQUEST_ID_SUBTEXT_ASS) {
deps.emitSubtitleAssChange({ text: (msg.data as string) || '' });
} else if (msg.request_id === MPV_REQUEST_ID_PATH) { } else if (msg.request_id === MPV_REQUEST_ID_PATH) {
deps.emitMediaPathChange({ path: (msg.data as string) || '' }); deps.emitMediaPathChange({ path: (msg.data as string) || '' });
} else if (msg.request_id === MPV_REQUEST_ID_MEDIA_TITLE) { } else if (msg.request_id === MPV_REQUEST_ID_MEDIA_TITLE) {
-13
View File
@@ -444,17 +444,6 @@ test('MpvIpcClient reconnect replays property subscriptions and initial state re
(command as { command: unknown[] }).command[1] === 1 && (command as { command: unknown[] }).command[1] === 1 &&
(command as { command: unknown[] }).command[2] === 'sub-text', (command as { command: unknown[] }).command[2] === 'sub-text',
); );
const hasAssSubtitleSubscription = commands.some(
(command) =>
Array.isArray((command as { command: unknown[] }).command) &&
(command as { command: unknown[] }).command[0] === 'observe_property' &&
(command as { command: unknown[] }).command[2] === 'sub-text/ass',
);
const hasDeprecatedAssSubtitleProperty = commands.some(
(command) =>
Array.isArray((command as { command: unknown[] }).command) &&
(command as { command: unknown[] }).command.includes('sub-text-ass'),
);
const hasPathRequest = commands.some( const hasPathRequest = commands.some(
(command) => (command) =>
Array.isArray((command as { command: unknown[] }).command) && Array.isArray((command as { command: unknown[] }).command) &&
@@ -464,8 +453,6 @@ test('MpvIpcClient reconnect replays property subscriptions and initial state re
assert.equal(hasSecondaryVisibilityReset, true); assert.equal(hasSecondaryVisibilityReset, true);
assert.equal(hasTrackSubscription, true); assert.equal(hasTrackSubscription, true);
assert.equal(hasAssSubtitleSubscription, true);
assert.equal(hasDeprecatedAssSubtitleProperty, false);
assert.equal(hasPathRequest, true); assert.equal(hasPathRequest, true);
}); });
-4
View File
@@ -123,7 +123,6 @@ export interface MpvIpcClientDeps extends MpvIpcClientProtocolDeps {}
export interface MpvIpcClientEventMap { export interface MpvIpcClientEventMap {
'connection-change': { connected: boolean }; 'connection-change': { connected: boolean };
'subtitle-change': { text: string; isOverlayVisible: boolean }; 'subtitle-change': { text: string; isOverlayVisible: boolean };
'subtitle-ass-change': { text: string };
'subtitle-timing': { text: string; start: number; end: number }; 'subtitle-timing': { text: string; start: number; end: number };
'time-pos-change': { time: number }; 'time-pos-change': { time: number };
'duration-change': { duration: number }; 'duration-change': { duration: number };
@@ -417,9 +416,6 @@ export class MpvIpcClient implements MpvClient {
emitSubtitleChange: (payload) => { emitSubtitleChange: (payload) => {
this.emit('subtitle-change', payload); this.emit('subtitle-change', payload);
}, },
emitSubtitleAssChange: (payload) => {
this.emit('subtitle-ass-change', payload);
},
emitSubtitleTiming: (payload) => { emitSubtitleTiming: (payload) => {
this.emit('subtitle-timing', payload); this.emit('subtitle-timing', payload);
}, },
@@ -2,7 +2,6 @@ import test from 'node:test';
import assert from 'node:assert/strict'; import assert from 'node:assert/strict';
import { import {
applyRuntimeOptionResultRuntime, applyRuntimeOptionResultRuntime,
cycleRuntimeOptionFromIpcRuntime,
setRuntimeOptionFromIpcRuntime, setRuntimeOptionFromIpcRuntime,
} from './runtime-options-ipc'; } from './runtime-options-ipc';
@@ -25,21 +24,3 @@ test('setRuntimeOptionFromIpcRuntime returns unavailable when manager missing',
assert.equal(result.error, 'Runtime options manager unavailable'); assert.equal(result.error, 'Runtime options manager unavailable');
assert.deepEqual(osd, []); assert.deepEqual(osd, []);
}); });
test('cycleRuntimeOptionFromIpcRuntime reports errors once', () => {
const osd: string[] = [];
const result = cycleRuntimeOptionFromIpcRuntime(
{
setOptionValue: () => ({ ok: true }),
cycleOption: () => ({ ok: false, error: 'bad option' }),
},
'anki.kikuFieldGrouping',
1,
(text) => {
osd.push(text);
},
);
assert.equal(result.ok, false);
assert.equal(result.error, 'bad option');
assert.deepEqual(osd, ['bad option']);
});
-16
View File
@@ -34,19 +34,3 @@ export function setRuntimeOptionFromIpcRuntime(
} }
return result; return result;
} }
export function cycleRuntimeOptionFromIpcRuntime(
manager: RuntimeOptionsManagerLike | null,
id: RuntimeOptionId,
direction: 1 | -1,
showMpvOsd: (text: string) => void,
): RuntimeOptionApplyResult {
if (!manager) {
return { ok: false, error: 'Runtime options manager unavailable' };
}
const result = applyRuntimeOptionResultRuntime(manager.cycleOption(id, direction), showMpvOsd);
if (!result.ok && result.error) {
showMpvOsd(result.error);
}
return result;
}
-50
View File
@@ -4767,7 +4767,6 @@ const {
autoplaySubtitlePrimingRuntime.resetAutoplaySubtitlePrime(); autoplaySubtitlePrimingRuntime.resetAutoplaySubtitlePrime();
lastObservedTimePos = 0; lastObservedTimePos = 0;
appState.currentSubText = ''; appState.currentSubText = '';
appState.currentSubAssText = '';
appState.currentSubtitleData = null; appState.currentSubtitleData = null;
if (!preserveParsedSubtitleCues) { if (!preserveParsedSubtitleCues) {
appState.activeParsedSubtitleCues = []; appState.activeParsedSubtitleCues = [];
@@ -5723,7 +5722,6 @@ const { registerIpcRuntimeHandlers } = composeIpcRuntimeHandlers({
stopMediaTimingReviewPreview: (reviewId) => mediaTimingReviewRuntime.stopPreview(reviewId), stopMediaTimingReviewPreview: (reviewId) => mediaTimingReviewRuntime.stopPreview(reviewId),
resolveMediaTimingReview: (request) => mediaTimingReviewRuntime.resolveReview(request), resolveMediaTimingReview: (request) => mediaTimingReviewRuntime.resolveReview(request),
getMainWindow: () => overlayManager.getMainWindow(), getMainWindow: () => overlayManager.getMainWindow(),
getVisibleOverlayVisibility: () => overlayManager.getVisibleOverlayVisible(),
focusMainWindow: () => { focusMainWindow: () => {
const mainWindow = overlayManager.getMainWindow(); const mainWindow = overlayManager.getMainWindow();
if (!mainWindow || mainWindow.isDestroyed()) return; if (!mainWindow || mainWindow.isDestroyed()) return;
@@ -5837,14 +5835,11 @@ const { registerIpcRuntimeHandlers } = composeIpcRuntimeHandlers({
} }
}, },
onYoutubePickerResolve: (request) => youtubeFlowRuntime.resolveActivePicker(request), onYoutubePickerResolve: (request) => youtubeFlowRuntime.resolveActivePicker(request),
openYomitanSettings: () => openYomitanSettings(),
// Overlay lookups carry no cue context of their own; fall back to snapshotting the // Overlay lookups carry no cue context of their own; fall back to snapshotting the
// live mpv sub timings at lookup time so media generation clips the mined line even // live mpv sub timings at lookup time so media generation clips the mined line even
// when extraction finishes long after playback has moved on. // when extraction finishes long after playback has moved on.
recordSubtitleMiningContext: (context) => recordSubtitleMiningContext: (context) =>
recordSubtitleMiningContext(context ?? captureCurrentPrimarySubtitleMiningContext()), recordSubtitleMiningContext(context ?? captureCurrentPrimarySubtitleMiningContext()),
quitApp: () => requestAppQuit(),
toggleVisibleOverlay: () => toggleVisibleOverlay(),
tokenizeCurrentSubtitle: async () => { tokenizeCurrentSubtitle: async () => {
const tokenizeSubtitleForCurrent = tokenizeSubtitleDeferred; const tokenizeSubtitleForCurrent = tokenizeSubtitleDeferred;
return resolveCurrentSubtitleForRenderer({ return resolveCurrentSubtitleForRenderer({
@@ -5862,7 +5857,6 @@ const { registerIpcRuntimeHandlers } = composeIpcRuntimeHandlers({
}); });
}, },
getCurrentSubtitleRaw: () => appState.currentSubText, getCurrentSubtitleRaw: () => appState.currentSubText,
getCurrentSubtitleAss: () => appState.currentSubAssText,
getSubtitleSidebarOpen: () => subtitleSidebarRequestedOpen, getSubtitleSidebarOpen: () => subtitleSidebarRequestedOpen,
getSubtitleSidebarPlaybackState: () => getSubtitleSidebarPlaybackState(), getSubtitleSidebarPlaybackState: () => getSubtitleSidebarPlaybackState(),
// Full snapshot: resolves the active source over mpv IPC and re-parses it when it changed. // Full snapshot: resolves the active source over mpv IPC and re-parses it when it changed.
@@ -5942,8 +5936,6 @@ const { registerIpcRuntimeHandlers } = composeIpcRuntimeHandlers({
return resolveSubtitleStyleForRenderer(resolvedConfig); return resolveSubtitleStyleForRenderer(resolvedConfig);
}, },
saveSubtitlePosition: (position) => saveSubtitlePosition(position), saveSubtitlePosition: (position) => saveSubtitlePosition(position),
getMecabTokenizer: () => appState.mecabTokenizer,
getKeybindings: () => appState.keybindings,
getMpvInputBindings: async () => { getMpvInputBindings: async () => {
await refreshMpvSessionBindings(); await refreshMpvSessionBindings();
return readMpvInputBindings({ return readMpvInputBindings({
@@ -5973,17 +5965,8 @@ const { registerIpcRuntimeHandlers } = composeIpcRuntimeHandlers({
controller: applyControllerConfigUpdate(currentRawConfig.controller, update), controller: applyControllerConfigUpdate(currentRawConfig.controller, update),
}); });
}, },
saveControllerPreference: ({ preferredGamepadId, preferredGamepadLabel }) => {
configService.patchRawConfig({
controller: {
preferredGamepadId,
preferredGamepadLabel,
},
});
},
getSecondarySubMode: () => appState.secondarySubMode, getSecondarySubMode: () => appState.secondarySubMode,
getMpvClient: () => appState.mpvClient, getMpvClient: () => appState.mpvClient,
getAnkiConnectStatus: () => appState.ankiIntegration !== null,
getRuntimeOptions: () => getRuntimeOptionsStateHandler(), getRuntimeOptions: () => getRuntimeOptionsStateHandler(),
reportOverlayContentBounds: (payload: unknown) => { reportOverlayContentBounds: (payload: unknown) => {
if (overlayContentMeasurementStore.report(payload)) { if (overlayContentMeasurementStore.report(payload)) {
@@ -6071,47 +6054,14 @@ const { registerIpcRuntimeHandlers } = composeIpcRuntimeHandlers({
} }
return result; return result;
}, },
appendClipboardVideoToQueue: () => appendClipboardVideoToQueueHandler(),
getChangelogSnapshot: (options) => getChangelogSnapshot(options), getChangelogSnapshot: (options) => getChangelogSnapshot(options),
...playlistBrowserMainDeps, ...playlistBrowserMainDeps,
getImmersionTracker: () => appState.immersionTracker, getImmersionTracker: () => appState.immersionTracker,
}, },
ankiJimakuDeps: createAnkiJimakuIpcRuntimeServiceDeps({ ankiJimakuDeps: createAnkiJimakuIpcRuntimeServiceDeps({
patchAnkiConnectEnabled: (enabled: boolean) => {
configService.patchRawConfig({ ankiConnect: { enabled } });
},
getResolvedConfig: () => configService.getConfig(), getResolvedConfig: () => configService.getConfig(),
getRuntimeOptionsManager: () => appState.runtimeOptionsManager,
getSubtitleTimingTracker: () => appState.subtitleTimingTracker,
getMpvClient: () => appState.mpvClient, getMpvClient: () => appState.mpvClient,
getAnkiIntegration: () => appState.ankiIntegration, getAnkiIntegration: () => appState.ankiIntegration,
setAnkiIntegration: (integration: AnkiIntegration | null) => {
appState.ankiIntegration = integration;
appState.ankiIntegration?.setRecordCardsMinedCallback(recordTrackedCardsMined);
appState.ankiIntegration?.setKnownWordCacheUpdatedCallback(
refreshCurrentSubtitleAnnotations,
);
appState.ankiIntegration?.setSubtitleMiningContextConsumer(
consumePendingSubtitleMiningContext,
);
appState.ankiIntegration?.setMediaTimingReviewCallback(
mediaTimingReviewRuntime.requestReview,
);
appState.ankiIntegration?.setSentenceFuriganaGenerator(generateMiningSentenceFurigana);
},
getKnownWordCacheStatePath: () => path.join(USER_DATA_PATH, 'known-words-cache.json'),
getCachedMediaPath: (currentVideoPath, kind) =>
getCachedYoutubeMediaPathForCurrentPlayback(currentVideoPath, kind),
shouldRequireRemoteMediaCache: () => shouldRequireYoutubeMediaCacheForCurrentPlayback(),
getYoutubeMediaSourceUrl: () => getCurrentYoutubeMediaCacheSourceUrl(),
showDesktopNotification,
showOverlayNotification: (payload) =>
overlayNotificationsRuntime.showOverlayNotification(payload),
dismissOverlayNotification: (id) =>
overlayNotificationsRuntime.dismissOverlayNotification(id),
createFieldGroupingCallback: () => createFieldGroupingCallback(),
broadcastRuntimeOptionsChanged: () =>
overlayVisibilityComposer.broadcastRuntimeOptionsChanged(),
getFieldGroupingResolver: () => getFieldGroupingResolverHandler(), getFieldGroupingResolver: () => getFieldGroupingResolverHandler(),
setFieldGroupingResolver: (resolver: ((choice: KikuFieldGroupingChoice) => void) | null) => setFieldGroupingResolver: (resolver: ((choice: KikuFieldGroupingChoice) => void) | null) =>
setFieldGroupingResolverHandler(resolver), setFieldGroupingResolverHandler(resolver),
+1 -62
View File
@@ -1,15 +1,11 @@
import { RuntimeOptionId, RuntimeOptionValue, SubsyncManualPayload } from '../types'; import { RuntimeOptionId, RuntimeOptionValue, SubsyncManualPayload } from '../types';
import type { OverlayNotificationPayload } from '../types/notification';
import { SubsyncResolvedConfig } from '../subsync/utils'; import { SubsyncResolvedConfig } from '../subsync/utils';
import type { SubsyncRuntimeDeps } from '../core/services/subsync-runner'; import type { SubsyncRuntimeDeps } from '../core/services/subsync-runner';
import type { IpcDepsRuntimeOptions } from '../core/services/ipc'; import type { IpcDepsRuntimeOptions } from '../core/services/ipc';
import type { AnkiJimakuIpcRuntimeOptions } from '../core/services/anki-jimaku'; import type { AnkiJimakuIpcRuntimeOptions } from '../core/services/anki-jimaku';
import type { CliCommandDepsRuntimeOptions } from '../core/services/cli-command'; import type { CliCommandDepsRuntimeOptions } from '../core/services/cli-command';
import type { HandleMpvCommandFromIpcOptions } from '../core/services/ipc-command'; import type { HandleMpvCommandFromIpcOptions } from '../core/services/ipc-command';
import { import { setRuntimeOptionFromIpcRuntime } from '../core/services/runtime-options-ipc';
cycleRuntimeOptionFromIpcRuntime,
setRuntimeOptionFromIpcRuntime,
} from '../core/services/runtime-options-ipc';
import { RuntimeOptionsManager } from '../runtime-options'; import { RuntimeOptionsManager } from '../runtime-options';
export interface RuntimeOptionsIpcDepsParams { export interface RuntimeOptionsIpcDepsParams {
@@ -28,17 +24,12 @@ export interface SubsyncRuntimeDepsParams {
export function createRuntimeOptionsIpcDeps(params: RuntimeOptionsIpcDepsParams): { export function createRuntimeOptionsIpcDeps(params: RuntimeOptionsIpcDepsParams): {
setRuntimeOption: (id: RuntimeOptionId, value: RuntimeOptionValue) => unknown; setRuntimeOption: (id: RuntimeOptionId, value: RuntimeOptionValue) => unknown;
cycleRuntimeOption: (id: RuntimeOptionId, direction: 1 | -1) => unknown;
} { } {
return { return {
setRuntimeOption: (id, value) => setRuntimeOption: (id, value) =>
setRuntimeOptionFromIpcRuntime(params.getRuntimeOptionsManager(), id, value, (text) => setRuntimeOptionFromIpcRuntime(params.getRuntimeOptionsManager(), id, value, (text) =>
params.showMpvOsd(text), params.showMpvOsd(text),
), ),
cycleRuntimeOption: (id, direction) =>
cycleRuntimeOptionFromIpcRuntime(params.getRuntimeOptionsManager(), id, direction, (text) =>
params.showMpvOsd(text),
),
}; };
} }
@@ -55,7 +46,6 @@ export function createSubsyncRuntimeDeps(params: SubsyncRuntimeDepsParams): Subs
export interface MainIpcRuntimeServiceDepsParams { export interface MainIpcRuntimeServiceDepsParams {
getMainWindow: IpcDepsRuntimeOptions['getMainWindow']; getMainWindow: IpcDepsRuntimeOptions['getMainWindow'];
getVisibleOverlayVisibility: IpcDepsRuntimeOptions['getVisibleOverlayVisibility'];
onOverlayModalClosed: IpcDepsRuntimeOptions['onOverlayModalClosed']; onOverlayModalClosed: IpcDepsRuntimeOptions['onOverlayModalClosed'];
onOverlayModalOpened?: IpcDepsRuntimeOptions['onOverlayModalOpened']; onOverlayModalOpened?: IpcDepsRuntimeOptions['onOverlayModalOpened'];
onOverlayMouseInteractionChanged?: IpcDepsRuntimeOptions['onOverlayMouseInteractionChanged']; onOverlayMouseInteractionChanged?: IpcDepsRuntimeOptions['onOverlayMouseInteractionChanged'];
@@ -67,12 +57,8 @@ export interface MainIpcRuntimeServiceDepsParams {
getMediaTimingReviewWaveform?: IpcDepsRuntimeOptions['getMediaTimingReviewWaveform']; getMediaTimingReviewWaveform?: IpcDepsRuntimeOptions['getMediaTimingReviewWaveform'];
stopMediaTimingReviewPreview?: IpcDepsRuntimeOptions['stopMediaTimingReviewPreview']; stopMediaTimingReviewPreview?: IpcDepsRuntimeOptions['stopMediaTimingReviewPreview'];
resolveMediaTimingReview?: IpcDepsRuntimeOptions['resolveMediaTimingReview']; resolveMediaTimingReview?: IpcDepsRuntimeOptions['resolveMediaTimingReview'];
openYomitanSettings: IpcDepsRuntimeOptions['openYomitanSettings'];
quitApp: IpcDepsRuntimeOptions['quitApp'];
toggleVisibleOverlay: IpcDepsRuntimeOptions['toggleVisibleOverlay'];
tokenizeCurrentSubtitle: IpcDepsRuntimeOptions['tokenizeCurrentSubtitle']; tokenizeCurrentSubtitle: IpcDepsRuntimeOptions['tokenizeCurrentSubtitle'];
getCurrentSubtitleRaw: IpcDepsRuntimeOptions['getCurrentSubtitleRaw']; getCurrentSubtitleRaw: IpcDepsRuntimeOptions['getCurrentSubtitleRaw'];
getCurrentSubtitleAss: IpcDepsRuntimeOptions['getCurrentSubtitleAss'];
getSubtitleSidebarSnapshot?: IpcDepsRuntimeOptions['getSubtitleSidebarSnapshot']; getSubtitleSidebarSnapshot?: IpcDepsRuntimeOptions['getSubtitleSidebarSnapshot'];
getSubtitleSidebarPlaybackState?: IpcDepsRuntimeOptions['getSubtitleSidebarPlaybackState']; getSubtitleSidebarPlaybackState?: IpcDepsRuntimeOptions['getSubtitleSidebarPlaybackState'];
getSubtitleSidebarOpen?: IpcDepsRuntimeOptions['getSubtitleSidebarOpen']; getSubtitleSidebarOpen?: IpcDepsRuntimeOptions['getSubtitleSidebarOpen'];
@@ -82,9 +68,7 @@ export interface MainIpcRuntimeServiceDepsParams {
getSubtitlePosition: IpcDepsRuntimeOptions['getSubtitlePosition']; getSubtitlePosition: IpcDepsRuntimeOptions['getSubtitlePosition'];
getSubtitleStyle: IpcDepsRuntimeOptions['getSubtitleStyle']; getSubtitleStyle: IpcDepsRuntimeOptions['getSubtitleStyle'];
saveSubtitlePosition: IpcDepsRuntimeOptions['saveSubtitlePosition']; saveSubtitlePosition: IpcDepsRuntimeOptions['saveSubtitlePosition'];
getMecabTokenizer: IpcDepsRuntimeOptions['getMecabTokenizer'];
handleMpvCommand: IpcDepsRuntimeOptions['handleMpvCommand']; handleMpvCommand: IpcDepsRuntimeOptions['handleMpvCommand'];
getKeybindings: IpcDepsRuntimeOptions['getKeybindings'];
getMpvInputBindings?: IpcDepsRuntimeOptions['getMpvInputBindings']; getMpvInputBindings?: IpcDepsRuntimeOptions['getMpvInputBindings'];
getSessionBindings: IpcDepsRuntimeOptions['getSessionBindings']; getSessionBindings: IpcDepsRuntimeOptions['getSessionBindings'];
getConfiguredShortcuts: IpcDepsRuntimeOptions['getConfiguredShortcuts']; getConfiguredShortcuts: IpcDepsRuntimeOptions['getConfiguredShortcuts'];
@@ -94,14 +78,11 @@ export interface MainIpcRuntimeServiceDepsParams {
getOverlayNotificationPosition: IpcDepsRuntimeOptions['getOverlayNotificationPosition']; getOverlayNotificationPosition: IpcDepsRuntimeOptions['getOverlayNotificationPosition'];
getControllerConfig: IpcDepsRuntimeOptions['getControllerConfig']; getControllerConfig: IpcDepsRuntimeOptions['getControllerConfig'];
saveControllerConfig: IpcDepsRuntimeOptions['saveControllerConfig']; saveControllerConfig: IpcDepsRuntimeOptions['saveControllerConfig'];
saveControllerPreference: IpcDepsRuntimeOptions['saveControllerPreference'];
getSecondarySubMode: IpcDepsRuntimeOptions['getSecondarySubMode']; getSecondarySubMode: IpcDepsRuntimeOptions['getSecondarySubMode'];
getMpvClient: IpcDepsRuntimeOptions['getMpvClient']; getMpvClient: IpcDepsRuntimeOptions['getMpvClient'];
runSubsyncManual: IpcDepsRuntimeOptions['runSubsyncManual']; runSubsyncManual: IpcDepsRuntimeOptions['runSubsyncManual'];
getAnkiConnectStatus: IpcDepsRuntimeOptions['getAnkiConnectStatus'];
getRuntimeOptions: IpcDepsRuntimeOptions['getRuntimeOptions']; getRuntimeOptions: IpcDepsRuntimeOptions['getRuntimeOptions'];
setRuntimeOption: IpcDepsRuntimeOptions['setRuntimeOption']; setRuntimeOption: IpcDepsRuntimeOptions['setRuntimeOption'];
cycleRuntimeOption: IpcDepsRuntimeOptions['cycleRuntimeOption'];
reportOverlayContentBounds: IpcDepsRuntimeOptions['reportOverlayContentBounds']; reportOverlayContentBounds: IpcDepsRuntimeOptions['reportOverlayContentBounds'];
getAnilistStatus: IpcDepsRuntimeOptions['getAnilistStatus']; getAnilistStatus: IpcDepsRuntimeOptions['getAnilistStatus'];
clearAnilistToken: IpcDepsRuntimeOptions['clearAnilistToken']; clearAnilistToken: IpcDepsRuntimeOptions['clearAnilistToken'];
@@ -115,7 +96,6 @@ export interface MainIpcRuntimeServiceDepsParams {
getCharacterDictionaryManagerSnapshot?: IpcDepsRuntimeOptions['getCharacterDictionaryManagerSnapshot']; getCharacterDictionaryManagerSnapshot?: IpcDepsRuntimeOptions['getCharacterDictionaryManagerSnapshot'];
removeCharacterDictionaryManagedEntry?: IpcDepsRuntimeOptions['removeCharacterDictionaryManagedEntry']; removeCharacterDictionaryManagedEntry?: IpcDepsRuntimeOptions['removeCharacterDictionaryManagedEntry'];
moveCharacterDictionaryManagedEntry?: IpcDepsRuntimeOptions['moveCharacterDictionaryManagedEntry']; moveCharacterDictionaryManagedEntry?: IpcDepsRuntimeOptions['moveCharacterDictionaryManagedEntry'];
appendClipboardVideoToQueue: IpcDepsRuntimeOptions['appendClipboardVideoToQueue'];
getChangelogSnapshot?: IpcDepsRuntimeOptions['getChangelogSnapshot']; getChangelogSnapshot?: IpcDepsRuntimeOptions['getChangelogSnapshot'];
getPlaylistBrowserSnapshot: IpcDepsRuntimeOptions['getPlaylistBrowserSnapshot']; getPlaylistBrowserSnapshot: IpcDepsRuntimeOptions['getPlaylistBrowserSnapshot'];
appendPlaylistBrowserFile: IpcDepsRuntimeOptions['appendPlaylistBrowserFile']; appendPlaylistBrowserFile: IpcDepsRuntimeOptions['appendPlaylistBrowserFile'];
@@ -126,22 +106,9 @@ export interface MainIpcRuntimeServiceDepsParams {
} }
export interface AnkiJimakuIpcRuntimeServiceDepsParams { export interface AnkiJimakuIpcRuntimeServiceDepsParams {
patchAnkiConnectEnabled: AnkiJimakuIpcRuntimeOptions['patchAnkiConnectEnabled'];
getResolvedConfig: AnkiJimakuIpcRuntimeOptions['getResolvedConfig']; getResolvedConfig: AnkiJimakuIpcRuntimeOptions['getResolvedConfig'];
getRuntimeOptionsManager: AnkiJimakuIpcRuntimeOptions['getRuntimeOptionsManager'];
getSubtitleTimingTracker: AnkiJimakuIpcRuntimeOptions['getSubtitleTimingTracker'];
getMpvClient: AnkiJimakuIpcRuntimeOptions['getMpvClient']; getMpvClient: AnkiJimakuIpcRuntimeOptions['getMpvClient'];
getAnkiIntegration: AnkiJimakuIpcRuntimeOptions['getAnkiIntegration']; getAnkiIntegration: AnkiJimakuIpcRuntimeOptions['getAnkiIntegration'];
setAnkiIntegration: AnkiJimakuIpcRuntimeOptions['setAnkiIntegration'];
getKnownWordCacheStatePath: AnkiJimakuIpcRuntimeOptions['getKnownWordCacheStatePath'];
getCachedMediaPath?: AnkiJimakuIpcRuntimeOptions['getCachedMediaPath'];
shouldRequireRemoteMediaCache?: AnkiJimakuIpcRuntimeOptions['shouldRequireRemoteMediaCache'];
getYoutubeMediaSourceUrl?: AnkiJimakuIpcRuntimeOptions['getYoutubeMediaSourceUrl'];
showDesktopNotification: AnkiJimakuIpcRuntimeOptions['showDesktopNotification'];
showOverlayNotification?: (payload: OverlayNotificationPayload) => void;
dismissOverlayNotification?: (id: string) => void;
createFieldGroupingCallback: AnkiJimakuIpcRuntimeOptions['createFieldGroupingCallback'];
broadcastRuntimeOptionsChanged: AnkiJimakuIpcRuntimeOptions['broadcastRuntimeOptionsChanged'];
getFieldGroupingResolver: AnkiJimakuIpcRuntimeOptions['getFieldGroupingResolver']; getFieldGroupingResolver: AnkiJimakuIpcRuntimeOptions['getFieldGroupingResolver'];
setFieldGroupingResolver: AnkiJimakuIpcRuntimeOptions['setFieldGroupingResolver']; setFieldGroupingResolver: AnkiJimakuIpcRuntimeOptions['setFieldGroupingResolver'];
parseMediaInfo: AnkiJimakuIpcRuntimeOptions['parseMediaInfo']; parseMediaInfo: AnkiJimakuIpcRuntimeOptions['parseMediaInfo'];
@@ -258,7 +225,6 @@ export function createMainIpcRuntimeServiceDeps(
): IpcDepsRuntimeOptions { ): IpcDepsRuntimeOptions {
return { return {
getMainWindow: params.getMainWindow, getMainWindow: params.getMainWindow,
getVisibleOverlayVisibility: params.getVisibleOverlayVisibility,
onOverlayModalClosed: params.onOverlayModalClosed, onOverlayModalClosed: params.onOverlayModalClosed,
onOverlayModalOpened: params.onOverlayModalOpened, onOverlayModalOpened: params.onOverlayModalOpened,
onOverlayMouseInteractionChanged: params.onOverlayMouseInteractionChanged, onOverlayMouseInteractionChanged: params.onOverlayMouseInteractionChanged,
@@ -270,12 +236,8 @@ export function createMainIpcRuntimeServiceDeps(
getMediaTimingReviewWaveform: params.getMediaTimingReviewWaveform, getMediaTimingReviewWaveform: params.getMediaTimingReviewWaveform,
stopMediaTimingReviewPreview: params.stopMediaTimingReviewPreview, stopMediaTimingReviewPreview: params.stopMediaTimingReviewPreview,
resolveMediaTimingReview: params.resolveMediaTimingReview, resolveMediaTimingReview: params.resolveMediaTimingReview,
openYomitanSettings: params.openYomitanSettings,
quitApp: params.quitApp,
toggleVisibleOverlay: params.toggleVisibleOverlay,
tokenizeCurrentSubtitle: params.tokenizeCurrentSubtitle, tokenizeCurrentSubtitle: params.tokenizeCurrentSubtitle,
getCurrentSubtitleRaw: params.getCurrentSubtitleRaw, getCurrentSubtitleRaw: params.getCurrentSubtitleRaw,
getCurrentSubtitleAss: params.getCurrentSubtitleAss,
getSubtitleSidebarSnapshot: params.getSubtitleSidebarSnapshot, getSubtitleSidebarSnapshot: params.getSubtitleSidebarSnapshot,
getSubtitleSidebarPlaybackState: params.getSubtitleSidebarPlaybackState, getSubtitleSidebarPlaybackState: params.getSubtitleSidebarPlaybackState,
getSubtitleSidebarOpen: params.getSubtitleSidebarOpen, getSubtitleSidebarOpen: params.getSubtitleSidebarOpen,
@@ -283,9 +245,7 @@ export function createMainIpcRuntimeServiceDeps(
getSubtitlePosition: params.getSubtitlePosition, getSubtitlePosition: params.getSubtitlePosition,
getSubtitleStyle: params.getSubtitleStyle, getSubtitleStyle: params.getSubtitleStyle,
saveSubtitlePosition: params.saveSubtitlePosition, saveSubtitlePosition: params.saveSubtitlePosition,
getMecabTokenizer: params.getMecabTokenizer,
handleMpvCommand: params.handleMpvCommand, handleMpvCommand: params.handleMpvCommand,
getKeybindings: params.getKeybindings,
getMpvInputBindings: params.getMpvInputBindings, getMpvInputBindings: params.getMpvInputBindings,
getSessionBindings: params.getSessionBindings, getSessionBindings: params.getSessionBindings,
getConfiguredShortcuts: params.getConfiguredShortcuts, getConfiguredShortcuts: params.getConfiguredShortcuts,
@@ -295,16 +255,13 @@ export function createMainIpcRuntimeServiceDeps(
getOverlayNotificationPosition: params.getOverlayNotificationPosition, getOverlayNotificationPosition: params.getOverlayNotificationPosition,
getControllerConfig: params.getControllerConfig, getControllerConfig: params.getControllerConfig,
saveControllerConfig: params.saveControllerConfig, saveControllerConfig: params.saveControllerConfig,
saveControllerPreference: params.saveControllerPreference,
focusMainWindow: params.focusMainWindow ?? (() => {}), focusMainWindow: params.focusMainWindow ?? (() => {}),
activatePlaybackWindowForOverlayInteraction: params.activatePlaybackWindowForOverlayInteraction, activatePlaybackWindowForOverlayInteraction: params.activatePlaybackWindowForOverlayInteraction,
getSecondarySubMode: params.getSecondarySubMode, getSecondarySubMode: params.getSecondarySubMode,
getMpvClient: params.getMpvClient, getMpvClient: params.getMpvClient,
runSubsyncManual: params.runSubsyncManual, runSubsyncManual: params.runSubsyncManual,
getAnkiConnectStatus: params.getAnkiConnectStatus,
getRuntimeOptions: params.getRuntimeOptions, getRuntimeOptions: params.getRuntimeOptions,
setRuntimeOption: params.setRuntimeOption, setRuntimeOption: params.setRuntimeOption,
cycleRuntimeOption: params.cycleRuntimeOption,
reportOverlayContentBounds: params.reportOverlayContentBounds, reportOverlayContentBounds: params.reportOverlayContentBounds,
getAnilistStatus: params.getAnilistStatus, getAnilistStatus: params.getAnilistStatus,
clearAnilistToken: params.clearAnilistToken, clearAnilistToken: params.clearAnilistToken,
@@ -318,7 +275,6 @@ export function createMainIpcRuntimeServiceDeps(
getCharacterDictionaryManagerSnapshot: params.getCharacterDictionaryManagerSnapshot, getCharacterDictionaryManagerSnapshot: params.getCharacterDictionaryManagerSnapshot,
removeCharacterDictionaryManagedEntry: params.removeCharacterDictionaryManagedEntry, removeCharacterDictionaryManagedEntry: params.removeCharacterDictionaryManagedEntry,
moveCharacterDictionaryManagedEntry: params.moveCharacterDictionaryManagedEntry, moveCharacterDictionaryManagedEntry: params.moveCharacterDictionaryManagedEntry,
appendClipboardVideoToQueue: params.appendClipboardVideoToQueue,
getChangelogSnapshot: params.getChangelogSnapshot, getChangelogSnapshot: params.getChangelogSnapshot,
getPlaylistBrowserSnapshot: params.getPlaylistBrowserSnapshot, getPlaylistBrowserSnapshot: params.getPlaylistBrowserSnapshot,
appendPlaylistBrowserFile: params.appendPlaylistBrowserFile, appendPlaylistBrowserFile: params.appendPlaylistBrowserFile,
@@ -333,26 +289,9 @@ export function createAnkiJimakuIpcRuntimeServiceDeps(
params: AnkiJimakuIpcRuntimeServiceDepsParams, params: AnkiJimakuIpcRuntimeServiceDepsParams,
): AnkiJimakuIpcRuntimeOptions { ): AnkiJimakuIpcRuntimeOptions {
return { return {
patchAnkiConnectEnabled: params.patchAnkiConnectEnabled,
getResolvedConfig: params.getResolvedConfig, getResolvedConfig: params.getResolvedConfig,
getRuntimeOptionsManager: params.getRuntimeOptionsManager,
getSubtitleTimingTracker: params.getSubtitleTimingTracker,
getMpvClient: params.getMpvClient, getMpvClient: params.getMpvClient,
getAnkiIntegration: params.getAnkiIntegration, getAnkiIntegration: params.getAnkiIntegration,
setAnkiIntegration: params.setAnkiIntegration,
getKnownWordCacheStatePath: params.getKnownWordCacheStatePath,
...(params.getCachedMediaPath ? { getCachedMediaPath: params.getCachedMediaPath } : {}),
...(params.shouldRequireRemoteMediaCache
? { shouldRequireRemoteMediaCache: params.shouldRequireRemoteMediaCache }
: {}),
...(params.getYoutubeMediaSourceUrl
? { getYoutubeMediaSourceUrl: params.getYoutubeMediaSourceUrl }
: {}),
showDesktopNotification: params.showDesktopNotification,
showOverlayNotification: params.showOverlayNotification,
dismissOverlayNotification: params.dismissOverlayNotification,
createFieldGroupingCallback: params.createFieldGroupingCallback,
broadcastRuntimeOptionsChanged: params.broadcastRuntimeOptionsChanged,
getFieldGroupingResolver: params.getFieldGroupingResolver, getFieldGroupingResolver: params.getFieldGroupingResolver,
setFieldGroupingResolver: params.setFieldGroupingResolver, setFieldGroupingResolver: params.setFieldGroupingResolver,
parseMediaInfo: params.parseMediaInfo, parseMediaInfo: params.parseMediaInfo,
+1 -2
View File
@@ -15,7 +15,7 @@ import {
export interface RegisterIpcRuntimeServicesParams { export interface RegisterIpcRuntimeServicesParams {
runtimeOptions: RuntimeOptionsIpcDepsParams; runtimeOptions: RuntimeOptionsIpcDepsParams;
mainDeps: Omit<MainIpcRuntimeServiceDepsParams, 'setRuntimeOption' | 'cycleRuntimeOption'>; mainDeps: Omit<MainIpcRuntimeServiceDepsParams, 'setRuntimeOption'>;
ankiJimakuDeps: AnkiJimakuIpcRuntimeServiceDepsParams; ankiJimakuDeps: AnkiJimakuIpcRuntimeServiceDepsParams;
} }
@@ -40,7 +40,6 @@ export function registerIpcRuntimeServices(params: RegisterIpcRuntimeServicesPar
registerMainIpcRuntimeServices({ registerMainIpcRuntimeServices({
...params.mainDeps, ...params.mainDeps,
setRuntimeOption: runtimeOptionsIpcDeps.setRuntimeOption, setRuntimeOption: runtimeOptionsIpcDeps.setRuntimeOption,
cycleRuntimeOption: runtimeOptionsIpcDeps.cycleRuntimeOption,
}); });
registerAnkiJimakuIpcRuntimeServices(params.ankiJimakuDeps); registerAnkiJimakuIpcRuntimeServices(params.ankiJimakuDeps);
} }
-1
View File
@@ -37,7 +37,6 @@ test('media path changes clear rendered subtitle state without clearing same-you
assert.ok(actionBlock); assert.ok(actionBlock);
assert.match(actionBlock, /appState\.currentSubText = '';/); assert.match(actionBlock, /appState\.currentSubText = '';/);
assert.match(actionBlock, /appState\.currentSubAssText = '';/);
assert.match(actionBlock, /appState\.currentSubtitleData = null;/); assert.match(actionBlock, /appState\.currentSubtitleData = null;/);
assert.match(actionBlock, /isSameYoutubeMediaPath\(/); assert.match(actionBlock, /isSameYoutubeMediaPath\(/);
assert.match(actionBlock, /if \(!preserveParsedSubtitleCues\)/); assert.match(actionBlock, /if \(!preserveParsedSubtitleCues\)/);
@@ -45,7 +45,6 @@ function createDefaultMpvFixture() {
immersionTracker: null, immersionTracker: null,
subtitleTimingTracker: null, subtitleTimingTracker: null,
currentSubText: '', currentSubText: '',
currentSubAssText: '',
playbackPaused: null, playbackPaused: null,
previousSecondarySubVisibility: null, previousSecondarySubVisibility: null,
}, },
@@ -1,7 +1,6 @@
type MpvBindingEventName = type MpvBindingEventName =
| 'connection-change' | 'connection-change'
| 'subtitle-change' | 'subtitle-change'
| 'subtitle-ass-change'
| 'secondary-subtitle-change' | 'secondary-subtitle-change'
| 'subtitle-track-change' | 'subtitle-track-change'
| 'secondary-subtitle-track-change' | 'secondary-subtitle-track-change'
@@ -89,7 +88,6 @@ export function createHandleMpvSubtitleTimingHandler(deps: {
export function createBindMpvClientEventHandlers(deps: { export function createBindMpvClientEventHandlers(deps: {
onConnectionChange: (payload: { connected: boolean }) => void; onConnectionChange: (payload: { connected: boolean }) => void;
onSubtitleChange: (payload: { text: string }) => void; onSubtitleChange: (payload: { text: string }) => void;
onSubtitleAssChange: (payload: { text: string }) => void;
onSecondarySubtitleChange: (payload: { text: string }) => void; onSecondarySubtitleChange: (payload: { text: string }) => void;
onSubtitleTrackChange: (payload: { sid: number | null }) => void; onSubtitleTrackChange: (payload: { sid: number | null }) => void;
onSecondarySubtitleTrackChange: (payload: { sid: number | null }) => void; onSecondarySubtitleTrackChange: (payload: { sid: number | null }) => void;
@@ -108,7 +106,6 @@ export function createBindMpvClientEventHandlers(deps: {
return (mpvClient: MpvEventClient): void => { return (mpvClient: MpvEventClient): void => {
mpvClient.on('connection-change', deps.onConnectionChange); mpvClient.on('connection-change', deps.onConnectionChange);
mpvClient.on('subtitle-change', deps.onSubtitleChange); mpvClient.on('subtitle-change', deps.onSubtitleChange);
mpvClient.on('subtitle-ass-change', deps.onSubtitleAssChange);
mpvClient.on('secondary-subtitle-change', deps.onSecondarySubtitleChange); mpvClient.on('secondary-subtitle-change', deps.onSecondarySubtitleChange);
mpvClient.on('subtitle-track-change', deps.onSubtitleTrackChange); mpvClient.on('subtitle-track-change', deps.onSubtitleTrackChange);
mpvClient.on('secondary-subtitle-track-change', deps.onSecondarySubtitleTrackChange); mpvClient.on('secondary-subtitle-track-change', deps.onSecondarySubtitleTrackChange);
@@ -49,16 +49,6 @@ export function createHandleMpvSubtitleChangeHandler(deps: {
}; };
} }
export function createHandleMpvSubtitleAssChangeHandler(deps: {
setCurrentSubAssText: (text: string) => void;
broadcastSubtitleAss: (text: string) => void;
}) {
return ({ text }: { text: string }): void => {
deps.setCurrentSubAssText(text);
deps.broadcastSubtitleAss(text);
};
}
export function createHandleMpvSecondarySubtitleChangeHandler(deps: { export function createHandleMpvSecondarySubtitleChangeHandler(deps: {
broadcastSecondarySubtitle: (text: string) => void; broadcastSecondarySubtitle: (text: string) => void;
}) { }) {
@@ -29,8 +29,6 @@ function createDeps(overrides: Partial<Deps> = {}): Deps {
onSubtitleChange: () => {}, onSubtitleChange: () => {},
refreshDiscordPresence: () => {}, refreshDiscordPresence: () => {},
setCurrentSubAssText: () => {},
broadcastSubtitleAss: () => {},
broadcastSecondarySubtitle: () => {}, broadcastSecondarySubtitle: () => {},
updateCurrentMediaPath: () => {}, updateCurrentMediaPath: () => {},
@@ -10,7 +10,6 @@ import {
createHandleMpvPauseChangeHandler, createHandleMpvPauseChangeHandler,
createHandleMpvSecondarySubtitleChangeHandler, createHandleMpvSecondarySubtitleChangeHandler,
createHandleMpvSecondarySubtitleVisibilityHandler, createHandleMpvSecondarySubtitleVisibilityHandler,
createHandleMpvSubtitleAssChangeHandler,
createHandleMpvSubtitleChangeHandler, createHandleMpvSubtitleChangeHandler,
createHandleMpvSubtitleMetricsChangeHandler, createHandleMpvSubtitleMetricsChangeHandler,
createHandleMpvTimePosChangeHandler, createHandleMpvTimePosChangeHandler,
@@ -52,8 +51,6 @@ export function createBindMpvMainEventHandlersHandler(deps: {
logSubtitleProcessingDebug?: (message: string) => void; logSubtitleProcessingDebug?: (message: string) => void;
refreshDiscordPresence: () => void; refreshDiscordPresence: () => void;
setCurrentSubAssText: (text: string) => void;
broadcastSubtitleAss: (text: string) => void;
broadcastSecondarySubtitle: (text: string) => void; broadcastSecondarySubtitle: (text: string) => void;
onSubtitleTrackChange?: (sid: number | null) => void; onSubtitleTrackChange?: (sid: number | null) => void;
onSecondarySubtitleTrackChange?: (sid: number | null) => void; onSecondarySubtitleTrackChange?: (sid: number | null) => void;
@@ -135,10 +132,6 @@ export function createBindMpvMainEventHandlersHandler(deps: {
: undefined, : undefined,
refreshDiscordPresence: () => deps.refreshDiscordPresence(), refreshDiscordPresence: () => deps.refreshDiscordPresence(),
}); });
const handleMpvSubtitleAssChange = createHandleMpvSubtitleAssChangeHandler({
setCurrentSubAssText: (text) => deps.setCurrentSubAssText(text),
broadcastSubtitleAss: (text) => deps.broadcastSubtitleAss(text),
});
const handleMpvSecondarySubtitleChange = createHandleMpvSecondarySubtitleChangeHandler({ const handleMpvSecondarySubtitleChange = createHandleMpvSecondarySubtitleChangeHandler({
broadcastSecondarySubtitle: (text) => deps.broadcastSecondarySubtitle(text), broadcastSecondarySubtitle: (text) => deps.broadcastSecondarySubtitle(text),
}); });
@@ -200,7 +193,6 @@ export function createBindMpvMainEventHandlersHandler(deps: {
createBindMpvClientEventHandlers({ createBindMpvClientEventHandlers({
onConnectionChange: handleMpvConnectionChangeWithSidebarReset, onConnectionChange: handleMpvConnectionChangeWithSidebarReset,
onSubtitleChange: handleMpvSubtitleChange, onSubtitleChange: handleMpvSubtitleChange,
onSubtitleAssChange: handleMpvSubtitleAssChange,
onSecondarySubtitleChange: handleMpvSecondarySubtitleChange, onSecondarySubtitleChange: handleMpvSecondarySubtitleChange,
onSubtitleTrackChange: ({ sid }) => deps.onSubtitleTrackChange?.(sid), onSubtitleTrackChange: ({ sid }) => deps.onSubtitleTrackChange?.(sid),
onSecondarySubtitleTrackChange: ({ sid }) => deps.onSecondarySubtitleTrackChange?.(sid), onSecondarySubtitleTrackChange: ({ sid }) => deps.onSecondarySubtitleTrackChange?.(sid),
@@ -20,7 +20,6 @@ function buildMainDeps(
immersionTracker: null, immersionTracker: null,
subtitleTimingTracker: null, subtitleTimingTracker: null,
currentSubText: '', currentSubText: '',
currentSubAssText: '',
playbackPaused: null, playbackPaused: null,
previousSecondarySubVisibility: false, previousSecondarySubVisibility: false,
...appStateOverrides, ...appStateOverrides,
@@ -46,7 +46,6 @@ export function createBuildBindMpvMainEventHandlersMainDepsHandler(deps: {
activeParsedSubtitleSource?: string | null; activeParsedSubtitleSource?: string | null;
currentMediaPath?: string | null; currentMediaPath?: string | null;
currentSubText: string; currentSubText: string;
currentSubAssText: string;
currentSubtitleData?: SubtitleData | null; currentSubtitleData?: SubtitleData | null;
playbackPaused: boolean | null; playbackPaused: boolean | null;
previousSecondarySubVisibility: boolean | null; previousSecondarySubVisibility: boolean | null;
@@ -300,11 +299,6 @@ export function createBuildBindMpvMainEventHandlersMainDepsHandler(deps: {
? (trackList: unknown[] | null) => deps.onSubtitleTrackListChange!(trackList) ? (trackList: unknown[] | null) => deps.onSubtitleTrackListChange!(trackList)
: undefined, : undefined,
refreshDiscordPresence: () => deps.refreshDiscordPresence(), refreshDiscordPresence: () => deps.refreshDiscordPresence(),
setCurrentSubAssText: (text: string) => {
deps.appState.currentSubAssText = text;
},
broadcastSubtitleAss: (text: string) =>
deps.broadcastToOverlayWindows('subtitle-ass:set', text),
broadcastSecondarySubtitle: (text: string) => { broadcastSecondarySubtitle: (text: string) => {
if (deps.onSecondarySubtitleChange) { if (deps.onSecondarySubtitleChange) {
deps.onSecondarySubtitleChange(text); deps.onSecondarySubtitleChange(text);
-2
View File
@@ -160,7 +160,6 @@ export interface AppState {
discordPresenceService: ReturnType<typeof createDiscordPresenceService> | null; discordPresenceService: ReturnType<typeof createDiscordPresenceService> | null;
reconnectTimer: ReturnType<typeof setTimeout> | null; reconnectTimer: ReturnType<typeof setTimeout> | null;
currentSubText: string; currentSubText: string;
currentSubAssText: string;
currentSubtitleData: SubtitleData | null; currentSubtitleData: SubtitleData | null;
activeParsedSubtitleCues: SubtitleCue[]; activeParsedSubtitleCues: SubtitleCue[];
activeParsedSubtitleSource: string | null; activeParsedSubtitleSource: string | null;
@@ -247,7 +246,6 @@ export function createAppState(values: AppStateInitialValues): AppState {
discordPresenceService: null, discordPresenceService: null,
reconnectTimer: null, reconnectTimer: null,
currentSubText: '', currentSubText: '',
currentSubAssText: '',
currentSubtitleData: null, currentSubtitleData: null,
activeParsedSubtitleCues: [], activeParsedSubtitleCues: [],
activeParsedSubtitleSource: null, activeParsedSubtitleSource: null,
-23
View File
@@ -399,29 +399,6 @@ export class MecabTokenizer {
path: this.mecabPath, path: this.mecabPath,
}; };
} }
setEnabled(enabled: boolean): void {
this.enabled = enabled;
if (!enabled) {
const pending: MecabQueuedRequest[] = [];
if (this.activeRequest) {
pending.push({
text: this.activeRequest.text,
retryCount: MecabTokenizer.MAX_RETRY_COUNT,
resolve: this.activeRequest.resolve,
});
}
if (this.requestQueue.length > 0) {
pending.push(...this.requestQueue);
}
this.activeRequest = null;
this.requestQueue = [];
for (const request of pending) {
request.resolve(null);
}
this.stopPersistentProcess();
}
}
} }
export { mapMecabPos1ToPartOfSpeech as mapPartOfSpeech }; export { mapMecabPos1ToPartOfSpeech as mapPartOfSpeech };
-59
View File
@@ -22,8 +22,6 @@ import { createIpcListenerFactories } from './preload-ipc-listeners';
import type { import type {
SubtitleData, SubtitleData,
SubtitlePosition, SubtitlePosition,
MecabStatus,
Keybinding,
ElectronAPI, ElectronAPI,
SecondarySubMode, SecondarySubMode,
SubtitleStyleConfig, SubtitleStyleConfig,
@@ -45,7 +43,6 @@ import type {
SubsyncManualPayload, SubsyncManualPayload,
SubsyncManualRunRequest, SubsyncManualRunRequest,
SubsyncResult, SubsyncResult,
ClipboardAppendResult,
PlaylistBrowserMutationResult, PlaylistBrowserMutationResult,
PlaylistBrowserSnapshot, PlaylistBrowserSnapshot,
KikuFieldGroupingRequestData, KikuFieldGroupingRequestData,
@@ -60,7 +57,6 @@ import type {
ShortcutsConfig, ShortcutsConfig,
ConfigHotReloadPayload, ConfigHotReloadPayload,
ControllerConfigUpdate, ControllerConfigUpdate,
ControllerPreferenceUpdate,
ResolvedControllerConfig, ResolvedControllerConfig,
SessionHelpOpenPayload, SessionHelpOpenPayload,
SessionNumericSelectionStartPayload, SessionNumericSelectionStartPayload,
@@ -176,10 +172,6 @@ const onOverlayNotificationEvent =
const onNotificationHistoryToggleEvent = createQueuedIpcListener( const onNotificationHistoryToggleEvent = createQueuedIpcListener(
IPC_CHANNELS.event.notificationHistoryToggle, IPC_CHANNELS.event.notificationHistoryToggle,
); );
const onSubtitleVisibilityEvent = createLatestValueIpcListenerWithPayload<boolean>(
IPC_CHANNELS.event.subtitleVisibility,
(payload) => payload === true,
);
const onSubtitlePositionSetEvent = createLatestValueIpcListenerWithPayload<SubtitlePosition | null>( const onSubtitlePositionSetEvent = createLatestValueIpcListenerWithPayload<SubtitlePosition | null>(
IPC_CHANNELS.event.subtitlePositionSet, IPC_CHANNELS.event.subtitlePositionSet,
(payload) => payload as SubtitlePosition | null, (payload) => payload as SubtitlePosition | null,
@@ -236,22 +228,14 @@ const electronAPI: ElectronAPI = {
}, },
onNotificationHistoryToggle: onNotificationHistoryToggleEvent, onNotificationHistoryToggle: onNotificationHistoryToggleEvent,
onVisibility: (callback: (visible: boolean) => void) => {
onSubtitleVisibilityEvent(callback);
},
onSubtitlePosition: (callback: (position: SubtitlePosition | null) => void) => { onSubtitlePosition: (callback: (position: SubtitlePosition | null) => void) => {
onSubtitlePositionSetEvent(callback); onSubtitlePositionSetEvent(callback);
}, },
getOverlayVisibility: (): Promise<boolean> =>
ipcRenderer.invoke(IPC_CHANNELS.request.getVisibleOverlayVisibility),
getCurrentSubtitle: (): Promise<SubtitleData> => getCurrentSubtitle: (): Promise<SubtitleData> =>
ipcRenderer.invoke(IPC_CHANNELS.request.getCurrentSubtitle), ipcRenderer.invoke(IPC_CHANNELS.request.getCurrentSubtitle),
getCurrentSubtitleRaw: (): Promise<string> => getCurrentSubtitleRaw: (): Promise<string> =>
ipcRenderer.invoke(IPC_CHANNELS.request.getCurrentSubtitleRaw), ipcRenderer.invoke(IPC_CHANNELS.request.getCurrentSubtitleRaw),
getCurrentSubtitleAss: (): Promise<string> =>
ipcRenderer.invoke(IPC_CHANNELS.request.getCurrentSubtitleAss),
getSubtitleSidebarOpen: (): Promise<boolean> => getSubtitleSidebarOpen: (): Promise<boolean> =>
ipcRenderer.invoke(IPC_CHANNELS.request.getSubtitleSidebarOpen), ipcRenderer.invoke(IPC_CHANNELS.request.getSubtitleSidebarOpen),
getSubtitleSidebarSnapshot: () => getSubtitleSidebarSnapshot: () =>
@@ -264,14 +248,6 @@ const electronAPI: ElectronAPI = {
}, },
getPlaybackPaused: (): Promise<boolean | null> => getPlaybackPaused: (): Promise<boolean | null> =>
ipcRenderer.invoke(IPC_CHANNELS.request.getPlaybackPaused), ipcRenderer.invoke(IPC_CHANNELS.request.getPlaybackPaused),
onSubtitleAss: (callback: (assText: string) => void) => {
ipcRenderer.on(
IPC_CHANNELS.event.subtitleAssSet,
(_event: IpcRendererEvent, assText: string) => {
callback(assText);
},
);
},
setIgnoreMouseEvents: (ignore: boolean, options?: { forward?: boolean }) => { setIgnoreMouseEvents: (ignore: boolean, options?: { forward?: boolean }) => {
ipcRenderer.send(IPC_CHANNELS.command.setIgnoreMouseEvents, ignore, options); ipcRenderer.send(IPC_CHANNELS.command.setIgnoreMouseEvents, ignore, options);
@@ -281,10 +257,6 @@ const electronAPI: ElectronAPI = {
ipcRenderer.send(IPC_CHANNELS.command.reportOverlayInteractive, interactive); ipcRenderer.send(IPC_CHANNELS.command.reportOverlayInteractive, interactive);
}, },
openYomitanSettings: () => {
ipcRenderer.send(IPC_CHANNELS.command.openYomitanSettings);
},
recordYomitanLookup: (context?: SubtitleMiningContext | null) => { recordYomitanLookup: (context?: SubtitleMiningContext | null) => {
ipcRenderer.send(IPC_CHANNELS.command.recordYomitanLookup, context ?? null); ipcRenderer.send(IPC_CHANNELS.command.recordYomitanLookup, context ?? null);
}, },
@@ -295,18 +267,10 @@ const electronAPI: ElectronAPI = {
ipcRenderer.send(IPC_CHANNELS.command.saveSubtitlePosition, position); ipcRenderer.send(IPC_CHANNELS.command.saveSubtitlePosition, position);
}, },
getMecabStatus: (): Promise<MecabStatus> =>
ipcRenderer.invoke(IPC_CHANNELS.request.getMecabStatus),
setMecabEnabled: (enabled: boolean) => {
ipcRenderer.send(IPC_CHANNELS.command.setMecabEnabled, enabled);
},
sendMpvCommand: (command: (string | number)[]) => { sendMpvCommand: (command: (string | number)[]) => {
ipcRenderer.send(IPC_CHANNELS.command.mpvCommand, command); ipcRenderer.send(IPC_CHANNELS.command.mpvCommand, command);
}, },
getKeybindings: (): Promise<Keybinding[]> =>
ipcRenderer.invoke(IPC_CHANNELS.request.getKeybindings),
getMpvInputBindings: () => ipcRenderer.invoke(IPC_CHANNELS.request.getMpvInputBindings), getMpvInputBindings: () => ipcRenderer.invoke(IPC_CHANNELS.request.getMpvInputBindings),
getSessionBindings: () => ipcRenderer.invoke(IPC_CHANNELS.request.getSessionBindings), getSessionBindings: () => ipcRenderer.invoke(IPC_CHANNELS.request.getSessionBindings),
getConfiguredShortcuts: (): Promise<Required<ShortcutsConfig>> => getConfiguredShortcuts: (): Promise<Required<ShortcutsConfig>> =>
@@ -325,8 +289,6 @@ const electronAPI: ElectronAPI = {
ipcRenderer.invoke(IPC_CHANNELS.request.getControllerConfig), ipcRenderer.invoke(IPC_CHANNELS.request.getControllerConfig),
saveControllerConfig: (update: ControllerConfigUpdate): Promise<void> => saveControllerConfig: (update: ControllerConfigUpdate): Promise<void> =>
ipcRenderer.invoke(IPC_CHANNELS.command.saveControllerConfig, update), ipcRenderer.invoke(IPC_CHANNELS.command.saveControllerConfig, update),
saveControllerPreference: (update: ControllerPreferenceUpdate): Promise<void> =>
ipcRenderer.invoke(IPC_CHANNELS.command.saveControllerPreference, update),
getJimakuMediaInfo: (): Promise<JimakuMediaInfo> => getJimakuMediaInfo: (): Promise<JimakuMediaInfo> =>
ipcRenderer.invoke(IPC_CHANNELS.request.jimakuGetMediaInfo), ipcRenderer.invoke(IPC_CHANNELS.request.jimakuGetMediaInfo),
@@ -350,31 +312,14 @@ const electronAPI: ElectronAPI = {
tsukihimeGetSecondaryLanguages: (): Promise<string[]> => tsukihimeGetSecondaryLanguages: (): Promise<string[]> =>
ipcRenderer.invoke(IPC_CHANNELS.request.tsukihimeGetSecondaryLanguages), ipcRenderer.invoke(IPC_CHANNELS.request.tsukihimeGetSecondaryLanguages),
quitApp: () => {
ipcRenderer.send(IPC_CHANNELS.command.quitApp);
},
toggleDevTools: () => { toggleDevTools: () => {
ipcRenderer.send(IPC_CHANNELS.command.toggleDevTools); ipcRenderer.send(IPC_CHANNELS.command.toggleDevTools);
}, },
toggleOverlay: () => {
ipcRenderer.send(IPC_CHANNELS.command.toggleOverlay);
},
toggleStatsOverlay: () => { toggleStatsOverlay: () => {
ipcRenderer.send(IPC_CHANNELS.command.toggleStatsOverlay); ipcRenderer.send(IPC_CHANNELS.command.toggleStatsOverlay);
}, },
getAnkiConnectStatus: (): Promise<boolean> =>
ipcRenderer.invoke(IPC_CHANNELS.request.getAnkiConnectStatus),
setAnkiConnectEnabled: (enabled: boolean) => {
ipcRenderer.send(IPC_CHANNELS.command.setAnkiConnectEnabled, enabled);
},
clearAnkiConnectHistory: () => {
ipcRenderer.send(IPC_CHANNELS.command.clearAnkiConnectHistory);
},
onSecondarySub: (callback: (text: string) => void) => { onSecondarySub: (callback: (text: string) => void) => {
onSecondarySubtitleSetEvent(callback); onSecondarySubtitleSetEvent(callback);
}, },
@@ -419,8 +364,6 @@ const electronAPI: ElectronAPI = {
value: RuntimeOptionValue, value: RuntimeOptionValue,
): Promise<RuntimeOptionApplyResult> => ): Promise<RuntimeOptionApplyResult> =>
ipcRenderer.invoke(IPC_CHANNELS.request.setRuntimeOption, id, value), ipcRenderer.invoke(IPC_CHANNELS.request.setRuntimeOption, id, value),
cycleRuntimeOption: (id: RuntimeOptionId, direction: 1 | -1): Promise<RuntimeOptionApplyResult> =>
ipcRenderer.invoke(IPC_CHANNELS.request.cycleRuntimeOption, id, direction),
onRuntimeOptionsChanged: (callback: (options: RuntimeOptionState[]) => void) => { onRuntimeOptionsChanged: (callback: (options: RuntimeOptionState[]) => void) => {
ipcRenderer.on( ipcRenderer.on(
IPC_CHANNELS.event.runtimeOptionsChanged, IPC_CHANNELS.event.runtimeOptionsChanged,
@@ -485,8 +428,6 @@ const electronAPI: ElectronAPI = {
onSessionNumericSelectionStart: onSessionNumericSelectionStartEvent, onSessionNumericSelectionStart: onSessionNumericSelectionStartEvent,
onKeyboardModeToggleRequested: onKeyboardModeToggleRequestedEvent, onKeyboardModeToggleRequested: onKeyboardModeToggleRequestedEvent,
onLookupWindowToggleRequested: onLookupWindowToggleRequestedEvent, onLookupWindowToggleRequested: onLookupWindowToggleRequestedEvent,
appendClipboardVideoToQueue: (): Promise<ClipboardAppendResult> =>
ipcRenderer.invoke(IPC_CHANNELS.request.appendClipboardVideoToQueue),
getPlaylistBrowserSnapshot: (): Promise<PlaylistBrowserSnapshot> => getPlaylistBrowserSnapshot: (): Promise<PlaylistBrowserSnapshot> =>
ipcRenderer.invoke(IPC_CHANNELS.request.getPlaylistBrowserSnapshot), ipcRenderer.invoke(IPC_CHANNELS.request.getPlaylistBrowserSnapshot),
appendPlaylistBrowserFile: (pathValue: string): Promise<PlaylistBrowserMutationResult> => appendPlaylistBrowserFile: (pathValue: string): Promise<PlaylistBrowserMutationResult> =>
-1
View File
@@ -242,7 +242,6 @@ function installKeyboardTestGlobals() {
windowFocusCalls += 1; windowFocusCalls += 1;
}, },
electronAPI: { electronAPI: {
getKeybindings: async () => [],
getMpvInputBindings: () => getMpvInputBindings(), getMpvInputBindings: () => getMpvInputBindings(),
getSessionBindings: () => getSessionBindingsImpl(), getSessionBindings: () => getSessionBindingsImpl(),
getConfiguredShortcuts: async () => configuredShortcuts, getConfiguredShortcuts: async () => configuredShortcuts,
-21
View File
@@ -25,22 +25,15 @@ export const IPC_CHANNELS = {
command: { command: {
setIgnoreMouseEvents: 'set-ignore-mouse-events', setIgnoreMouseEvents: 'set-ignore-mouse-events',
overlayModalClosed: 'overlay:modal-closed', overlayModalClosed: 'overlay:modal-closed',
openYomitanSettings: 'open-yomitan-settings',
recordYomitanLookup: 'record-yomitan-lookup', recordYomitanLookup: 'record-yomitan-lookup',
quitApp: 'quit-app',
youtubePickerResolve: 'youtube:picker-resolve', youtubePickerResolve: 'youtube:picker-resolve',
toggleDevTools: 'toggle-dev-tools', toggleDevTools: 'toggle-dev-tools',
toggleOverlay: 'toggle-overlay',
saveSubtitlePosition: 'save-subtitle-position', saveSubtitlePosition: 'save-subtitle-position',
statsNativeConfirmDialog: 'stats:native-confirm-dialog', statsNativeConfirmDialog: 'stats:native-confirm-dialog',
statsNativeDialogOpened: 'stats:native-dialog-opened', statsNativeDialogOpened: 'stats:native-dialog-opened',
statsNativeDialogClosed: 'stats:native-dialog-closed', statsNativeDialogClosed: 'stats:native-dialog-closed',
saveControllerConfig: 'save-controller-config', saveControllerConfig: 'save-controller-config',
saveControllerPreference: 'save-controller-preference',
setMecabEnabled: 'set-mecab-enabled',
mpvCommand: 'mpv-command', mpvCommand: 'mpv-command',
setAnkiConnectEnabled: 'set-anki-connect-enabled',
clearAnkiConnectHistory: 'clear-anki-connect-history',
refreshKnownWords: 'anki:refresh-known-words', refreshKnownWords: 'anki:refresh-known-words',
kikuFieldGroupingRespond: 'kiku:field-grouping-respond', kikuFieldGroupingRespond: 'kiku:field-grouping-respond',
reportOverlayContentBounds: 'overlay-content-bounds:report', reportOverlayContentBounds: 'overlay-content-bounds:report',
@@ -63,18 +56,14 @@ export const IPC_CHANNELS = {
downloadSubtitleGenerationVadModel: 'subtitle-generation:download-vad', downloadSubtitleGenerationVadModel: 'subtitle-generation:download-vad',
setSubtitleGenerationVadEnabled: 'subtitle-generation:set-vad-enabled', setSubtitleGenerationVadEnabled: 'subtitle-generation:set-vad-enabled',
cancelSubtitleGeneration: 'subtitle-generation:cancel', cancelSubtitleGeneration: 'subtitle-generation:cancel',
getVisibleOverlayVisibility: 'get-visible-overlay-visibility',
getCurrentSubtitle: 'get-current-subtitle', getCurrentSubtitle: 'get-current-subtitle',
getCurrentSubtitleRaw: 'get-current-subtitle-raw', getCurrentSubtitleRaw: 'get-current-subtitle-raw',
getCurrentSubtitleAss: 'get-current-subtitle-ass',
getSubtitleSidebarSnapshot: 'get-subtitle-sidebar-snapshot', getSubtitleSidebarSnapshot: 'get-subtitle-sidebar-snapshot',
getSubtitleSidebarPlaybackState: 'get-subtitle-sidebar-playback-state', getSubtitleSidebarPlaybackState: 'get-subtitle-sidebar-playback-state',
getSubtitleSidebarOpen: 'get-subtitle-sidebar-open', getSubtitleSidebarOpen: 'get-subtitle-sidebar-open',
getPlaybackPaused: 'get-playback-paused', getPlaybackPaused: 'get-playback-paused',
getSubtitlePosition: 'get-subtitle-position', getSubtitlePosition: 'get-subtitle-position',
getSubtitleStyle: 'get-subtitle-style', getSubtitleStyle: 'get-subtitle-style',
getMecabStatus: 'get-mecab-status',
getKeybindings: 'get-keybindings',
getMpvInputBindings: 'get-mpv-input-bindings', getMpvInputBindings: 'get-mpv-input-bindings',
getSessionBindings: 'get-session-bindings', getSessionBindings: 'get-session-bindings',
getConfigShortcuts: 'get-config-shortcuts', getConfigShortcuts: 'get-config-shortcuts',
@@ -88,10 +77,8 @@ export const IPC_CHANNELS = {
focusMainWindow: 'focus-main-window', focusMainWindow: 'focus-main-window',
activatePlaybackWindowForOverlayInteraction: 'overlay:activate-playback-window', activatePlaybackWindowForOverlayInteraction: 'overlay:activate-playback-window',
runSubsyncManual: 'subsync:run-manual', runSubsyncManual: 'subsync:run-manual',
getAnkiConnectStatus: 'get-anki-connect-status',
getRuntimeOptions: 'runtime-options:get', getRuntimeOptions: 'runtime-options:get',
setRuntimeOption: 'runtime-options:set', setRuntimeOption: 'runtime-options:set',
cycleRuntimeOption: 'runtime-options:cycle',
getAnilistStatus: 'anilist:get-status', getAnilistStatus: 'anilist:get-status',
clearAnilistToken: 'anilist:clear-token', clearAnilistToken: 'anilist:clear-token',
openAnilistSetup: 'anilist:open-setup', openAnilistSetup: 'anilist:open-setup',
@@ -103,7 +90,6 @@ export const IPC_CHANNELS = {
getCharacterDictionaryManagerSnapshot: 'character-dictionary:get-manager-snapshot', getCharacterDictionaryManagerSnapshot: 'character-dictionary:get-manager-snapshot',
removeCharacterDictionaryManagedEntry: 'character-dictionary:remove-managed-entry', removeCharacterDictionaryManagedEntry: 'character-dictionary:remove-managed-entry',
moveCharacterDictionaryManagedEntry: 'character-dictionary:move-managed-entry', moveCharacterDictionaryManagedEntry: 'character-dictionary:move-managed-entry',
appendClipboardVideoToQueue: 'clipboard:append-video-to-queue',
getPlaylistBrowserSnapshot: 'playlist-browser:get-snapshot', getPlaylistBrowserSnapshot: 'playlist-browser:get-snapshot',
appendPlaylistBrowserFile: 'playlist-browser:append-file', appendPlaylistBrowserFile: 'playlist-browser:append-file',
playPlaylistBrowserIndex: 'playlist-browser:play-index', playPlaylistBrowserIndex: 'playlist-browser:play-index',
@@ -153,9 +139,7 @@ export const IPC_CHANNELS = {
subtitleGenerationProgress: 'subtitle-generation:progress', subtitleGenerationProgress: 'subtitle-generation:progress',
subtitleSet: 'subtitle:set', subtitleSet: 'subtitle:set',
overlayPointerRecoveryRequest: 'overlay:pointer-recovery-request', overlayPointerRecoveryRequest: 'overlay:pointer-recovery-request',
subtitleVisibility: 'mpv:subVisibility',
subtitlePositionSet: 'subtitle-position:set', subtitlePositionSet: 'subtitle-position:set',
subtitleAssSet: 'subtitle-ass:set',
secondarySubtitleSet: 'secondary-subtitle:set', secondarySubtitleSet: 'secondary-subtitle:set',
secondarySubtitleMode: 'secondary-subtitle:mode', secondarySubtitleMode: 'secondary-subtitle:mode',
subsyncOpenManual: 'subsync:open-manual', subsyncOpenManual: 'subsync:open-manual',
@@ -197,11 +181,6 @@ export type RuntimeOptionsSetRequest = {
value: RuntimeOptionValue; value: RuntimeOptionValue;
}; };
export type RuntimeOptionsCycleRequest = {
id: RuntimeOptionId;
direction: 1 | -1;
};
export type OverlayContentBoundsReportRequest = { export type OverlayContentBoundsReportRequest = {
measurement: OverlayContentMeasurement; measurement: OverlayContentMeasurement;
}; };
-15
View File
@@ -10,7 +10,6 @@ import type {
} from '../../types/integrations'; } from '../../types/integrations';
import type { import type {
ControllerConfigUpdate, ControllerConfigUpdate,
ControllerPreferenceUpdate,
SessionActionDispatchRequest, SessionActionDispatchRequest,
SubsyncManualRunRequest, SubsyncManualRunRequest,
} from '../../types/runtime'; } from '../../types/runtime';
@@ -131,16 +130,6 @@ export function parseSubtitlePosition(value: unknown): SubtitlePosition | null {
}; };
} }
export function parseControllerPreferenceUpdate(value: unknown): ControllerPreferenceUpdate | null {
if (!isObject(value)) return null;
if (typeof value.preferredGamepadId !== 'string') return null;
if (typeof value.preferredGamepadLabel !== 'string') return null;
return {
preferredGamepadId: value.preferredGamepadId,
preferredGamepadLabel: value.preferredGamepadLabel,
};
}
function parseDiscreteBinding(value: unknown) { function parseDiscreteBinding(value: unknown) {
if (!isObject(value) || typeof value.kind !== 'string') return null; if (!isObject(value) || typeof value.kind !== 'string') return null;
if (value.kind === 'none') { if (value.kind === 'none') {
@@ -328,10 +317,6 @@ export function parseRuntimeOptionId(value: unknown): RuntimeOptionId | null {
return RUNTIME_OPTION_IDS.includes(value as RuntimeOptionId) ? (value as RuntimeOptionId) : null; return RUNTIME_OPTION_IDS.includes(value as RuntimeOptionId) ? (value as RuntimeOptionId) : null;
} }
export function parseRuntimeOptionDirection(value: unknown): 1 | -1 | null {
return value === 1 || value === -1 ? value : null;
}
export function parseRuntimeOptionValue(value: unknown): RuntimeOptionValue | null { export function parseRuntimeOptionValue(value: unknown): RuntimeOptionValue | null {
return typeof value === 'boolean' || typeof value === 'string' return typeof value === 'boolean' || typeof value === 'string'
? (value as RuntimeOptionValue) ? (value as RuntimeOptionValue)
-21
View File
@@ -297,11 +297,6 @@ export interface ControllerConfig {
profiles?: Record<string, ControllerProfileConfig>; profiles?: Record<string, ControllerProfileConfig>;
} }
export interface ControllerPreferenceUpdate {
preferredGamepadId: string;
preferredGamepadLabel: string;
}
export type ControllerConfigUpdate = ControllerConfig; export type ControllerConfigUpdate = ControllerConfig;
export interface ControllerDeviceInfo { export interface ControllerDeviceInfo {
@@ -470,28 +465,20 @@ export interface ElectronAPI {
options?: Pick<OverlayNotificationAction, 'noteId'>, options?: Pick<OverlayNotificationAction, 'noteId'>,
) => void; ) => void;
onNotificationHistoryToggle: (callback: () => void) => void; onNotificationHistoryToggle: (callback: () => void) => void;
onVisibility: (callback: (visible: boolean) => void) => void;
onSubtitlePosition: (callback: (position: SubtitlePosition | null) => void) => void; onSubtitlePosition: (callback: (position: SubtitlePosition | null) => void) => void;
getOverlayVisibility: () => Promise<boolean>;
getCurrentSubtitle: () => Promise<SubtitleData>; getCurrentSubtitle: () => Promise<SubtitleData>;
getCurrentSubtitleRaw: () => Promise<string>; getCurrentSubtitleRaw: () => Promise<string>;
getCurrentSubtitleAss: () => Promise<string>;
getSubtitleSidebarSnapshot: () => Promise<SubtitleSidebarSnapshot>; getSubtitleSidebarSnapshot: () => Promise<SubtitleSidebarSnapshot>;
getSubtitleSidebarPlaybackState: () => Promise<SubtitleSidebarPlaybackState>; getSubtitleSidebarPlaybackState: () => Promise<SubtitleSidebarPlaybackState>;
copySubtitleSidebarSelection: (text: string) => Promise<void>; copySubtitleSidebarSelection: (text: string) => Promise<void>;
getSubtitleSidebarOpen: () => Promise<boolean>; getSubtitleSidebarOpen: () => Promise<boolean>;
getPlaybackPaused: () => Promise<boolean | null>; getPlaybackPaused: () => Promise<boolean | null>;
onSubtitleAss: (callback: (assText: string) => void) => void;
setIgnoreMouseEvents: (ignore: boolean, options?: { forward?: boolean }) => void; setIgnoreMouseEvents: (ignore: boolean, options?: { forward?: boolean }) => void;
reportOverlayInteractive: (interactive: boolean) => void; reportOverlayInteractive: (interactive: boolean) => void;
openYomitanSettings: () => void;
recordYomitanLookup: (context?: SubtitleMiningContext | null) => void; recordYomitanLookup: (context?: SubtitleMiningContext | null) => void;
getSubtitlePosition: () => Promise<SubtitlePosition | null>; getSubtitlePosition: () => Promise<SubtitlePosition | null>;
saveSubtitlePosition: (position: SubtitlePosition) => void; saveSubtitlePosition: (position: SubtitlePosition) => void;
getMecabStatus: () => Promise<MecabStatus>;
setMecabEnabled: (enabled: boolean) => void;
sendMpvCommand: (command: (string | number)[]) => void; sendMpvCommand: (command: (string | number)[]) => void;
getKeybindings: () => Promise<Keybinding[]>;
getMpvInputBindings: () => Promise<MpvInputBindingsSnapshot>; getMpvInputBindings: () => Promise<MpvInputBindingsSnapshot>;
getSessionBindings: () => Promise<CompiledSessionBinding[]>; getSessionBindings: () => Promise<CompiledSessionBinding[]>;
getConfiguredShortcuts: () => Promise<Required<ShortcutsConfig>>; getConfiguredShortcuts: () => Promise<Required<ShortcutsConfig>>;
@@ -505,7 +492,6 @@ export interface ElectronAPI {
markActiveVideoWatched: () => Promise<boolean>; markActiveVideoWatched: () => Promise<boolean>;
getControllerConfig: () => Promise<ResolvedControllerConfig>; getControllerConfig: () => Promise<ResolvedControllerConfig>;
saveControllerConfig: (update: ControllerConfigUpdate) => Promise<void>; saveControllerConfig: (update: ControllerConfigUpdate) => Promise<void>;
saveControllerPreference: (update: ControllerPreferenceUpdate) => Promise<void>;
getJimakuMediaInfo: () => Promise<JimakuMediaInfo>; getJimakuMediaInfo: () => Promise<JimakuMediaInfo>;
jimakuSearchEntries: (query: JimakuSearchQuery) => Promise<JimakuApiResponse<JimakuEntry[]>>; jimakuSearchEntries: (query: JimakuSearchQuery) => Promise<JimakuApiResponse<JimakuEntry[]>>;
jimakuListFiles: (query: JimakuFilesQuery) => Promise<JimakuApiResponse<JimakuFileEntry[]>>; jimakuListFiles: (query: JimakuFilesQuery) => Promise<JimakuApiResponse<JimakuFileEntry[]>>;
@@ -518,13 +504,8 @@ export interface ElectronAPI {
) => Promise<TsukihimeApiResponse<TsukihimeSubtitleFile[]>>; ) => Promise<TsukihimeApiResponse<TsukihimeSubtitleFile[]>>;
tsukihimeDownloadFile: (query: TsukihimeDownloadQuery) => Promise<TsukihimeDownloadResult>; tsukihimeDownloadFile: (query: TsukihimeDownloadQuery) => Promise<TsukihimeDownloadResult>;
tsukihimeGetSecondaryLanguages: () => Promise<string[]>; tsukihimeGetSecondaryLanguages: () => Promise<string[]>;
quitApp: () => void;
toggleDevTools: () => void; toggleDevTools: () => void;
toggleOverlay: () => void;
toggleStatsOverlay: () => void; toggleStatsOverlay: () => void;
getAnkiConnectStatus: () => Promise<boolean>;
setAnkiConnectEnabled: (enabled: boolean) => void;
clearAnkiConnectHistory: () => void;
onSecondarySub: (callback: (text: string) => void) => void; onSecondarySub: (callback: (text: string) => void) => void;
onSecondarySubMode: (callback: (mode: SecondarySubMode) => void) => void; onSecondarySubMode: (callback: (mode: SecondarySubMode) => void) => void;
getSecondarySubMode: () => Promise<SecondarySubMode>; getSecondarySubMode: () => Promise<SecondarySubMode>;
@@ -550,7 +531,6 @@ export interface ElectronAPI {
id: RuntimeOptionId, id: RuntimeOptionId,
value: RuntimeOptionValue, value: RuntimeOptionValue,
) => Promise<RuntimeOptionApplyResult>; ) => Promise<RuntimeOptionApplyResult>;
cycleRuntimeOption: (id: RuntimeOptionId, direction: 1 | -1) => Promise<RuntimeOptionApplyResult>;
onRuntimeOptionsChanged: (callback: (options: RuntimeOptionState[]) => void) => void; onRuntimeOptionsChanged: (callback: (options: RuntimeOptionState[]) => void) => void;
onOpenRuntimeOptions: (callback: () => void) => void; onOpenRuntimeOptions: (callback: () => void) => void;
onOpenSessionHelp: (callback: (payload: SessionHelpOpenPayload) => void) => void; onOpenSessionHelp: (callback: (payload: SessionHelpOpenPayload) => void) => void;
@@ -589,7 +569,6 @@ export interface ElectronAPI {
) => void; ) => void;
onKeyboardModeToggleRequested: (callback: () => void) => void; onKeyboardModeToggleRequested: (callback: () => void) => void;
onLookupWindowToggleRequested: (callback: () => void) => void; onLookupWindowToggleRequested: (callback: () => void) => void;
appendClipboardVideoToQueue: () => Promise<ClipboardAppendResult>;
getPlaylistBrowserSnapshot: () => Promise<PlaylistBrowserSnapshot>; getPlaylistBrowserSnapshot: () => Promise<PlaylistBrowserSnapshot>;
appendPlaylistBrowserFile: (path: string) => Promise<PlaylistBrowserMutationResult>; appendPlaylistBrowserFile: (path: string) => Promise<PlaylistBrowserMutationResult>;
playPlaylistBrowserIndex: (index: number) => Promise<PlaylistBrowserMutationResult>; playPlaylistBrowserIndex: (index: number) => Promise<PlaylistBrowserMutationResult>;