mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-01 06:22:44 -08:00
refactor: extract core ipc handler registration service
This commit is contained in:
157
src/core/services/ipc-service.ts
Normal file
157
src/core/services/ipc-service.ts
Normal file
@@ -0,0 +1,157 @@
|
|||||||
|
import { BrowserWindow, ipcMain, IpcMainEvent } from "electron";
|
||||||
|
|
||||||
|
export interface IpcServiceDeps {
|
||||||
|
getInvisibleWindow: () => BrowserWindow | null;
|
||||||
|
isVisibleOverlayVisible: () => boolean;
|
||||||
|
setInvisibleIgnoreMouseEvents: (ignore: boolean, options?: { forward?: boolean }) => void;
|
||||||
|
onOverlayModalClosed: (modal: string) => void;
|
||||||
|
openYomitanSettings: () => void;
|
||||||
|
quitApp: () => void;
|
||||||
|
toggleDevTools: () => void;
|
||||||
|
getVisibleOverlayVisibility: () => boolean;
|
||||||
|
toggleVisibleOverlay: () => void;
|
||||||
|
getInvisibleOverlayVisibility: () => boolean;
|
||||||
|
tokenizeCurrentSubtitle: () => Promise<unknown>;
|
||||||
|
getCurrentSubtitleAss: () => string;
|
||||||
|
getMpvSubtitleRenderMetrics: () => unknown;
|
||||||
|
getSubtitlePosition: () => unknown;
|
||||||
|
getSubtitleStyle: () => unknown;
|
||||||
|
saveSubtitlePosition: (position: unknown) => void;
|
||||||
|
getMecabStatus: () => { available: boolean; enabled: boolean; path: string | null };
|
||||||
|
setMecabEnabled: (enabled: boolean) => void;
|
||||||
|
handleMpvCommand: (command: Array<string | number>) => void;
|
||||||
|
getKeybindings: () => unknown;
|
||||||
|
getSecondarySubMode: () => unknown;
|
||||||
|
getCurrentSecondarySub: () => string;
|
||||||
|
runSubsyncManual: (request: unknown) => Promise<unknown>;
|
||||||
|
getAnkiConnectStatus: () => boolean;
|
||||||
|
getRuntimeOptions: () => unknown;
|
||||||
|
setRuntimeOption: (id: string, value: unknown) => unknown;
|
||||||
|
cycleRuntimeOption: (id: string, direction: 1 | -1) => unknown;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function registerIpcHandlersService(deps: IpcServiceDeps): void {
|
||||||
|
ipcMain.on(
|
||||||
|
"set-ignore-mouse-events",
|
||||||
|
(
|
||||||
|
event: IpcMainEvent,
|
||||||
|
ignore: boolean,
|
||||||
|
options: { forward?: boolean } = {},
|
||||||
|
) => {
|
||||||
|
const senderWindow = BrowserWindow.fromWebContents(event.sender);
|
||||||
|
if (senderWindow && !senderWindow.isDestroyed()) {
|
||||||
|
const invisibleWindow = deps.getInvisibleWindow();
|
||||||
|
if (
|
||||||
|
senderWindow === invisibleWindow &&
|
||||||
|
deps.isVisibleOverlayVisible() &&
|
||||||
|
invisibleWindow &&
|
||||||
|
!invisibleWindow.isDestroyed()
|
||||||
|
) {
|
||||||
|
deps.setInvisibleIgnoreMouseEvents(true, { forward: true });
|
||||||
|
} else {
|
||||||
|
senderWindow.setIgnoreMouseEvents(ignore, options);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
ipcMain.on("overlay:modal-closed", (_event: IpcMainEvent, modal: string) => {
|
||||||
|
deps.onOverlayModalClosed(modal);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on("open-yomitan-settings", () => {
|
||||||
|
deps.openYomitanSettings();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on("quit-app", () => {
|
||||||
|
deps.quitApp();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on("toggle-dev-tools", () => {
|
||||||
|
deps.toggleDevTools();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("get-overlay-visibility", () => {
|
||||||
|
return deps.getVisibleOverlayVisibility();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on("toggle-overlay", () => {
|
||||||
|
deps.toggleVisibleOverlay();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("get-visible-overlay-visibility", () => {
|
||||||
|
return deps.getVisibleOverlayVisibility();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("get-invisible-overlay-visibility", () => {
|
||||||
|
return deps.getInvisibleOverlayVisibility();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("get-current-subtitle", async () => {
|
||||||
|
return await deps.tokenizeCurrentSubtitle();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("get-current-subtitle-ass", () => {
|
||||||
|
return deps.getCurrentSubtitleAss();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("get-mpv-subtitle-render-metrics", () => {
|
||||||
|
return deps.getMpvSubtitleRenderMetrics();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("get-subtitle-position", () => {
|
||||||
|
return deps.getSubtitlePosition();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("get-subtitle-style", () => {
|
||||||
|
return deps.getSubtitleStyle();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on("save-subtitle-position", (_event: IpcMainEvent, position: unknown) => {
|
||||||
|
deps.saveSubtitlePosition(position);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("get-mecab-status", () => {
|
||||||
|
return deps.getMecabStatus();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on("set-mecab-enabled", (_event: IpcMainEvent, enabled: boolean) => {
|
||||||
|
deps.setMecabEnabled(enabled);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.on("mpv-command", (_event: IpcMainEvent, command: (string | number)[]) => {
|
||||||
|
deps.handleMpvCommand(command);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("get-keybindings", () => {
|
||||||
|
return deps.getKeybindings();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("get-secondary-sub-mode", () => {
|
||||||
|
return deps.getSecondarySubMode();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("get-current-secondary-sub", () => {
|
||||||
|
return deps.getCurrentSecondarySub();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("subsync:run-manual", async (_event, request: unknown) => {
|
||||||
|
return await deps.runSubsyncManual(request);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("get-anki-connect-status", () => {
|
||||||
|
return deps.getAnkiConnectStatus();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("runtime-options:get", () => {
|
||||||
|
return deps.getRuntimeOptions();
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("runtime-options:set", (_event, id: string, value: unknown) => {
|
||||||
|
return deps.setRuntimeOption(id, value);
|
||||||
|
});
|
||||||
|
|
||||||
|
ipcMain.handle("runtime-options:cycle", (_event, id: string, direction: 1 | -1) => {
|
||||||
|
return deps.cycleRuntimeOption(id, direction);
|
||||||
|
});
|
||||||
|
}
|
||||||
333
src/main.ts
333
src/main.ts
@@ -130,6 +130,7 @@ import {
|
|||||||
SubtitleWebSocketService,
|
SubtitleWebSocketService,
|
||||||
} from "./core/services/subtitle-ws-service";
|
} from "./core/services/subtitle-ws-service";
|
||||||
import { registerGlobalShortcutsService } from "./core/services/shortcut-service";
|
import { registerGlobalShortcutsService } from "./core/services/shortcut-service";
|
||||||
|
import { registerIpcHandlersService } from "./core/services/ipc-service";
|
||||||
import {
|
import {
|
||||||
ConfigService,
|
ConfigService,
|
||||||
DEFAULT_CONFIG,
|
DEFAULT_CONFIG,
|
||||||
@@ -3775,234 +3776,138 @@ function toggleOverlay(): void {
|
|||||||
toggleVisibleOverlay();
|
toggleVisibleOverlay();
|
||||||
}
|
}
|
||||||
|
|
||||||
ipcMain.on(
|
function handleOverlayModalClosed(modal: OverlayHostedModal): void {
|
||||||
"set-ignore-mouse-events",
|
if (!restoreVisibleOverlayOnModalClose.has(modal)) return;
|
||||||
(
|
restoreVisibleOverlayOnModalClose.delete(modal);
|
||||||
event: IpcMainEvent,
|
if (restoreVisibleOverlayOnModalClose.size === 0) {
|
||||||
ignore: boolean,
|
setVisibleOverlayVisible(false);
|
||||||
options: { forward?: boolean } = {},
|
|
||||||
) => {
|
|
||||||
const senderWindow = BrowserWindow.fromWebContents(event.sender);
|
|
||||||
if (senderWindow && !senderWindow.isDestroyed()) {
|
|
||||||
if (
|
|
||||||
senderWindow === invisibleWindow &&
|
|
||||||
visibleOverlayVisible &&
|
|
||||||
!invisibleWindow.isDestroyed()
|
|
||||||
) {
|
|
||||||
invisibleWindow.setIgnoreMouseEvents(true, { forward: true });
|
|
||||||
} else {
|
|
||||||
senderWindow.setIgnoreMouseEvents(ignore, options);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
ipcMain.on(
|
|
||||||
"overlay:modal-closed",
|
|
||||||
(_event: IpcMainEvent, modal: OverlayHostedModal) => {
|
|
||||||
if (!restoreVisibleOverlayOnModalClose.has(modal)) return;
|
|
||||||
restoreVisibleOverlayOnModalClose.delete(modal);
|
|
||||||
if (restoreVisibleOverlayOnModalClose.size === 0) {
|
|
||||||
setVisibleOverlayVisible(false);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
ipcMain.on("open-yomitan-settings", () => {
|
|
||||||
openYomitanSettings();
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on("quit-app", () => {
|
|
||||||
app.quit();
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on("toggle-dev-tools", () => {
|
|
||||||
if (mainWindow && !mainWindow.isDestroyed()) {
|
|
||||||
mainWindow.webContents.toggleDevTools();
|
|
||||||
}
|
}
|
||||||
});
|
}
|
||||||
|
|
||||||
ipcMain.handle("get-overlay-visibility", () => {
|
function handleMpvCommandFromIpc(command: (string | number)[]): void {
|
||||||
return visibleOverlayVisible;
|
const first = typeof command[0] === "string" ? command[0] : "";
|
||||||
});
|
if (first === SPECIAL_COMMANDS.SUBSYNC_TRIGGER) {
|
||||||
|
triggerSubsyncFromConfig();
|
||||||
ipcMain.on("toggle-overlay", () => {
|
return;
|
||||||
toggleVisibleOverlay();
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.handle("get-visible-overlay-visibility", () => {
|
|
||||||
return visibleOverlayVisible;
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.handle("get-invisible-overlay-visibility", () => {
|
|
||||||
return invisibleOverlayVisible;
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.handle("get-current-subtitle", async () => {
|
|
||||||
return await tokenizeSubtitle(currentSubText);
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.handle("get-current-subtitle-ass", () => {
|
|
||||||
return currentSubAssText;
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.handle("get-mpv-subtitle-render-metrics", () => {
|
|
||||||
return mpvSubtitleRenderMetrics;
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.handle("get-subtitle-position", () => {
|
|
||||||
return loadSubtitlePosition();
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.handle("get-subtitle-style", () => {
|
|
||||||
const config = getResolvedConfig();
|
|
||||||
return config.subtitleStyle ?? null;
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on(
|
|
||||||
"save-subtitle-position",
|
|
||||||
(_event: IpcMainEvent, position: SubtitlePosition) => {
|
|
||||||
saveSubtitlePosition(position);
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
ipcMain.handle("get-mecab-status", () => {
|
|
||||||
if (mecabTokenizer) {
|
|
||||||
return mecabTokenizer.getStatus();
|
|
||||||
}
|
}
|
||||||
return { available: false, enabled: false, path: null };
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on("set-mecab-enabled", (_event: IpcMainEvent, enabled: boolean) => {
|
if (first === SPECIAL_COMMANDS.RUNTIME_OPTIONS_OPEN) {
|
||||||
if (mecabTokenizer) {
|
openRuntimeOptionsPalette();
|
||||||
mecabTokenizer.setEnabled(enabled);
|
return;
|
||||||
}
|
}
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.on(
|
if (first.startsWith(SPECIAL_COMMANDS.RUNTIME_OPTION_CYCLE_PREFIX)) {
|
||||||
"mpv-command",
|
if (!runtimeOptionsManager) return;
|
||||||
(_event: IpcMainEvent, command: (string | number)[]) => {
|
const [, idToken, directionToken] = first.split(":");
|
||||||
const first = typeof command[0] === "string" ? command[0] : "";
|
const id = idToken as RuntimeOptionId;
|
||||||
if (first === SPECIAL_COMMANDS.SUBSYNC_TRIGGER) {
|
const direction: 1 | -1 = directionToken === "prev" ? -1 : 1;
|
||||||
triggerSubsyncFromConfig();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (first === SPECIAL_COMMANDS.RUNTIME_OPTIONS_OPEN) {
|
|
||||||
openRuntimeOptionsPalette();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (first.startsWith(SPECIAL_COMMANDS.RUNTIME_OPTION_CYCLE_PREFIX)) {
|
|
||||||
if (!runtimeOptionsManager) return;
|
|
||||||
const [, idToken, directionToken] = first.split(":");
|
|
||||||
const id = idToken as RuntimeOptionId;
|
|
||||||
const direction: 1 | -1 = directionToken === "prev" ? -1 : 1;
|
|
||||||
const result = applyRuntimeOptionResult(
|
|
||||||
runtimeOptionsManager.cycleOption(id, direction),
|
|
||||||
);
|
|
||||||
if (!result.ok && result.error) {
|
|
||||||
showMpvOsd(result.error);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mpvClient && mpvClient.connected) {
|
|
||||||
if (first === SPECIAL_COMMANDS.REPLAY_SUBTITLE) {
|
|
||||||
mpvClient.replayCurrentSubtitle();
|
|
||||||
} else if (first === SPECIAL_COMMANDS.PLAY_NEXT_SUBTITLE) {
|
|
||||||
mpvClient.playNextSubtitle();
|
|
||||||
} else {
|
|
||||||
mpvClient.send({ command });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
ipcMain.handle("get-keybindings", () => {
|
|
||||||
return keybindings;
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.handle("get-secondary-sub-mode", () => {
|
|
||||||
return secondarySubMode;
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.handle("get-current-secondary-sub", () => {
|
|
||||||
return mpvClient?.currentSecondarySubText || "";
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.handle(
|
|
||||||
"subsync:run-manual",
|
|
||||||
async (_event, request: SubsyncManualRunRequest): Promise<SubsyncResult> => {
|
|
||||||
if (subsyncInProgress) {
|
|
||||||
const busy = "Subsync already running";
|
|
||||||
showMpvOsd(busy);
|
|
||||||
return { ok: false, message: busy };
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
subsyncInProgress = true;
|
|
||||||
const result = await runWithSubsyncSpinner(() =>
|
|
||||||
runSubsyncManual(request),
|
|
||||||
);
|
|
||||||
showMpvOsd(result.message);
|
|
||||||
return result;
|
|
||||||
} catch (error) {
|
|
||||||
const message = `Subsync failed: ${(error as Error).message}`;
|
|
||||||
showMpvOsd(message);
|
|
||||||
return { ok: false, message };
|
|
||||||
} finally {
|
|
||||||
subsyncInProgress = false;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
ipcMain.handle("get-anki-connect-status", () => {
|
|
||||||
return ankiIntegration !== null;
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.handle("runtime-options:get", (): RuntimeOptionState[] => {
|
|
||||||
return getRuntimeOptionsState();
|
|
||||||
});
|
|
||||||
|
|
||||||
ipcMain.handle(
|
|
||||||
"runtime-options:set",
|
|
||||||
(
|
|
||||||
_event,
|
|
||||||
id: RuntimeOptionId,
|
|
||||||
value: RuntimeOptionValue,
|
|
||||||
): RuntimeOptionApplyResult => {
|
|
||||||
if (!runtimeOptionsManager) {
|
|
||||||
return { ok: false, error: "Runtime options manager unavailable" };
|
|
||||||
}
|
|
||||||
const result = applyRuntimeOptionResult(
|
|
||||||
runtimeOptionsManager.setOptionValue(id, value),
|
|
||||||
);
|
|
||||||
if (!result.ok && result.error) {
|
|
||||||
showMpvOsd(result.error);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
},
|
|
||||||
);
|
|
||||||
|
|
||||||
ipcMain.handle(
|
|
||||||
"runtime-options:cycle",
|
|
||||||
(
|
|
||||||
_event,
|
|
||||||
id: RuntimeOptionId,
|
|
||||||
direction: 1 | -1,
|
|
||||||
): RuntimeOptionApplyResult => {
|
|
||||||
if (!runtimeOptionsManager) {
|
|
||||||
return { ok: false, error: "Runtime options manager unavailable" };
|
|
||||||
}
|
|
||||||
const result = applyRuntimeOptionResult(
|
const result = applyRuntimeOptionResult(
|
||||||
runtimeOptionsManager.cycleOption(id, direction),
|
runtimeOptionsManager.cycleOption(id, direction),
|
||||||
);
|
);
|
||||||
if (!result.ok && result.error) {
|
if (!result.ok && result.error) {
|
||||||
showMpvOsd(result.error);
|
showMpvOsd(result.error);
|
||||||
}
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mpvClient && mpvClient.connected) {
|
||||||
|
if (first === SPECIAL_COMMANDS.REPLAY_SUBTITLE) {
|
||||||
|
mpvClient.replayCurrentSubtitle();
|
||||||
|
} else if (first === SPECIAL_COMMANDS.PLAY_NEXT_SUBTITLE) {
|
||||||
|
mpvClient.playNextSubtitle();
|
||||||
|
} else {
|
||||||
|
mpvClient.send({ command });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function runSubsyncManualFromIpc(
|
||||||
|
request: SubsyncManualRunRequest,
|
||||||
|
): Promise<SubsyncResult> {
|
||||||
|
if (subsyncInProgress) {
|
||||||
|
const busy = "Subsync already running";
|
||||||
|
showMpvOsd(busy);
|
||||||
|
return { ok: false, message: busy };
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
subsyncInProgress = true;
|
||||||
|
const result = await runWithSubsyncSpinner(() => runSubsyncManual(request));
|
||||||
|
showMpvOsd(result.message);
|
||||||
|
return result;
|
||||||
|
} catch (error) {
|
||||||
|
const message = `Subsync failed: ${(error as Error).message}`;
|
||||||
|
showMpvOsd(message);
|
||||||
|
return { ok: false, message };
|
||||||
|
} finally {
|
||||||
|
subsyncInProgress = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
registerIpcHandlersService({
|
||||||
|
getInvisibleWindow: () => invisibleWindow,
|
||||||
|
isVisibleOverlayVisible: () => visibleOverlayVisible,
|
||||||
|
setInvisibleIgnoreMouseEvents: (ignore, options) => {
|
||||||
|
if (!invisibleWindow || invisibleWindow.isDestroyed()) return;
|
||||||
|
invisibleWindow.setIgnoreMouseEvents(ignore, options);
|
||||||
|
},
|
||||||
|
onOverlayModalClosed: (modal) =>
|
||||||
|
handleOverlayModalClosed(modal as OverlayHostedModal),
|
||||||
|
openYomitanSettings: () => openYomitanSettings(),
|
||||||
|
quitApp: () => app.quit(),
|
||||||
|
toggleDevTools: () => {
|
||||||
|
if (mainWindow && !mainWindow.isDestroyed()) {
|
||||||
|
mainWindow.webContents.toggleDevTools();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getVisibleOverlayVisibility: () => visibleOverlayVisible,
|
||||||
|
toggleVisibleOverlay: () => toggleVisibleOverlay(),
|
||||||
|
getInvisibleOverlayVisibility: () => invisibleOverlayVisible,
|
||||||
|
tokenizeCurrentSubtitle: () => tokenizeSubtitle(currentSubText),
|
||||||
|
getCurrentSubtitleAss: () => currentSubAssText,
|
||||||
|
getMpvSubtitleRenderMetrics: () => mpvSubtitleRenderMetrics,
|
||||||
|
getSubtitlePosition: () => loadSubtitlePosition(),
|
||||||
|
getSubtitleStyle: () => getResolvedConfig().subtitleStyle ?? null,
|
||||||
|
saveSubtitlePosition: (position) => saveSubtitlePosition(position as SubtitlePosition),
|
||||||
|
getMecabStatus: () =>
|
||||||
|
mecabTokenizer
|
||||||
|
? mecabTokenizer.getStatus()
|
||||||
|
: { available: false, enabled: false, path: null },
|
||||||
|
setMecabEnabled: (enabled) => {
|
||||||
|
if (mecabTokenizer) mecabTokenizer.setEnabled(enabled);
|
||||||
|
},
|
||||||
|
handleMpvCommand: (command) => handleMpvCommandFromIpc(command),
|
||||||
|
getKeybindings: () => keybindings,
|
||||||
|
getSecondarySubMode: () => secondarySubMode,
|
||||||
|
getCurrentSecondarySub: () => mpvClient?.currentSecondarySubText || "",
|
||||||
|
runSubsyncManual: (request) =>
|
||||||
|
runSubsyncManualFromIpc(request as SubsyncManualRunRequest),
|
||||||
|
getAnkiConnectStatus: () => ankiIntegration !== null,
|
||||||
|
getRuntimeOptions: () => getRuntimeOptionsState(),
|
||||||
|
setRuntimeOption: (id, value) => {
|
||||||
|
if (!runtimeOptionsManager) {
|
||||||
|
return { ok: false, error: "Runtime options manager unavailable" };
|
||||||
|
}
|
||||||
|
const result = applyRuntimeOptionResult(
|
||||||
|
runtimeOptionsManager.setOptionValue(id as RuntimeOptionId, value as RuntimeOptionValue),
|
||||||
|
);
|
||||||
|
if (!result.ok && result.error) {
|
||||||
|
showMpvOsd(result.error);
|
||||||
|
}
|
||||||
return result;
|
return result;
|
||||||
},
|
},
|
||||||
);
|
cycleRuntimeOption: (id, direction) => {
|
||||||
|
if (!runtimeOptionsManager) {
|
||||||
|
return { ok: false, error: "Runtime options manager unavailable" };
|
||||||
|
}
|
||||||
|
const result = applyRuntimeOptionResult(
|
||||||
|
runtimeOptionsManager.cycleOption(id as RuntimeOptionId, direction),
|
||||||
|
);
|
||||||
|
if (!result.ok && result.error) {
|
||||||
|
showMpvOsd(result.error);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create and show a desktop notification with robust icon handling.
|
* Create and show a desktop notification with robust icon handling.
|
||||||
|
|||||||
Reference in New Issue
Block a user