mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-03-20 12:11:28 -07:00
feat(yomitan): add read-only external profile support for shared dictionaries (#18)
This commit is contained in:
@@ -154,7 +154,7 @@ test('runAppReadyRuntime skips heavy startup when shouldSkipHeavyStartup returns
|
||||
await runAppReadyRuntime(deps);
|
||||
|
||||
assert.equal(calls.includes('ensureDefaultConfigBootstrap'), true);
|
||||
assert.equal(calls.includes('reloadConfig'), false);
|
||||
assert.equal(calls.includes('reloadConfig'), true);
|
||||
assert.equal(calls.includes('getResolvedConfig'), false);
|
||||
assert.equal(calls.includes('getConfigWarnings'), false);
|
||||
assert.equal(calls.includes('setLogLevel:warn:config'), false);
|
||||
@@ -170,6 +170,8 @@ test('runAppReadyRuntime skips heavy startup when shouldSkipHeavyStartup returns
|
||||
assert.equal(calls.includes('loadYomitanExtension'), true);
|
||||
assert.equal(calls.includes('handleFirstRunSetup'), true);
|
||||
assert.ok(calls.indexOf('loadYomitanExtension') < calls.indexOf('handleInitialArgs'));
|
||||
assert.ok(calls.indexOf('loadYomitanExtension') < calls.indexOf('reloadConfig'));
|
||||
assert.ok(calls.indexOf('reloadConfig') < calls.indexOf('handleFirstRunSetup'));
|
||||
assert.ok(calls.indexOf('loadYomitanExtension') < calls.indexOf('handleFirstRunSetup'));
|
||||
assert.ok(calls.indexOf('handleFirstRunSetup') < calls.indexOf('handleInitialArgs'));
|
||||
});
|
||||
|
||||
@@ -1,11 +1,27 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import { buildOverlayWindowOptions } from './overlay-window-options';
|
||||
|
||||
test('overlay window config explicitly disables renderer sandbox for preload compatibility', () => {
|
||||
const sourcePath = path.join(process.cwd(), 'src/core/services/overlay-window.ts');
|
||||
const source = fs.readFileSync(sourcePath, 'utf8');
|
||||
const options = buildOverlayWindowOptions('visible', {
|
||||
isDev: false,
|
||||
yomitanSession: null,
|
||||
});
|
||||
|
||||
assert.match(source, /webPreferences:\s*\{[\s\S]*sandbox:\s*false[\s\S]*\}/m);
|
||||
assert.equal(options.webPreferences?.sandbox, false);
|
||||
});
|
||||
|
||||
test('overlay window config uses the provided Yomitan session when available', () => {
|
||||
const yomitanSession = { id: 'session' } as never;
|
||||
const withSession = buildOverlayWindowOptions('visible', {
|
||||
isDev: false,
|
||||
yomitanSession,
|
||||
});
|
||||
const withoutSession = buildOverlayWindowOptions('visible', {
|
||||
isDev: false,
|
||||
yomitanSession: null,
|
||||
});
|
||||
|
||||
assert.equal(withSession.webPreferences?.session, yomitanSession);
|
||||
assert.equal(withoutSession.webPreferences?.session, undefined);
|
||||
});
|
||||
|
||||
39
src/core/services/overlay-window-options.ts
Normal file
39
src/core/services/overlay-window-options.ts
Normal file
@@ -0,0 +1,39 @@
|
||||
import type { BrowserWindowConstructorOptions, Session } from 'electron';
|
||||
import * as path from 'path';
|
||||
import type { OverlayWindowKind } from './overlay-window-input';
|
||||
|
||||
export function buildOverlayWindowOptions(
|
||||
kind: OverlayWindowKind,
|
||||
options: {
|
||||
isDev: boolean;
|
||||
yomitanSession?: Session | null;
|
||||
},
|
||||
): BrowserWindowConstructorOptions {
|
||||
const showNativeDebugFrame = process.platform === 'win32' && options.isDev;
|
||||
|
||||
return {
|
||||
show: false,
|
||||
width: 800,
|
||||
height: 600,
|
||||
x: 0,
|
||||
y: 0,
|
||||
transparent: true,
|
||||
frame: false,
|
||||
alwaysOnTop: true,
|
||||
skipTaskbar: true,
|
||||
resizable: false,
|
||||
hasShadow: false,
|
||||
focusable: true,
|
||||
acceptFirstMouse: true,
|
||||
...(process.platform === 'win32' ? { thickFrame: showNativeDebugFrame } : {}),
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, '..', '..', 'preload.js'),
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false,
|
||||
sandbox: false,
|
||||
webSecurity: true,
|
||||
session: options.yomitanSession ?? undefined,
|
||||
additionalArguments: [`--overlay-layer=${kind}`],
|
||||
},
|
||||
};
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
import { BrowserWindow } from 'electron';
|
||||
import { BrowserWindow, type Session } from 'electron';
|
||||
import * as path from 'path';
|
||||
import { WindowGeometry } from '../../types';
|
||||
import { createLogger } from '../../logger';
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
handleOverlayWindowBeforeInputEvent,
|
||||
type OverlayWindowKind,
|
||||
} from './overlay-window-input';
|
||||
import { buildOverlayWindowOptions } from './overlay-window-options';
|
||||
|
||||
const logger = createLogger('main:overlay-window');
|
||||
const overlayWindowLayerByInstance = new WeakMap<BrowserWindow, OverlayWindowKind>();
|
||||
@@ -78,33 +79,10 @@ export function createOverlayWindow(
|
||||
tryHandleOverlayShortcutLocalFallback: (input: Electron.Input) => boolean;
|
||||
forwardTabToMpv: () => void;
|
||||
onWindowClosed: (kind: OverlayWindowKind) => void;
|
||||
yomitanSession?: Session | null;
|
||||
},
|
||||
): BrowserWindow {
|
||||
const showNativeDebugFrame = process.platform === 'win32' && options.isDev;
|
||||
const window = new BrowserWindow({
|
||||
show: false,
|
||||
width: 800,
|
||||
height: 600,
|
||||
x: 0,
|
||||
y: 0,
|
||||
transparent: true,
|
||||
frame: false,
|
||||
alwaysOnTop: true,
|
||||
skipTaskbar: true,
|
||||
resizable: false,
|
||||
hasShadow: false,
|
||||
focusable: true,
|
||||
acceptFirstMouse: true,
|
||||
...(process.platform === 'win32' ? { thickFrame: showNativeDebugFrame } : {}),
|
||||
webPreferences: {
|
||||
preload: path.join(__dirname, '..', '..', 'preload.js'),
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false,
|
||||
sandbox: false,
|
||||
webSecurity: true,
|
||||
additionalArguments: [`--overlay-layer=${kind}`],
|
||||
},
|
||||
});
|
||||
const window = new BrowserWindow(buildOverlayWindowOptions(kind, options));
|
||||
|
||||
options.ensureOverlayWindowLevel(window);
|
||||
loadOverlayWindowLayer(window, kind);
|
||||
@@ -170,4 +148,5 @@ export function syncOverlayWindowLayer(window: BrowserWindow, layer: 'visible'):
|
||||
loadOverlayWindowLayer(window, layer);
|
||||
}
|
||||
|
||||
export { buildOverlayWindowOptions } from './overlay-window-options';
|
||||
export type { OverlayWindowKind } from './overlay-window-input';
|
||||
|
||||
@@ -185,6 +185,7 @@ export async function runAppReadyRuntime(deps: AppReadyRuntimeDeps): Promise<voi
|
||||
deps.ensureDefaultConfigBootstrap();
|
||||
if (deps.shouldSkipHeavyStartup?.()) {
|
||||
await deps.loadYomitanExtension();
|
||||
deps.reloadConfig();
|
||||
await deps.handleFirstRunSetup();
|
||||
deps.handleInitialArgs();
|
||||
return;
|
||||
@@ -194,6 +195,7 @@ export async function runAppReadyRuntime(deps: AppReadyRuntimeDeps): Promise<voi
|
||||
|
||||
if (deps.shouldSkipHeavyStartup?.()) {
|
||||
await deps.loadYomitanExtension();
|
||||
deps.reloadConfig();
|
||||
await deps.handleFirstRunSetup();
|
||||
deps.handleInitialArgs();
|
||||
deps.logDebug?.(`App-ready critical path finished in ${now() - startupStartedAtMs}ms.`);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { BrowserWindow, Extension } from 'electron';
|
||||
import type { BrowserWindow, Extension, Session } from 'electron';
|
||||
import { mergeTokens } from '../../token-merger';
|
||||
import { createLogger } from '../../logger';
|
||||
import {
|
||||
@@ -33,6 +33,7 @@ type MecabTokenEnrichmentFn = (
|
||||
|
||||
export interface TokenizerServiceDeps {
|
||||
getYomitanExt: () => Extension | null;
|
||||
getYomitanSession?: () => Session | null;
|
||||
getYomitanParserWindow: () => BrowserWindow | null;
|
||||
setYomitanParserWindow: (window: BrowserWindow | null) => void;
|
||||
getYomitanParserReadyPromise: () => Promise<void> | null;
|
||||
@@ -63,6 +64,7 @@ interface MecabTokenizerLike {
|
||||
|
||||
export interface TokenizerDepsRuntimeOptions {
|
||||
getYomitanExt: () => Extension | null;
|
||||
getYomitanSession?: () => Session | null;
|
||||
getYomitanParserWindow: () => BrowserWindow | null;
|
||||
setYomitanParserWindow: (window: BrowserWindow | null) => void;
|
||||
getYomitanParserReadyPromise: () => Promise<void> | null;
|
||||
@@ -182,6 +184,7 @@ export function createTokenizerDepsRuntime(
|
||||
|
||||
return {
|
||||
getYomitanExt: options.getYomitanExt,
|
||||
getYomitanSession: options.getYomitanSession,
|
||||
getYomitanParserWindow: options.getYomitanParserWindow,
|
||||
setYomitanParserWindow: options.setYomitanParserWindow,
|
||||
getYomitanParserReadyPromise: options.getYomitanParserReadyPromise,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { BrowserWindow, Extension } from 'electron';
|
||||
import type { BrowserWindow, Extension, Session } from 'electron';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { selectYomitanParseTokens } from './parser-selection-stage';
|
||||
@@ -10,6 +10,7 @@ interface LoggerLike {
|
||||
|
||||
interface YomitanParserRuntimeDeps {
|
||||
getYomitanExt: () => Extension | null;
|
||||
getYomitanSession?: () => Session | null;
|
||||
getYomitanParserWindow: () => BrowserWindow | null;
|
||||
setYomitanParserWindow: (window: BrowserWindow | null) => void;
|
||||
getYomitanParserReadyPromise: () => Promise<void> | null;
|
||||
@@ -465,6 +466,7 @@ async function ensureYomitanParserWindow(
|
||||
|
||||
const initPromise = (async () => {
|
||||
const { BrowserWindow, session } = electron;
|
||||
const yomitanSession = deps.getYomitanSession?.() ?? session.defaultSession;
|
||||
const parserWindow = new BrowserWindow({
|
||||
show: false,
|
||||
width: 800,
|
||||
@@ -472,7 +474,7 @@ async function ensureYomitanParserWindow(
|
||||
webPreferences: {
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false,
|
||||
session: session.defaultSession,
|
||||
session: yomitanSession,
|
||||
},
|
||||
});
|
||||
deps.setYomitanParserWindow(parserWindow);
|
||||
@@ -539,6 +541,7 @@ async function createYomitanExtensionWindow(
|
||||
}
|
||||
|
||||
const { BrowserWindow, session } = electron;
|
||||
const yomitanSession = deps.getYomitanSession?.() ?? session.defaultSession;
|
||||
const window = new BrowserWindow({
|
||||
show: false,
|
||||
width: 1200,
|
||||
@@ -546,7 +549,7 @@ async function createYomitanExtensionWindow(
|
||||
webPreferences: {
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false,
|
||||
session: session.defaultSession,
|
||||
session: yomitanSession,
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
@@ -1,12 +1,18 @@
|
||||
import electron from 'electron';
|
||||
import type { BrowserWindow, Extension } from 'electron';
|
||||
import type { BrowserWindow, Extension, Session } from 'electron';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import { createLogger } from '../../logger';
|
||||
import { ensureExtensionCopy } from './yomitan-extension-copy';
|
||||
import {
|
||||
getYomitanExtensionSearchPaths,
|
||||
resolveExternalYomitanExtensionPath,
|
||||
resolveExistingYomitanExtensionPath,
|
||||
} from './yomitan-extension-paths';
|
||||
import {
|
||||
clearYomitanExtensionRuntimeState,
|
||||
clearYomitanParserRuntimeState,
|
||||
} from './yomitan-extension-runtime-state';
|
||||
|
||||
const { session } = electron;
|
||||
const logger = createLogger('main:yomitan-extension-loader');
|
||||
@@ -14,51 +20,82 @@ const logger = createLogger('main:yomitan-extension-loader');
|
||||
export interface YomitanExtensionLoaderDeps {
|
||||
userDataPath: string;
|
||||
extensionPath?: string;
|
||||
externalProfilePath?: string;
|
||||
getYomitanParserWindow: () => BrowserWindow | null;
|
||||
setYomitanParserWindow: (window: BrowserWindow | null) => void;
|
||||
setYomitanParserReadyPromise: (promise: Promise<void> | null) => void;
|
||||
setYomitanParserInitPromise: (promise: Promise<boolean> | null) => void;
|
||||
setYomitanExtension: (extension: Extension | null) => void;
|
||||
setYomitanSession: (session: Session | null) => void;
|
||||
}
|
||||
|
||||
export async function loadYomitanExtension(
|
||||
deps: YomitanExtensionLoaderDeps,
|
||||
): Promise<Extension | null> {
|
||||
const searchPaths = getYomitanExtensionSearchPaths({
|
||||
explicitPath: deps.extensionPath,
|
||||
moduleDir: __dirname,
|
||||
resourcesPath: process.resourcesPath,
|
||||
userDataPath: deps.userDataPath,
|
||||
});
|
||||
let extPath = resolveExistingYomitanExtensionPath(searchPaths, fs.existsSync);
|
||||
const clearRuntimeState = () =>
|
||||
clearYomitanExtensionRuntimeState({
|
||||
getYomitanParserWindow: deps.getYomitanParserWindow,
|
||||
setYomitanParserWindow: deps.setYomitanParserWindow,
|
||||
setYomitanParserReadyPromise: deps.setYomitanParserReadyPromise,
|
||||
setYomitanParserInitPromise: deps.setYomitanParserInitPromise,
|
||||
setYomitanExtension: () => deps.setYomitanExtension(null),
|
||||
setYomitanSession: () => deps.setYomitanSession(null),
|
||||
});
|
||||
const clearParserState = () =>
|
||||
clearYomitanParserRuntimeState({
|
||||
getYomitanParserWindow: deps.getYomitanParserWindow,
|
||||
setYomitanParserWindow: deps.setYomitanParserWindow,
|
||||
setYomitanParserReadyPromise: deps.setYomitanParserReadyPromise,
|
||||
setYomitanParserInitPromise: deps.setYomitanParserInitPromise,
|
||||
});
|
||||
const externalProfilePath = deps.externalProfilePath?.trim() ?? '';
|
||||
let extPath: string | null = null;
|
||||
let targetSession: Session = session.defaultSession;
|
||||
|
||||
if (!extPath) {
|
||||
logger.error('Yomitan extension not found in any search path');
|
||||
logger.error('Run `bun run build:yomitan` or install Yomitan to one of:', searchPaths);
|
||||
return null;
|
||||
if (externalProfilePath) {
|
||||
const resolvedProfilePath = path.resolve(externalProfilePath);
|
||||
extPath = resolveExternalYomitanExtensionPath(resolvedProfilePath, fs.existsSync);
|
||||
if (!extPath) {
|
||||
logger.error('External Yomitan extension not found in configured profile path');
|
||||
logger.error('Expected unpacked extension at:', path.join(resolvedProfilePath, 'extensions'));
|
||||
clearRuntimeState();
|
||||
return null;
|
||||
}
|
||||
|
||||
targetSession = session.fromPath(resolvedProfilePath);
|
||||
} else {
|
||||
const searchPaths = getYomitanExtensionSearchPaths({
|
||||
explicitPath: deps.extensionPath,
|
||||
moduleDir: __dirname,
|
||||
resourcesPath: process.resourcesPath,
|
||||
userDataPath: deps.userDataPath,
|
||||
});
|
||||
extPath = resolveExistingYomitanExtensionPath(searchPaths, fs.existsSync);
|
||||
|
||||
if (!extPath) {
|
||||
logger.error('Yomitan extension not found in any search path');
|
||||
logger.error('Run `bun run build:yomitan` or install Yomitan to one of:', searchPaths);
|
||||
clearRuntimeState();
|
||||
return null;
|
||||
}
|
||||
|
||||
const extensionCopy = ensureExtensionCopy(extPath, deps.userDataPath);
|
||||
if (extensionCopy.copied) {
|
||||
logger.info(`Copied yomitan extension to ${extensionCopy.targetDir}`);
|
||||
}
|
||||
extPath = extensionCopy.targetDir;
|
||||
}
|
||||
|
||||
const extensionCopy = ensureExtensionCopy(extPath, deps.userDataPath);
|
||||
if (extensionCopy.copied) {
|
||||
logger.info(`Copied yomitan extension to ${extensionCopy.targetDir}`);
|
||||
}
|
||||
extPath = extensionCopy.targetDir;
|
||||
|
||||
const parserWindow = deps.getYomitanParserWindow();
|
||||
if (parserWindow && !parserWindow.isDestroyed()) {
|
||||
parserWindow.destroy();
|
||||
}
|
||||
deps.setYomitanParserWindow(null);
|
||||
deps.setYomitanParserReadyPromise(null);
|
||||
deps.setYomitanParserInitPromise(null);
|
||||
clearParserState();
|
||||
deps.setYomitanSession(targetSession);
|
||||
|
||||
try {
|
||||
const extensions = session.defaultSession.extensions;
|
||||
const extensions = targetSession.extensions;
|
||||
const extension = extensions
|
||||
? await extensions.loadExtension(extPath, {
|
||||
allowFileAccess: true,
|
||||
})
|
||||
: await session.defaultSession.loadExtension(extPath, {
|
||||
: await targetSession.loadExtension(extPath, {
|
||||
allowFileAccess: true,
|
||||
});
|
||||
deps.setYomitanExtension(extension);
|
||||
@@ -66,7 +103,7 @@ export async function loadYomitanExtension(
|
||||
} catch (err) {
|
||||
logger.error('Failed to load Yomitan extension:', (err as Error).message);
|
||||
logger.error('Full error:', err);
|
||||
deps.setYomitanExtension(null);
|
||||
clearRuntimeState();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ import test from 'node:test';
|
||||
|
||||
import {
|
||||
getYomitanExtensionSearchPaths,
|
||||
resolveExternalYomitanExtensionPath,
|
||||
resolveExistingYomitanExtensionPath,
|
||||
} from './yomitan-extension-paths';
|
||||
|
||||
@@ -51,3 +52,19 @@ test('resolveExistingYomitanExtensionPath ignores source tree without built mani
|
||||
|
||||
assert.equal(resolved, null);
|
||||
});
|
||||
|
||||
test('resolveExternalYomitanExtensionPath returns external extension dir when manifest exists', () => {
|
||||
const profilePath = path.join('/Users', 'kyle', '.local', 'share', 'gsm-profile');
|
||||
const resolved = resolveExternalYomitanExtensionPath(profilePath, (candidate) =>
|
||||
candidate === path.join(profilePath, 'extensions', 'yomitan', 'manifest.json'),
|
||||
);
|
||||
|
||||
assert.equal(resolved, path.join(profilePath, 'extensions', 'yomitan'));
|
||||
});
|
||||
|
||||
test('resolveExternalYomitanExtensionPath returns null when external profile has no extension', () => {
|
||||
const profilePath = path.join('/Users', 'kyle', '.local', 'share', 'gsm-profile');
|
||||
const resolved = resolveExternalYomitanExtensionPath(profilePath, () => false);
|
||||
|
||||
assert.equal(resolved, null);
|
||||
});
|
||||
|
||||
@@ -58,3 +58,16 @@ export function resolveYomitanExtensionPath(
|
||||
): string | null {
|
||||
return resolveExistingYomitanExtensionPath(getYomitanExtensionSearchPaths(options), existsSync);
|
||||
}
|
||||
|
||||
export function resolveExternalYomitanExtensionPath(
|
||||
externalProfilePath: string,
|
||||
existsSync: (path: string) => boolean = fs.existsSync,
|
||||
): string | null {
|
||||
const normalizedProfilePath = externalProfilePath.trim();
|
||||
if (!normalizedProfilePath) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const candidate = path.join(path.resolve(normalizedProfilePath), 'extensions', 'yomitan');
|
||||
return existsSync(path.join(candidate, 'manifest.json')) ? candidate : null;
|
||||
}
|
||||
|
||||
45
src/core/services/yomitan-extension-runtime-state.test.ts
Normal file
45
src/core/services/yomitan-extension-runtime-state.test.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { clearYomitanParserRuntimeState } from './yomitan-extension-runtime-state';
|
||||
|
||||
test('clearYomitanParserRuntimeState destroys parser window and clears parser promises', () => {
|
||||
const calls: string[] = [];
|
||||
const parserWindow = {
|
||||
isDestroyed: () => false,
|
||||
destroy: () => {
|
||||
calls.push('destroy');
|
||||
},
|
||||
};
|
||||
|
||||
clearYomitanParserRuntimeState({
|
||||
getYomitanParserWindow: () => parserWindow as never,
|
||||
setYomitanParserWindow: (window) => calls.push(`window:${window === null ? 'null' : 'set'}`),
|
||||
setYomitanParserReadyPromise: (promise) =>
|
||||
calls.push(`ready:${promise === null ? 'null' : 'set'}`),
|
||||
setYomitanParserInitPromise: (promise) =>
|
||||
calls.push(`init:${promise === null ? 'null' : 'set'}`),
|
||||
});
|
||||
|
||||
assert.deepEqual(calls, ['destroy', 'window:null', 'ready:null', 'init:null']);
|
||||
});
|
||||
|
||||
test('clearYomitanParserRuntimeState skips destroy when parser window is already gone', () => {
|
||||
const calls: string[] = [];
|
||||
const parserWindow = {
|
||||
isDestroyed: () => true,
|
||||
destroy: () => {
|
||||
calls.push('destroy');
|
||||
},
|
||||
};
|
||||
|
||||
clearYomitanParserRuntimeState({
|
||||
getYomitanParserWindow: () => parserWindow as never,
|
||||
setYomitanParserWindow: (window) => calls.push(`window:${window === null ? 'null' : 'set'}`),
|
||||
setYomitanParserReadyPromise: (promise) =>
|
||||
calls.push(`ready:${promise === null ? 'null' : 'set'}`),
|
||||
setYomitanParserInitPromise: (promise) =>
|
||||
calls.push(`init:${promise === null ? 'null' : 'set'}`),
|
||||
});
|
||||
|
||||
assert.deepEqual(calls, ['window:null', 'ready:null', 'init:null']);
|
||||
});
|
||||
34
src/core/services/yomitan-extension-runtime-state.ts
Normal file
34
src/core/services/yomitan-extension-runtime-state.ts
Normal file
@@ -0,0 +1,34 @@
|
||||
type ParserWindowLike = {
|
||||
isDestroyed?: () => boolean;
|
||||
destroy?: () => void;
|
||||
} | null;
|
||||
|
||||
export interface YomitanParserRuntimeStateDeps {
|
||||
getYomitanParserWindow: () => ParserWindowLike;
|
||||
setYomitanParserWindow: (window: null) => void;
|
||||
setYomitanParserReadyPromise: (promise: Promise<void> | null) => void;
|
||||
setYomitanParserInitPromise: (promise: Promise<boolean> | null) => void;
|
||||
}
|
||||
|
||||
export interface YomitanExtensionRuntimeStateDeps extends YomitanParserRuntimeStateDeps {
|
||||
setYomitanExtension: (extension: null) => void;
|
||||
setYomitanSession: (session: null) => void;
|
||||
}
|
||||
|
||||
export function clearYomitanParserRuntimeState(deps: YomitanParserRuntimeStateDeps): void {
|
||||
const parserWindow = deps.getYomitanParserWindow();
|
||||
if (parserWindow && !parserWindow.isDestroyed?.()) {
|
||||
parserWindow.destroy?.();
|
||||
}
|
||||
deps.setYomitanParserWindow(null);
|
||||
deps.setYomitanParserReadyPromise(null);
|
||||
deps.setYomitanParserInitPromise(null);
|
||||
}
|
||||
|
||||
export function clearYomitanExtensionRuntimeState(
|
||||
deps: YomitanExtensionRuntimeStateDeps,
|
||||
): void {
|
||||
clearYomitanParserRuntimeState(deps);
|
||||
deps.setYomitanExtension(null);
|
||||
deps.setYomitanSession(null);
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import electron from 'electron';
|
||||
import type { BrowserWindow, Extension } from 'electron';
|
||||
import type { BrowserWindow, Extension, Session } from 'electron';
|
||||
import { createLogger } from '../../logger';
|
||||
|
||||
const { BrowserWindow: ElectronBrowserWindow, session } = electron;
|
||||
@@ -9,6 +9,7 @@ export interface OpenYomitanSettingsWindowOptions {
|
||||
yomitanExt: Extension | null;
|
||||
getExistingWindow: () => BrowserWindow | null;
|
||||
setWindow: (window: BrowserWindow | null) => void;
|
||||
yomitanSession?: Session | null;
|
||||
onWindowClosed?: () => void;
|
||||
}
|
||||
|
||||
@@ -37,7 +38,7 @@ export function openYomitanSettingsWindow(options: OpenYomitanSettingsWindowOpti
|
||||
webPreferences: {
|
||||
contextIsolation: true,
|
||||
nodeIntegration: false,
|
||||
session: session.defaultSession,
|
||||
session: options.yomitanSession ?? session.defaultSession,
|
||||
},
|
||||
});
|
||||
options.setWindow(settingsWindow);
|
||||
|
||||
Reference in New Issue
Block a user