mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-01 18:22:41 -08:00
155 lines
4.6 KiB
TypeScript
155 lines
4.6 KiB
TypeScript
import type { BrowserWindow, Extension } from 'electron';
|
|
|
|
interface LoggerLike {
|
|
error: (message: string, ...args: unknown[]) => void;
|
|
}
|
|
|
|
interface YomitanParserRuntimeDeps {
|
|
getYomitanExt: () => Extension | null;
|
|
getYomitanParserWindow: () => BrowserWindow | null;
|
|
setYomitanParserWindow: (window: BrowserWindow | null) => void;
|
|
getYomitanParserReadyPromise: () => Promise<void> | null;
|
|
setYomitanParserReadyPromise: (promise: Promise<void> | null) => void;
|
|
getYomitanParserInitPromise: () => Promise<boolean> | null;
|
|
setYomitanParserInitPromise: (promise: Promise<boolean> | null) => void;
|
|
}
|
|
|
|
async function ensureYomitanParserWindow(
|
|
deps: YomitanParserRuntimeDeps,
|
|
logger: LoggerLike,
|
|
): Promise<boolean> {
|
|
const electron = await import('electron');
|
|
const yomitanExt = deps.getYomitanExt();
|
|
if (!yomitanExt) {
|
|
return false;
|
|
}
|
|
|
|
const currentWindow = deps.getYomitanParserWindow();
|
|
if (currentWindow && !currentWindow.isDestroyed()) {
|
|
return true;
|
|
}
|
|
|
|
const existingInitPromise = deps.getYomitanParserInitPromise();
|
|
if (existingInitPromise) {
|
|
return existingInitPromise;
|
|
}
|
|
|
|
const initPromise = (async () => {
|
|
const { BrowserWindow, session } = electron;
|
|
const parserWindow = new BrowserWindow({
|
|
show: false,
|
|
width: 800,
|
|
height: 600,
|
|
webPreferences: {
|
|
contextIsolation: true,
|
|
nodeIntegration: false,
|
|
session: session.defaultSession,
|
|
},
|
|
});
|
|
deps.setYomitanParserWindow(parserWindow);
|
|
|
|
deps.setYomitanParserReadyPromise(
|
|
new Promise((resolve, reject) => {
|
|
parserWindow.webContents.once('did-finish-load', () => resolve());
|
|
parserWindow.webContents.once('did-fail-load', (_event, _errorCode, errorDescription) => {
|
|
reject(new Error(errorDescription));
|
|
});
|
|
}),
|
|
);
|
|
|
|
parserWindow.on('closed', () => {
|
|
if (deps.getYomitanParserWindow() === parserWindow) {
|
|
deps.setYomitanParserWindow(null);
|
|
deps.setYomitanParserReadyPromise(null);
|
|
}
|
|
});
|
|
|
|
try {
|
|
await parserWindow.loadURL(`chrome-extension://${yomitanExt.id}/search.html`);
|
|
const readyPromise = deps.getYomitanParserReadyPromise();
|
|
if (readyPromise) {
|
|
await readyPromise;
|
|
}
|
|
|
|
return true;
|
|
} catch (err) {
|
|
logger.error('Failed to initialize Yomitan parser window:', (err as Error).message);
|
|
if (!parserWindow.isDestroyed()) {
|
|
parserWindow.destroy();
|
|
}
|
|
if (deps.getYomitanParserWindow() === parserWindow) {
|
|
deps.setYomitanParserWindow(null);
|
|
deps.setYomitanParserReadyPromise(null);
|
|
}
|
|
|
|
return false;
|
|
} finally {
|
|
deps.setYomitanParserInitPromise(null);
|
|
}
|
|
})();
|
|
|
|
deps.setYomitanParserInitPromise(initPromise);
|
|
return initPromise;
|
|
}
|
|
|
|
export async function requestYomitanParseResults(
|
|
text: string,
|
|
deps: YomitanParserRuntimeDeps,
|
|
logger: LoggerLike,
|
|
): Promise<unknown[] | null> {
|
|
const yomitanExt = deps.getYomitanExt();
|
|
if (!text || !yomitanExt) {
|
|
return null;
|
|
}
|
|
|
|
const isReady = await ensureYomitanParserWindow(deps, logger);
|
|
const parserWindow = deps.getYomitanParserWindow();
|
|
if (!isReady || !parserWindow || parserWindow.isDestroyed()) {
|
|
return null;
|
|
}
|
|
|
|
const script = `
|
|
(async () => {
|
|
const invoke = (action, params) =>
|
|
new Promise((resolve, reject) => {
|
|
chrome.runtime.sendMessage({ action, params }, (response) => {
|
|
if (chrome.runtime.lastError) {
|
|
reject(new Error(chrome.runtime.lastError.message));
|
|
return;
|
|
}
|
|
if (!response || typeof response !== "object") {
|
|
reject(new Error("Invalid response from Yomitan backend"));
|
|
return;
|
|
}
|
|
if (response.error) {
|
|
reject(new Error(response.error.message || "Yomitan backend error"));
|
|
return;
|
|
}
|
|
resolve(response.result);
|
|
});
|
|
});
|
|
|
|
const optionsFull = await invoke("optionsGetFull", undefined);
|
|
const profileIndex = optionsFull.profileCurrent;
|
|
const scanLength =
|
|
optionsFull.profiles?.[profileIndex]?.options?.scanning?.length ?? 40;
|
|
|
|
return await invoke("parseText", {
|
|
text: ${JSON.stringify(text)},
|
|
optionsContext: { index: profileIndex },
|
|
scanLength,
|
|
useInternalParser: true,
|
|
useMecabParser: true
|
|
});
|
|
})();
|
|
`;
|
|
|
|
try {
|
|
const parseResults = await parserWindow.webContents.executeJavaScript(script, true);
|
|
return Array.isArray(parseResults) ? parseResults : null;
|
|
} catch (err) {
|
|
logger.error('Yomitan parser request failed:', (err as Error).message);
|
|
return null;
|
|
}
|
|
}
|