mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-28 18:22:42 -08:00
fix(runtime): avoid loading disabled integrations
This commit is contained in:
@@ -209,6 +209,24 @@ test('AnkiIntegration.refreshKnownWordCache deduplicates concurrent refreshes',
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('AnkiIntegration does not allocate proxy server when proxy transport is disabled', () => {
|
||||||
|
const integration = new AnkiIntegration(
|
||||||
|
{
|
||||||
|
enabled: true,
|
||||||
|
proxy: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
|
} as never,
|
||||||
|
{} as never,
|
||||||
|
{} as never,
|
||||||
|
);
|
||||||
|
|
||||||
|
const privateState = integration as unknown as {
|
||||||
|
proxyServer: unknown | null;
|
||||||
|
};
|
||||||
|
assert.equal(privateState.proxyServer, null);
|
||||||
|
});
|
||||||
|
|
||||||
test('FieldGroupingMergeCollaborator synchronizes ExpressionAudio from merged SentenceAudio', async () => {
|
test('FieldGroupingMergeCollaborator synchronizes ExpressionAudio from merged SentenceAudio', async () => {
|
||||||
const collaborator = createFieldGroupingMergeCollaborator();
|
const collaborator = createFieldGroupingMergeCollaborator();
|
||||||
|
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ import {
|
|||||||
} from './anki-integration/ui-feedback';
|
} from './anki-integration/ui-feedback';
|
||||||
import { KnownWordCacheManager } from './anki-integration/known-word-cache';
|
import { KnownWordCacheManager } from './anki-integration/known-word-cache';
|
||||||
import { PollingRunner } from './anki-integration/polling';
|
import { PollingRunner } from './anki-integration/polling';
|
||||||
|
import type { AnkiConnectProxyServer } from './anki-integration/anki-connect-proxy';
|
||||||
import { findDuplicateNote as findDuplicateNoteForAnkiIntegration } from './anki-integration/duplicate';
|
import { findDuplicateNote as findDuplicateNoteForAnkiIntegration } from './anki-integration/duplicate';
|
||||||
import { CardCreationService } from './anki-integration/card-creation';
|
import { CardCreationService } from './anki-integration/card-creation';
|
||||||
import { FieldGroupingService } from './anki-integration/field-grouping';
|
import { FieldGroupingService } from './anki-integration/field-grouping';
|
||||||
@@ -63,6 +64,8 @@ export class AnkiIntegration {
|
|||||||
private timingTracker: SubtitleTimingTracker;
|
private timingTracker: SubtitleTimingTracker;
|
||||||
private config: AnkiConnectConfig;
|
private config: AnkiConnectConfig;
|
||||||
private pollingRunner!: PollingRunner;
|
private pollingRunner!: PollingRunner;
|
||||||
|
private proxyServer: AnkiConnectProxyServer | null = null;
|
||||||
|
private started = false;
|
||||||
private previousNoteIds = new Set<number>();
|
private previousNoteIds = new Set<number>();
|
||||||
private mpvClient: MpvClient;
|
private mpvClient: MpvClient;
|
||||||
private osdCallback: ((text: string) => void) | null = null;
|
private osdCallback: ((text: string) => void) | null = null;
|
||||||
@@ -131,13 +134,46 @@ export class AnkiIntegration {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private normalizeConfig(config: AnkiConnectConfig): AnkiConnectConfig {
|
private normalizeConfig(config: AnkiConnectConfig): AnkiConnectConfig {
|
||||||
|
const resolvedUrl =
|
||||||
|
typeof config.url === 'string' && config.url.trim().length > 0
|
||||||
|
? config.url.trim()
|
||||||
|
: DEFAULT_ANKI_CONNECT_CONFIG.url;
|
||||||
|
const proxySource =
|
||||||
|
config.proxy && typeof config.proxy === 'object'
|
||||||
|
? (config.proxy as NonNullable<AnkiConnectConfig['proxy']>)
|
||||||
|
: {};
|
||||||
|
const normalizedProxyPort =
|
||||||
|
typeof proxySource.port === 'number' &&
|
||||||
|
Number.isInteger(proxySource.port) &&
|
||||||
|
proxySource.port >= 1 &&
|
||||||
|
proxySource.port <= 65535
|
||||||
|
? proxySource.port
|
||||||
|
: DEFAULT_ANKI_CONNECT_CONFIG.proxy?.port;
|
||||||
|
const normalizedProxyHost =
|
||||||
|
typeof proxySource.host === 'string' && proxySource.host.trim().length > 0
|
||||||
|
? proxySource.host.trim()
|
||||||
|
: DEFAULT_ANKI_CONNECT_CONFIG.proxy?.host;
|
||||||
|
const normalizedProxyUpstreamUrl =
|
||||||
|
typeof proxySource.upstreamUrl === 'string' && proxySource.upstreamUrl.trim().length > 0
|
||||||
|
? proxySource.upstreamUrl.trim()
|
||||||
|
: resolvedUrl;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...DEFAULT_ANKI_CONNECT_CONFIG,
|
...DEFAULT_ANKI_CONNECT_CONFIG,
|
||||||
...config,
|
...config,
|
||||||
|
url: resolvedUrl,
|
||||||
fields: {
|
fields: {
|
||||||
...DEFAULT_ANKI_CONNECT_CONFIG.fields,
|
...DEFAULT_ANKI_CONNECT_CONFIG.fields,
|
||||||
...(config.fields ?? {}),
|
...(config.fields ?? {}),
|
||||||
},
|
},
|
||||||
|
proxy: {
|
||||||
|
...DEFAULT_ANKI_CONNECT_CONFIG.proxy,
|
||||||
|
...(config.proxy ?? {}),
|
||||||
|
enabled: proxySource.enabled === true,
|
||||||
|
host: normalizedProxyHost,
|
||||||
|
port: normalizedProxyPort,
|
||||||
|
upstreamUrl: normalizedProxyUpstreamUrl,
|
||||||
|
},
|
||||||
ai: {
|
ai: {
|
||||||
...DEFAULT_ANKI_CONNECT_CONFIG.ai,
|
...DEFAULT_ANKI_CONNECT_CONFIG.ai,
|
||||||
...(config.openRouter ?? {}),
|
...(config.openRouter ?? {}),
|
||||||
@@ -202,6 +238,24 @@ export class AnkiIntegration {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private createProxyServer(): AnkiConnectProxyServer {
|
||||||
|
const { AnkiConnectProxyServer } = require('./anki-integration/anki-connect-proxy') as typeof import('./anki-integration/anki-connect-proxy');
|
||||||
|
return new AnkiConnectProxyServer({
|
||||||
|
shouldAutoUpdateNewCards: () => this.config.behavior?.autoUpdateNewCards !== false,
|
||||||
|
processNewCard: (noteId: number) => this.processNewCard(noteId),
|
||||||
|
logInfo: (message, ...args) => log.info(message, ...args),
|
||||||
|
logWarn: (message, ...args) => log.warn(message, ...args),
|
||||||
|
logError: (message, ...args) => log.error(message, ...args),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private getOrCreateProxyServer(): AnkiConnectProxyServer {
|
||||||
|
if (!this.proxyServer) {
|
||||||
|
this.proxyServer = this.createProxyServer();
|
||||||
|
}
|
||||||
|
return this.proxyServer;
|
||||||
|
}
|
||||||
|
|
||||||
private createCardCreationService(): CardCreationService {
|
private createCardCreationService(): CardCreationService {
|
||||||
return new CardCreationService({
|
return new CardCreationService({
|
||||||
getConfig: () => this.config,
|
getConfig: () => this.config,
|
||||||
@@ -499,19 +553,63 @@ export class AnkiIntegration {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
start(): void {
|
private isProxyTransportEnabled(config: AnkiConnectConfig = this.config): boolean {
|
||||||
if (this.pollingRunner.isRunning) {
|
return config.proxy?.enabled === true;
|
||||||
this.stop();
|
}
|
||||||
|
|
||||||
|
private getTransportConfigKey(config: AnkiConnectConfig = this.config): string {
|
||||||
|
if (this.isProxyTransportEnabled(config)) {
|
||||||
|
return [
|
||||||
|
'proxy',
|
||||||
|
config.proxy?.host ?? '',
|
||||||
|
String(config.proxy?.port ?? ''),
|
||||||
|
config.proxy?.upstreamUrl ?? '',
|
||||||
|
].join(':');
|
||||||
|
}
|
||||||
|
return ['polling', String(config.pollingRate ?? DEFAULT_ANKI_CONNECT_CONFIG.pollingRate)].join(
|
||||||
|
':',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private startTransport(): void {
|
||||||
|
if (this.isProxyTransportEnabled()) {
|
||||||
|
const proxyHost = this.config.proxy?.host ?? '127.0.0.1';
|
||||||
|
const proxyPort = this.config.proxy?.port ?? 8766;
|
||||||
|
const upstreamUrl = this.config.proxy?.upstreamUrl ?? this.config.url ?? '';
|
||||||
|
this.getOrCreateProxyServer().start({
|
||||||
|
host: proxyHost,
|
||||||
|
port: proxyPort,
|
||||||
|
upstreamUrl,
|
||||||
|
});
|
||||||
|
log.info(
|
||||||
|
`Starting AnkiConnect integration with local proxy: http://${proxyHost}:${proxyPort} -> ${upstreamUrl}`,
|
||||||
|
);
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info('Starting AnkiConnect integration with polling rate:', this.config.pollingRate);
|
log.info('Starting AnkiConnect integration with polling rate:', this.config.pollingRate);
|
||||||
this.startKnownWordCacheLifecycle();
|
|
||||||
this.pollingRunner.start();
|
this.pollingRunner.start();
|
||||||
}
|
}
|
||||||
|
|
||||||
stop(): void {
|
private stopTransport(): void {
|
||||||
this.pollingRunner.stop();
|
this.pollingRunner.stop();
|
||||||
|
this.proxyServer?.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
start(): void {
|
||||||
|
if (this.started) {
|
||||||
|
this.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.startKnownWordCacheLifecycle();
|
||||||
|
this.startTransport();
|
||||||
|
this.started = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
stop(): void {
|
||||||
|
this.stopTransport();
|
||||||
this.stopKnownWordCacheLifecycle();
|
this.stopKnownWordCacheLifecycle();
|
||||||
|
this.started = false;
|
||||||
log.info('Stopped AnkiConnect integration');
|
log.info('Stopped AnkiConnect integration');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1062,8 +1160,9 @@ export class AnkiIntegration {
|
|||||||
|
|
||||||
applyRuntimeConfigPatch(patch: Partial<AnkiConnectConfig>): void {
|
applyRuntimeConfigPatch(patch: Partial<AnkiConnectConfig>): void {
|
||||||
const wasEnabled = this.config.nPlusOne?.highlightEnabled === true;
|
const wasEnabled = this.config.nPlusOne?.highlightEnabled === true;
|
||||||
const previousPollingRate = this.config.pollingRate;
|
const previousTransportKey = this.getTransportConfigKey(this.config);
|
||||||
this.config = {
|
|
||||||
|
const mergedConfig: AnkiConnectConfig = {
|
||||||
...this.config,
|
...this.config,
|
||||||
...patch,
|
...patch,
|
||||||
nPlusOne:
|
nPlusOne:
|
||||||
@@ -1083,6 +1182,8 @@ export class AnkiIntegration {
|
|||||||
patch.behavior !== undefined
|
patch.behavior !== undefined
|
||||||
? { ...this.config.behavior, ...patch.behavior }
|
? { ...this.config.behavior, ...patch.behavior }
|
||||||
: this.config.behavior,
|
: this.config.behavior,
|
||||||
|
proxy:
|
||||||
|
patch.proxy !== undefined ? { ...this.config.proxy, ...patch.proxy } : this.config.proxy,
|
||||||
metadata:
|
metadata:
|
||||||
patch.metadata !== undefined
|
patch.metadata !== undefined
|
||||||
? { ...this.config.metadata, ...patch.metadata }
|
? { ...this.config.metadata, ...patch.metadata }
|
||||||
@@ -1096,6 +1197,7 @@ export class AnkiIntegration {
|
|||||||
? { ...this.config.isKiku, ...patch.isKiku }
|
? { ...this.config.isKiku, ...patch.isKiku }
|
||||||
: this.config.isKiku,
|
: this.config.isKiku,
|
||||||
};
|
};
|
||||||
|
this.config = this.normalizeConfig(mergedConfig);
|
||||||
|
|
||||||
if (wasEnabled && this.config.nPlusOne?.highlightEnabled === false) {
|
if (wasEnabled && this.config.nPlusOne?.highlightEnabled === false) {
|
||||||
this.stopKnownWordCacheLifecycle();
|
this.stopKnownWordCacheLifecycle();
|
||||||
@@ -1104,12 +1206,10 @@ export class AnkiIntegration {
|
|||||||
this.startKnownWordCacheLifecycle();
|
this.startKnownWordCacheLifecycle();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
const nextTransportKey = this.getTransportConfigKey(this.config);
|
||||||
patch.pollingRate !== undefined &&
|
if (this.started && previousTransportKey !== nextTransportKey) {
|
||||||
previousPollingRate !== this.config.pollingRate &&
|
this.stopTransport();
|
||||||
this.pollingRunner.isRunning
|
this.startTransport();
|
||||||
) {
|
|
||||||
this.pollingRunner.start();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
100
src/core/services/overlay-runtime-init.test.ts
Normal file
100
src/core/services/overlay-runtime-init.test.ts
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
import assert from 'node:assert/strict';
|
||||||
|
import test from 'node:test';
|
||||||
|
import { initializeOverlayRuntime } from './overlay-runtime-init';
|
||||||
|
|
||||||
|
test('initializeOverlayRuntime skips Anki integration when ankiConnect.enabled is false', () => {
|
||||||
|
let createdIntegrations = 0;
|
||||||
|
let startedIntegrations = 0;
|
||||||
|
let setIntegrationCalls = 0;
|
||||||
|
|
||||||
|
initializeOverlayRuntime({
|
||||||
|
backendOverride: null,
|
||||||
|
createMainWindow: () => {},
|
||||||
|
registerGlobalShortcuts: () => {},
|
||||||
|
updateVisibleOverlayBounds: () => {},
|
||||||
|
isVisibleOverlayVisible: () => false,
|
||||||
|
updateVisibleOverlayVisibility: () => {},
|
||||||
|
getOverlayWindows: () => [],
|
||||||
|
syncOverlayShortcuts: () => {},
|
||||||
|
setWindowTracker: () => {},
|
||||||
|
getMpvSocketPath: () => '/tmp/mpv.sock',
|
||||||
|
createWindowTracker: () => null,
|
||||||
|
getResolvedConfig: () => ({
|
||||||
|
ankiConnect: { enabled: false } as never,
|
||||||
|
}),
|
||||||
|
getSubtitleTimingTracker: () => ({}),
|
||||||
|
getMpvClient: () => ({
|
||||||
|
send: () => {},
|
||||||
|
}),
|
||||||
|
getRuntimeOptionsManager: () => ({
|
||||||
|
getEffectiveAnkiConnectConfig: (config) => config as never,
|
||||||
|
}),
|
||||||
|
createAnkiIntegration: () => {
|
||||||
|
createdIntegrations += 1;
|
||||||
|
return {
|
||||||
|
start: () => {
|
||||||
|
startedIntegrations += 1;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
setAnkiIntegration: () => {
|
||||||
|
setIntegrationCalls += 1;
|
||||||
|
},
|
||||||
|
showDesktopNotification: () => {},
|
||||||
|
createFieldGroupingCallback: () => async () => 'auto',
|
||||||
|
getKnownWordCacheStatePath: () => '/tmp/known-words-cache.json',
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(createdIntegrations, 0);
|
||||||
|
assert.equal(startedIntegrations, 0);
|
||||||
|
assert.equal(setIntegrationCalls, 0);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('initializeOverlayRuntime starts Anki integration when ankiConnect.enabled is true', () => {
|
||||||
|
let createdIntegrations = 0;
|
||||||
|
let startedIntegrations = 0;
|
||||||
|
let setIntegrationCalls = 0;
|
||||||
|
|
||||||
|
initializeOverlayRuntime({
|
||||||
|
backendOverride: null,
|
||||||
|
createMainWindow: () => {},
|
||||||
|
registerGlobalShortcuts: () => {},
|
||||||
|
updateVisibleOverlayBounds: () => {},
|
||||||
|
isVisibleOverlayVisible: () => false,
|
||||||
|
updateVisibleOverlayVisibility: () => {},
|
||||||
|
getOverlayWindows: () => [],
|
||||||
|
syncOverlayShortcuts: () => {},
|
||||||
|
setWindowTracker: () => {},
|
||||||
|
getMpvSocketPath: () => '/tmp/mpv.sock',
|
||||||
|
createWindowTracker: () => null,
|
||||||
|
getResolvedConfig: () => ({
|
||||||
|
ankiConnect: { enabled: true } as never,
|
||||||
|
}),
|
||||||
|
getSubtitleTimingTracker: () => ({}),
|
||||||
|
getMpvClient: () => ({
|
||||||
|
send: () => {},
|
||||||
|
}),
|
||||||
|
getRuntimeOptionsManager: () => ({
|
||||||
|
getEffectiveAnkiConnectConfig: (config) => config as never,
|
||||||
|
}),
|
||||||
|
createAnkiIntegration: (args) => {
|
||||||
|
createdIntegrations += 1;
|
||||||
|
assert.equal(args.config.enabled, true);
|
||||||
|
return {
|
||||||
|
start: () => {
|
||||||
|
startedIntegrations += 1;
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
setAnkiIntegration: () => {
|
||||||
|
setIntegrationCalls += 1;
|
||||||
|
},
|
||||||
|
showDesktopNotification: () => {},
|
||||||
|
createFieldGroupingCallback: () => async () => 'manual',
|
||||||
|
getKnownWordCacheStatePath: () => '/tmp/known-words-cache.json',
|
||||||
|
});
|
||||||
|
|
||||||
|
assert.equal(createdIntegrations, 1);
|
||||||
|
assert.equal(startedIntegrations, 1);
|
||||||
|
assert.equal(setIntegrationCalls, 1);
|
||||||
|
});
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import { BrowserWindow } from 'electron';
|
import { BrowserWindow } from 'electron';
|
||||||
import { AnkiIntegration } from '../../anki-integration';
|
|
||||||
import { BaseWindowTracker, createWindowTracker } from '../../window-trackers';
|
import { BaseWindowTracker, createWindowTracker } from '../../window-trackers';
|
||||||
import {
|
import {
|
||||||
AnkiConnectConfig,
|
AnkiConnectConfig,
|
||||||
@@ -8,6 +7,40 @@ import {
|
|||||||
WindowGeometry,
|
WindowGeometry,
|
||||||
} from '../../types';
|
} from '../../types';
|
||||||
|
|
||||||
|
type AnkiIntegrationLike = {
|
||||||
|
start: () => void;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CreateAnkiIntegrationArgs = {
|
||||||
|
config: AnkiConnectConfig;
|
||||||
|
subtitleTimingTracker: unknown;
|
||||||
|
mpvClient: { send?: (payload: { command: string[] }) => void };
|
||||||
|
showDesktopNotification: (title: string, options: { body?: string; icon?: string }) => void;
|
||||||
|
createFieldGroupingCallback: () => (
|
||||||
|
data: KikuFieldGroupingRequestData,
|
||||||
|
) => Promise<KikuFieldGroupingChoice>;
|
||||||
|
knownWordCacheStatePath: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
function createDefaultAnkiIntegration(args: CreateAnkiIntegrationArgs): AnkiIntegrationLike {
|
||||||
|
const { AnkiIntegration } = require('../../anki-integration') as typeof import('../../anki-integration');
|
||||||
|
return new AnkiIntegration(
|
||||||
|
args.config,
|
||||||
|
args.subtitleTimingTracker as never,
|
||||||
|
args.mpvClient as never,
|
||||||
|
(text: string) => {
|
||||||
|
if (args.mpvClient && typeof args.mpvClient.send === 'function') {
|
||||||
|
args.mpvClient.send({
|
||||||
|
command: ['show-text', text, '3000'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
args.showDesktopNotification,
|
||||||
|
args.createFieldGroupingCallback(),
|
||||||
|
args.knownWordCacheStatePath,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function initializeOverlayRuntime(options: {
|
export function initializeOverlayRuntime(options: {
|
||||||
backendOverride: string | null;
|
backendOverride: string | null;
|
||||||
createMainWindow: () => void;
|
createMainWindow: () => void;
|
||||||
@@ -18,6 +51,10 @@ export function initializeOverlayRuntime(options: {
|
|||||||
getOverlayWindows: () => BrowserWindow[];
|
getOverlayWindows: () => BrowserWindow[];
|
||||||
syncOverlayShortcuts: () => void;
|
syncOverlayShortcuts: () => void;
|
||||||
setWindowTracker: (tracker: BaseWindowTracker | null) => void;
|
setWindowTracker: (tracker: BaseWindowTracker | null) => void;
|
||||||
|
createWindowTracker?: (
|
||||||
|
override?: string | null,
|
||||||
|
targetMpvSocketPath?: string | null,
|
||||||
|
) => BaseWindowTracker | null;
|
||||||
getMpvSocketPath: () => string;
|
getMpvSocketPath: () => string;
|
||||||
getResolvedConfig: () => { ankiConnect?: AnkiConnectConfig };
|
getResolvedConfig: () => { ankiConnect?: AnkiConnectConfig };
|
||||||
getSubtitleTimingTracker: () => unknown | null;
|
getSubtitleTimingTracker: () => unknown | null;
|
||||||
@@ -33,11 +70,13 @@ export function initializeOverlayRuntime(options: {
|
|||||||
data: KikuFieldGroupingRequestData,
|
data: KikuFieldGroupingRequestData,
|
||||||
) => Promise<KikuFieldGroupingChoice>;
|
) => Promise<KikuFieldGroupingChoice>;
|
||||||
getKnownWordCacheStatePath: () => string;
|
getKnownWordCacheStatePath: () => string;
|
||||||
|
createAnkiIntegration?: (args: CreateAnkiIntegrationArgs) => AnkiIntegrationLike;
|
||||||
}): void {
|
}): void {
|
||||||
options.createMainWindow();
|
options.createMainWindow();
|
||||||
options.registerGlobalShortcuts();
|
options.registerGlobalShortcuts();
|
||||||
|
|
||||||
const windowTracker = createWindowTracker(options.backendOverride, options.getMpvSocketPath());
|
const createWindowTrackerHandler = options.createWindowTracker ?? createWindowTracker;
|
||||||
|
const windowTracker = createWindowTrackerHandler(options.backendOverride, options.getMpvSocketPath());
|
||||||
options.setWindowTracker(windowTracker);
|
options.setWindowTracker(windowTracker);
|
||||||
if (windowTracker) {
|
if (windowTracker) {
|
||||||
windowTracker.onGeometryChange = (geometry: WindowGeometry) => {
|
windowTracker.onGeometryChange = (geometry: WindowGeometry) => {
|
||||||
@@ -63,25 +102,24 @@ export function initializeOverlayRuntime(options: {
|
|||||||
const mpvClient = options.getMpvClient();
|
const mpvClient = options.getMpvClient();
|
||||||
const runtimeOptionsManager = options.getRuntimeOptionsManager();
|
const runtimeOptionsManager = options.getRuntimeOptionsManager();
|
||||||
|
|
||||||
if (config.ankiConnect && subtitleTimingTracker && mpvClient && runtimeOptionsManager) {
|
if (
|
||||||
|
config.ankiConnect?.enabled === true &&
|
||||||
|
subtitleTimingTracker &&
|
||||||
|
mpvClient &&
|
||||||
|
runtimeOptionsManager
|
||||||
|
) {
|
||||||
const effectiveAnkiConfig = runtimeOptionsManager.getEffectiveAnkiConnectConfig(
|
const effectiveAnkiConfig = runtimeOptionsManager.getEffectiveAnkiConnectConfig(
|
||||||
config.ankiConnect,
|
config.ankiConnect,
|
||||||
);
|
);
|
||||||
const integration = new AnkiIntegration(
|
const createAnkiIntegration = options.createAnkiIntegration ?? createDefaultAnkiIntegration;
|
||||||
effectiveAnkiConfig,
|
const integration = createAnkiIntegration({
|
||||||
subtitleTimingTracker as never,
|
config: effectiveAnkiConfig,
|
||||||
mpvClient as never,
|
subtitleTimingTracker,
|
||||||
(text: string) => {
|
mpvClient,
|
||||||
if (mpvClient && typeof mpvClient.send === 'function') {
|
showDesktopNotification: options.showDesktopNotification,
|
||||||
mpvClient.send({
|
createFieldGroupingCallback: options.createFieldGroupingCallback,
|
||||||
command: ['show-text', text, '3000'],
|
knownWordCacheStatePath: options.getKnownWordCacheStatePath(),
|
||||||
});
|
});
|
||||||
}
|
|
||||||
},
|
|
||||||
options.showDesktopNotification,
|
|
||||||
options.createFieldGroupingCallback(),
|
|
||||||
options.getKnownWordCacheStatePath(),
|
|
||||||
);
|
|
||||||
integration.start();
|
integration.start();
|
||||||
options.setAnkiIntegration(integration);
|
options.setAnkiIntegration(integration);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user