mirror of
https://github.com/ksyasuda/SubMiner.git
synced 2026-02-28 06:22:45 -08:00
refactor: remove invisible subtitle overlay code
This commit is contained in:
@@ -17,9 +17,7 @@ export interface CliCommandRuntimeServiceContext {
|
||||
isOverlayInitialized: () => boolean;
|
||||
initializeOverlay: () => void;
|
||||
toggleVisibleOverlay: () => void;
|
||||
toggleInvisibleOverlay: () => void;
|
||||
setVisibleOverlay: (visible: boolean) => void;
|
||||
setInvisibleOverlay: (visible: boolean) => void;
|
||||
copyCurrentSubtitle: () => void;
|
||||
startPendingMultiCopy: (timeoutMs: number) => void;
|
||||
mineSentenceCard: () => Promise<void>;
|
||||
@@ -74,9 +72,7 @@ function createCliCommandDepsFromContext(
|
||||
isInitialized: context.isOverlayInitialized,
|
||||
initialize: context.initializeOverlay,
|
||||
toggleVisible: context.toggleVisibleOverlay,
|
||||
toggleInvisible: context.toggleInvisibleOverlay,
|
||||
setVisible: context.setVisibleOverlay,
|
||||
setInvisible: context.setInvisibleOverlay,
|
||||
},
|
||||
mining: {
|
||||
copyCurrentSubtitle: context.copyCurrentSubtitle,
|
||||
|
||||
@@ -53,11 +53,10 @@ export function createSubsyncRuntimeDeps(params: SubsyncRuntimeDepsParams): Subs
|
||||
}
|
||||
|
||||
export interface MainIpcRuntimeServiceDepsParams {
|
||||
getInvisibleWindow: IpcDepsRuntimeOptions['getInvisibleWindow'];
|
||||
getMainWindow: IpcDepsRuntimeOptions['getMainWindow'];
|
||||
getVisibleOverlayVisibility: IpcDepsRuntimeOptions['getVisibleOverlayVisibility'];
|
||||
getInvisibleOverlayVisibility: IpcDepsRuntimeOptions['getInvisibleOverlayVisibility'];
|
||||
onOverlayModalClosed: IpcDepsRuntimeOptions['onOverlayModalClosed'];
|
||||
onOverlayModalOpened?: IpcDepsRuntimeOptions['onOverlayModalOpened'];
|
||||
openYomitanSettings: IpcDepsRuntimeOptions['openYomitanSettings'];
|
||||
quitApp: IpcDepsRuntimeOptions['quitApp'];
|
||||
toggleVisibleOverlay: IpcDepsRuntimeOptions['toggleVisibleOverlay'];
|
||||
@@ -65,7 +64,6 @@ export interface MainIpcRuntimeServiceDepsParams {
|
||||
getCurrentSubtitleRaw: IpcDepsRuntimeOptions['getCurrentSubtitleRaw'];
|
||||
getCurrentSubtitleAss: IpcDepsRuntimeOptions['getCurrentSubtitleAss'];
|
||||
focusMainWindow?: IpcDepsRuntimeOptions['focusMainWindow'];
|
||||
getMpvSubtitleRenderMetrics: IpcDepsRuntimeOptions['getMpvSubtitleRenderMetrics'];
|
||||
getSubtitlePosition: IpcDepsRuntimeOptions['getSubtitlePosition'];
|
||||
getSubtitleStyle: IpcDepsRuntimeOptions['getSubtitleStyle'];
|
||||
saveSubtitlePosition: IpcDepsRuntimeOptions['saveSubtitlePosition'];
|
||||
@@ -81,7 +79,6 @@ export interface MainIpcRuntimeServiceDepsParams {
|
||||
setRuntimeOption: IpcDepsRuntimeOptions['setRuntimeOption'];
|
||||
cycleRuntimeOption: IpcDepsRuntimeOptions['cycleRuntimeOption'];
|
||||
reportOverlayContentBounds: IpcDepsRuntimeOptions['reportOverlayContentBounds'];
|
||||
reportHoveredSubtitleToken: IpcDepsRuntimeOptions['reportHoveredSubtitleToken'];
|
||||
getAnilistStatus: IpcDepsRuntimeOptions['getAnilistStatus'];
|
||||
clearAnilistToken: IpcDepsRuntimeOptions['clearAnilistToken'];
|
||||
openAnilistSetup: IpcDepsRuntimeOptions['openAnilistSetup'];
|
||||
@@ -132,9 +129,7 @@ export interface CliCommandRuntimeServiceDepsParams {
|
||||
isInitialized: CliCommandDepsRuntimeOptions['overlay']['isInitialized'];
|
||||
initialize: CliCommandDepsRuntimeOptions['overlay']['initialize'];
|
||||
toggleVisible: CliCommandDepsRuntimeOptions['overlay']['toggleVisible'];
|
||||
toggleInvisible: CliCommandDepsRuntimeOptions['overlay']['toggleInvisible'];
|
||||
setVisible: CliCommandDepsRuntimeOptions['overlay']['setVisible'];
|
||||
setInvisible: CliCommandDepsRuntimeOptions['overlay']['setInvisible'];
|
||||
};
|
||||
mining: {
|
||||
copyCurrentSubtitle: CliCommandDepsRuntimeOptions['mining']['copyCurrentSubtitle'];
|
||||
@@ -192,18 +187,16 @@ export function createMainIpcRuntimeServiceDeps(
|
||||
params: MainIpcRuntimeServiceDepsParams,
|
||||
): IpcDepsRuntimeOptions {
|
||||
return {
|
||||
getInvisibleWindow: params.getInvisibleWindow,
|
||||
getMainWindow: params.getMainWindow,
|
||||
getVisibleOverlayVisibility: params.getVisibleOverlayVisibility,
|
||||
getInvisibleOverlayVisibility: params.getInvisibleOverlayVisibility,
|
||||
onOverlayModalClosed: params.onOverlayModalClosed,
|
||||
onOverlayModalOpened: params.onOverlayModalOpened,
|
||||
openYomitanSettings: params.openYomitanSettings,
|
||||
quitApp: params.quitApp,
|
||||
toggleVisibleOverlay: params.toggleVisibleOverlay,
|
||||
tokenizeCurrentSubtitle: params.tokenizeCurrentSubtitle,
|
||||
getCurrentSubtitleRaw: params.getCurrentSubtitleRaw,
|
||||
getCurrentSubtitleAss: params.getCurrentSubtitleAss,
|
||||
getMpvSubtitleRenderMetrics: params.getMpvSubtitleRenderMetrics,
|
||||
getSubtitlePosition: params.getSubtitlePosition,
|
||||
getSubtitleStyle: params.getSubtitleStyle,
|
||||
saveSubtitlePosition: params.saveSubtitlePosition,
|
||||
@@ -220,7 +213,6 @@ export function createMainIpcRuntimeServiceDeps(
|
||||
setRuntimeOption: params.setRuntimeOption,
|
||||
cycleRuntimeOption: params.cycleRuntimeOption,
|
||||
reportOverlayContentBounds: params.reportOverlayContentBounds,
|
||||
reportHoveredSubtitleToken: params.reportHoveredSubtitleToken,
|
||||
getAnilistStatus: params.getAnilistStatus,
|
||||
clearAnilistToken: params.clearAnilistToken,
|
||||
openAnilistSetup: params.openAnilistSetup,
|
||||
@@ -279,9 +271,7 @@ export function createCliCommandRuntimeServiceDeps(
|
||||
isInitialized: params.overlay.isInitialized,
|
||||
initialize: params.overlay.initialize,
|
||||
toggleVisible: params.overlay.toggleVisible,
|
||||
toggleInvisible: params.overlay.toggleInvisible,
|
||||
setVisible: params.overlay.setVisible,
|
||||
setInvisible: params.overlay.setInvisible,
|
||||
},
|
||||
mining: {
|
||||
copyCurrentSubtitle: params.mining.copyCurrentSubtitle,
|
||||
|
||||
@@ -2,50 +2,31 @@ import type { BrowserWindow } from 'electron';
|
||||
|
||||
import type { BaseWindowTracker } from '../window-trackers';
|
||||
import type { WindowGeometry } from '../types';
|
||||
import {
|
||||
syncInvisibleOverlayMousePassthrough,
|
||||
updateInvisibleOverlayVisibility,
|
||||
updateVisibleOverlayVisibility,
|
||||
} from '../core/services';
|
||||
import { updateVisibleOverlayVisibility } from '../core/services';
|
||||
|
||||
export interface OverlayVisibilityRuntimeDeps {
|
||||
getMainWindow: () => BrowserWindow | null;
|
||||
getInvisibleWindow: () => BrowserWindow | null;
|
||||
getVisibleOverlayVisible: () => boolean;
|
||||
getInvisibleOverlayVisible: () => boolean;
|
||||
getWindowTracker: () => BaseWindowTracker | null;
|
||||
getTrackerNotReadyWarningShown: () => boolean;
|
||||
setTrackerNotReadyWarningShown: (shown: boolean) => void;
|
||||
updateVisibleOverlayBounds: (geometry: WindowGeometry) => void;
|
||||
updateInvisibleOverlayBounds: (geometry: WindowGeometry) => void;
|
||||
ensureOverlayWindowLevel: (window: BrowserWindow) => void;
|
||||
syncPrimaryOverlayWindowLayer: (layer: 'visible') => void;
|
||||
enforceOverlayLayerOrder: () => void;
|
||||
syncOverlayShortcuts: () => void;
|
||||
isMacOSPlatform: () => boolean;
|
||||
showOverlayLoadingOsd: (message: string) => void;
|
||||
resolveFallbackBounds: () => WindowGeometry;
|
||||
}
|
||||
|
||||
export interface OverlayVisibilityRuntimeService {
|
||||
updateVisibleOverlayVisibility: () => void;
|
||||
updateInvisibleOverlayVisibility: () => void;
|
||||
syncInvisibleOverlayMousePassthrough: () => void;
|
||||
}
|
||||
|
||||
export function createOverlayVisibilityRuntimeService(
|
||||
deps: OverlayVisibilityRuntimeDeps,
|
||||
): OverlayVisibilityRuntimeService {
|
||||
const hasInvisibleWindow = (): boolean => {
|
||||
const invisibleWindow = deps.getInvisibleWindow();
|
||||
return Boolean(invisibleWindow && !invisibleWindow.isDestroyed());
|
||||
};
|
||||
|
||||
const setIgnoreMouseEvents = (
|
||||
ignore: boolean,
|
||||
options?: Parameters<BrowserWindow['setIgnoreMouseEvents']>[1],
|
||||
): void => {
|
||||
const invisibleWindow = deps.getInvisibleWindow();
|
||||
if (!invisibleWindow || invisibleWindow.isDestroyed()) return;
|
||||
invisibleWindow.setIgnoreMouseEvents(ignore, options);
|
||||
};
|
||||
|
||||
return {
|
||||
updateVisibleOverlayVisibility(): void {
|
||||
updateVisibleOverlayVisibility({
|
||||
@@ -59,31 +40,13 @@ export function createOverlayVisibilityRuntimeService(
|
||||
updateVisibleOverlayBounds: (geometry: WindowGeometry) =>
|
||||
deps.updateVisibleOverlayBounds(geometry),
|
||||
ensureOverlayWindowLevel: (window: BrowserWindow) => deps.ensureOverlayWindowLevel(window),
|
||||
syncPrimaryOverlayWindowLayer: (layer: 'visible') =>
|
||||
deps.syncPrimaryOverlayWindowLayer(layer),
|
||||
enforceOverlayLayerOrder: () => deps.enforceOverlayLayerOrder(),
|
||||
syncOverlayShortcuts: () => deps.syncOverlayShortcuts(),
|
||||
});
|
||||
},
|
||||
|
||||
updateInvisibleOverlayVisibility(): void {
|
||||
updateInvisibleOverlayVisibility({
|
||||
invisibleWindow: deps.getInvisibleWindow(),
|
||||
visibleOverlayVisible: deps.getVisibleOverlayVisible(),
|
||||
invisibleOverlayVisible: deps.getInvisibleOverlayVisible(),
|
||||
windowTracker: deps.getWindowTracker(),
|
||||
updateInvisibleOverlayBounds: (geometry: WindowGeometry) =>
|
||||
deps.updateInvisibleOverlayBounds(geometry),
|
||||
ensureOverlayWindowLevel: (window: BrowserWindow) => deps.ensureOverlayWindowLevel(window),
|
||||
enforceOverlayLayerOrder: () => deps.enforceOverlayLayerOrder(),
|
||||
syncOverlayShortcuts: () => deps.syncOverlayShortcuts(),
|
||||
});
|
||||
},
|
||||
|
||||
syncInvisibleOverlayMousePassthrough(): void {
|
||||
syncInvisibleOverlayMousePassthrough({
|
||||
hasInvisibleWindow,
|
||||
setIgnoreMouseEvents,
|
||||
visibleOverlayVisible: deps.getVisibleOverlayVisible(),
|
||||
invisibleOverlayVisible: deps.getInvisibleOverlayVisible(),
|
||||
isMacOSPlatform: deps.isMacOSPlatform(),
|
||||
showOverlayLoadingOsd: (message: string) => deps.showOverlayLoadingOsd(message),
|
||||
resolveFallbackBounds: () => deps.resolveFallbackBounds(),
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
@@ -19,14 +19,12 @@ test('restore windows on activate deps builder maps all restoration callbacks',
|
||||
const calls: string[] = [];
|
||||
const deps = createBuildRestoreWindowsOnActivateMainDepsHandler({
|
||||
createMainWindow: () => calls.push('main'),
|
||||
createInvisibleWindow: () => calls.push('invisible'),
|
||||
updateVisibleOverlayVisibility: () => calls.push('visible'),
|
||||
updateInvisibleOverlayVisibility: () => calls.push('invisible-visible'),
|
||||
syncOverlayMpvSubtitleSuppression: () => calls.push('mpv-sync'),
|
||||
})();
|
||||
|
||||
deps.createMainWindow();
|
||||
deps.createInvisibleWindow();
|
||||
deps.updateVisibleOverlayVisibility();
|
||||
deps.updateInvisibleOverlayVisibility();
|
||||
assert.deepEqual(calls, ['main', 'invisible', 'visible', 'invisible-visible']);
|
||||
deps.syncOverlayMpvSubtitleSuppression();
|
||||
assert.deepEqual(calls, ['main', 'visible', 'mpv-sync']);
|
||||
});
|
||||
|
||||
@@ -10,14 +10,12 @@ export function createBuildShouldRestoreWindowsOnActivateMainDepsHandler(deps: {
|
||||
|
||||
export function createBuildRestoreWindowsOnActivateMainDepsHandler(deps: {
|
||||
createMainWindow: () => void;
|
||||
createInvisibleWindow: () => void;
|
||||
updateVisibleOverlayVisibility: () => void;
|
||||
updateInvisibleOverlayVisibility: () => void;
|
||||
syncOverlayMpvSubtitleSuppression: () => void;
|
||||
}) {
|
||||
return () => ({
|
||||
createMainWindow: () => deps.createMainWindow(),
|
||||
createInvisibleWindow: () => deps.createInvisibleWindow(),
|
||||
updateVisibleOverlayVisibility: () => deps.updateVisibleOverlayVisibility(),
|
||||
updateInvisibleOverlayVisibility: () => deps.updateInvisibleOverlayVisibility(),
|
||||
syncOverlayMpvSubtitleSuppression: () => deps.syncOverlayMpvSubtitleSuppression(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -50,26 +50,18 @@ test('initialize overlay runtime main deps map build options and callbacks', ()
|
||||
isOverlayRuntimeInitialized: () => false,
|
||||
initializeOverlayRuntimeCore: (value) => {
|
||||
calls.push(`core:${JSON.stringify(value)}`);
|
||||
return { invisibleOverlayVisible: true };
|
||||
},
|
||||
buildOptions: () => options,
|
||||
setInvisibleOverlayVisible: (visible) => calls.push(`set-invisible:${visible}`),
|
||||
setOverlayRuntimeInitialized: (initialized) => calls.push(`set-initialized:${initialized}`),
|
||||
startBackgroundWarmups: () => calls.push('warmups'),
|
||||
})();
|
||||
|
||||
assert.equal(deps.isOverlayRuntimeInitialized(), false);
|
||||
assert.equal(deps.buildOptions(), options);
|
||||
assert.deepEqual(deps.initializeOverlayRuntimeCore(options), { invisibleOverlayVisible: true });
|
||||
deps.setInvisibleOverlayVisible(true);
|
||||
assert.equal(deps.initializeOverlayRuntimeCore(options), undefined);
|
||||
deps.setOverlayRuntimeInitialized(true);
|
||||
deps.startBackgroundWarmups();
|
||||
assert.deepEqual(calls, [
|
||||
'core:{"id":"opts"}',
|
||||
'set-invisible:true',
|
||||
'set-initialized:true',
|
||||
'warmups',
|
||||
]);
|
||||
assert.deepEqual(calls, ['core:{"id":"opts"}', 'set-initialized:true', 'warmups']);
|
||||
});
|
||||
|
||||
test('open yomitan settings main deps map async open callbacks', async () => {
|
||||
|
||||
@@ -45,9 +45,8 @@ export function createBuildDestroyTrayMainDepsHandler<TTray>(deps: {
|
||||
|
||||
export function createBuildInitializeOverlayRuntimeBootstrapMainDepsHandler<TOptions>(deps: {
|
||||
isOverlayRuntimeInitialized: () => boolean;
|
||||
initializeOverlayRuntimeCore: (options: TOptions) => { invisibleOverlayVisible: boolean };
|
||||
initializeOverlayRuntimeCore: (options: TOptions) => void;
|
||||
buildOptions: () => TOptions;
|
||||
setInvisibleOverlayVisible: (visible: boolean) => void;
|
||||
setOverlayRuntimeInitialized: (initialized: boolean) => void;
|
||||
startBackgroundWarmups: () => void;
|
||||
}) {
|
||||
@@ -55,7 +54,6 @@ export function createBuildInitializeOverlayRuntimeBootstrapMainDepsHandler<TOpt
|
||||
isOverlayRuntimeInitialized: () => deps.isOverlayRuntimeInitialized(),
|
||||
initializeOverlayRuntimeCore: (options: TOptions) => deps.initializeOverlayRuntimeCore(options),
|
||||
buildOptions: () => deps.buildOptions(),
|
||||
setInvisibleOverlayVisible: (visible: boolean) => deps.setInvisibleOverlayVisible(visible),
|
||||
setOverlayRuntimeInitialized: (initialized: boolean) =>
|
||||
deps.setOverlayRuntimeInitialized(initialized),
|
||||
startBackgroundWarmups: () => deps.startBackgroundWarmups(),
|
||||
|
||||
@@ -18,9 +18,7 @@ test('build cli command context deps maps handlers and values', () => {
|
||||
isOverlayInitialized: () => true,
|
||||
initializeOverlay: () => calls.push('init'),
|
||||
toggleVisibleOverlay: () => calls.push('toggle-visible'),
|
||||
toggleInvisibleOverlay: () => calls.push('toggle-invisible'),
|
||||
setVisibleOverlay: (visible) => calls.push(`set-visible:${visible}`),
|
||||
setInvisibleOverlay: (visible) => calls.push(`set-invisible:${visible}`),
|
||||
copyCurrentSubtitle: () => calls.push('copy'),
|
||||
startPendingMultiCopy: (ms) => calls.push(`multi:${ms}`),
|
||||
mineSentenceCard: async () => {
|
||||
|
||||
@@ -15,9 +15,7 @@ export function createBuildCliCommandContextDepsHandler(deps: {
|
||||
isOverlayInitialized: () => boolean;
|
||||
initializeOverlay: () => void;
|
||||
toggleVisibleOverlay: () => void;
|
||||
toggleInvisibleOverlay: () => void;
|
||||
setVisibleOverlay: (visible: boolean) => void;
|
||||
setInvisibleOverlay: (visible: boolean) => void;
|
||||
copyCurrentSubtitle: () => void;
|
||||
startPendingMultiCopy: (timeoutMs: number) => void;
|
||||
mineSentenceCard: () => Promise<void>;
|
||||
@@ -60,9 +58,7 @@ export function createBuildCliCommandContextDepsHandler(deps: {
|
||||
isOverlayInitialized: deps.isOverlayInitialized,
|
||||
initializeOverlay: deps.initializeOverlay,
|
||||
toggleVisibleOverlay: deps.toggleVisibleOverlay,
|
||||
toggleInvisibleOverlay: deps.toggleInvisibleOverlay,
|
||||
setVisibleOverlay: deps.setVisibleOverlay,
|
||||
setInvisibleOverlay: deps.setInvisibleOverlay,
|
||||
copyCurrentSubtitle: deps.copyCurrentSubtitle,
|
||||
startPendingMultiCopy: deps.startPendingMultiCopy,
|
||||
mineSentenceCard: deps.mineSentenceCard,
|
||||
|
||||
@@ -20,9 +20,7 @@ test('cli command context factory composes main deps and context handlers', () =
|
||||
showMpvOsd: (text) => calls.push(`osd:${text}`),
|
||||
initializeOverlayRuntime: () => calls.push('init-overlay'),
|
||||
toggleVisibleOverlay: () => calls.push('toggle-visible'),
|
||||
toggleInvisibleOverlay: () => calls.push('toggle-invisible'),
|
||||
setVisibleOverlayVisible: (visible) => calls.push(`set-visible:${visible}`),
|
||||
setInvisibleOverlayVisible: (visible) => calls.push(`set-invisible:${visible}`),
|
||||
copyCurrentSubtitle: () => calls.push('copy-sub'),
|
||||
startPendingMultiCopy: (timeoutMs) => calls.push(`multi:${timeoutMs}`),
|
||||
mineSentenceCard: async () => {},
|
||||
@@ -73,16 +71,8 @@ test('cli command context factory composes main deps and context handlers', () =
|
||||
context.setSocketPath('/tmp/new.sock');
|
||||
context.showOsd('hello');
|
||||
context.setVisibleOverlay(true);
|
||||
context.setInvisibleOverlay(false);
|
||||
context.toggleVisibleOverlay();
|
||||
context.toggleInvisibleOverlay();
|
||||
|
||||
assert.equal(appState.mpvSocketPath, '/tmp/new.sock');
|
||||
assert.deepEqual(calls, [
|
||||
'osd:hello',
|
||||
'set-visible:true',
|
||||
'set-invisible:false',
|
||||
'toggle-visible',
|
||||
'toggle-invisible',
|
||||
]);
|
||||
assert.deepEqual(calls, ['osd:hello', 'set-visible:true', 'toggle-visible']);
|
||||
});
|
||||
|
||||
@@ -23,9 +23,7 @@ test('cli command context main deps builder maps state and callbacks', async ()
|
||||
|
||||
initializeOverlayRuntime: () => calls.push('init-overlay'),
|
||||
toggleVisibleOverlay: () => calls.push('toggle-visible'),
|
||||
toggleInvisibleOverlay: () => calls.push('toggle-invisible'),
|
||||
setVisibleOverlayVisible: (visible) => calls.push(`set-visible:${visible}`),
|
||||
setInvisibleOverlayVisible: (visible) => calls.push(`set-invisible:${visible}`),
|
||||
|
||||
copyCurrentSubtitle: () => calls.push('copy-sub'),
|
||||
startPendingMultiCopy: (timeoutMs) => calls.push(`multi:${timeoutMs}`),
|
||||
@@ -103,16 +101,9 @@ test('cli command context main deps builder maps state and callbacks', async ()
|
||||
deps.showOsd('hello');
|
||||
deps.initializeOverlay();
|
||||
deps.setVisibleOverlay(true);
|
||||
deps.setInvisibleOverlay(false);
|
||||
deps.printHelp();
|
||||
|
||||
assert.deepEqual(calls, [
|
||||
'osd:hello',
|
||||
'init-overlay',
|
||||
'set-visible:true',
|
||||
'set-invisible:false',
|
||||
'help',
|
||||
]);
|
||||
assert.deepEqual(calls, ['osd:hello', 'init-overlay', 'set-visible:true', 'help']);
|
||||
|
||||
const retry = await deps.retryAnilistQueueNow();
|
||||
assert.deepEqual(retry, { ok: true, message: 'ok' });
|
||||
|
||||
@@ -18,9 +18,7 @@ export function createBuildCliCommandContextMainDepsHandler(deps: {
|
||||
|
||||
initializeOverlayRuntime: () => void;
|
||||
toggleVisibleOverlay: () => void;
|
||||
toggleInvisibleOverlay: () => void;
|
||||
setVisibleOverlayVisible: (visible: boolean) => void;
|
||||
setInvisibleOverlayVisible: (visible: boolean) => void;
|
||||
|
||||
copyCurrentSubtitle: () => void;
|
||||
startPendingMultiCopy: (timeoutMs: number) => void;
|
||||
@@ -70,9 +68,7 @@ export function createBuildCliCommandContextMainDepsHandler(deps: {
|
||||
isOverlayInitialized: () => deps.appState.overlayRuntimeInitialized,
|
||||
initializeOverlay: () => deps.initializeOverlayRuntime(),
|
||||
toggleVisibleOverlay: () => deps.toggleVisibleOverlay(),
|
||||
toggleInvisibleOverlay: () => deps.toggleInvisibleOverlay(),
|
||||
setVisibleOverlay: (visible: boolean) => deps.setVisibleOverlayVisible(visible),
|
||||
setInvisibleOverlay: (visible: boolean) => deps.setInvisibleOverlayVisible(visible),
|
||||
copyCurrentSubtitle: () => deps.copyCurrentSubtitle(),
|
||||
startPendingMultiCopy: (timeoutMs: number) => deps.startPendingMultiCopy(timeoutMs),
|
||||
mineSentenceCard: () => deps.mineSentenceCard(),
|
||||
|
||||
@@ -24,9 +24,7 @@ function createDeps() {
|
||||
isOverlayInitialized: () => true,
|
||||
initializeOverlay: () => {},
|
||||
toggleVisibleOverlay: () => {},
|
||||
toggleInvisibleOverlay: () => {},
|
||||
setVisibleOverlay: () => {},
|
||||
setInvisibleOverlay: () => {},
|
||||
copyCurrentSubtitle: () => {},
|
||||
startPendingMultiCopy: () => {},
|
||||
mineSentenceCard: async () => {},
|
||||
|
||||
@@ -20,9 +20,7 @@ export type CliCommandContextFactoryDeps = {
|
||||
isOverlayInitialized: () => boolean;
|
||||
initializeOverlay: () => void;
|
||||
toggleVisibleOverlay: () => void;
|
||||
toggleInvisibleOverlay: () => void;
|
||||
setVisibleOverlay: (visible: boolean) => void;
|
||||
setInvisibleOverlay: (visible: boolean) => void;
|
||||
copyCurrentSubtitle: () => void;
|
||||
startPendingMultiCopy: (timeoutMs: number) => void;
|
||||
mineSentenceCard: () => Promise<void>;
|
||||
@@ -72,9 +70,7 @@ export function createCliCommandContext(
|
||||
isOverlayInitialized: deps.isOverlayInitialized,
|
||||
initializeOverlay: deps.initializeOverlay,
|
||||
toggleVisibleOverlay: deps.toggleVisibleOverlay,
|
||||
toggleInvisibleOverlay: deps.toggleInvisibleOverlay,
|
||||
setVisibleOverlay: deps.setVisibleOverlay,
|
||||
setInvisibleOverlay: deps.setInvisibleOverlay,
|
||||
copyCurrentSubtitle: deps.copyCurrentSubtitle,
|
||||
startPendingMultiCopy: deps.startPendingMultiCopy,
|
||||
mineSentenceCard: deps.mineSentenceCard,
|
||||
|
||||
@@ -32,10 +32,8 @@ test('composeIpcRuntimeHandlers returns callable IPC handlers and registration b
|
||||
showMpvOsd: () => {},
|
||||
},
|
||||
mainDeps: {
|
||||
getInvisibleWindow: () => null,
|
||||
getMainWindow: () => null,
|
||||
getVisibleOverlayVisibility: () => false,
|
||||
getInvisibleOverlayVisibility: () => false,
|
||||
focusMainWindow: () => {},
|
||||
onOverlayModalClosed: () => {},
|
||||
openYomitanSettings: () => {},
|
||||
@@ -44,7 +42,6 @@ test('composeIpcRuntimeHandlers returns callable IPC handlers and registration b
|
||||
tokenizeCurrentSubtitle: async () => null,
|
||||
getCurrentSubtitleRaw: () => '',
|
||||
getCurrentSubtitleAss: () => '',
|
||||
getMpvSubtitleRenderMetrics: () => ({}) as never,
|
||||
getSubtitlePosition: () => ({}) as never,
|
||||
getSubtitleStyle: () => ({}) as never,
|
||||
saveSubtitlePosition: () => {},
|
||||
@@ -56,7 +53,6 @@ test('composeIpcRuntimeHandlers returns callable IPC handlers and registration b
|
||||
getAnkiConnectStatus: () => false,
|
||||
getRuntimeOptions: () => [],
|
||||
reportOverlayContentBounds: () => {},
|
||||
reportHoveredSubtitleToken: () => {},
|
||||
getAnilistStatus: () => ({}) as never,
|
||||
clearAnilistToken: () => {},
|
||||
openAnilistSetup: () => {},
|
||||
|
||||
@@ -68,12 +68,14 @@ test('composeMpvRuntimeHandlers returns callable handlers and forwards to inject
|
||||
scheduleQuitCheck: () => {},
|
||||
quitApp: () => {},
|
||||
reportJellyfinRemoteStopped: () => {},
|
||||
syncOverlayMpvSubtitleSuppression: () => {},
|
||||
maybeRunAnilistPostWatchUpdate: async () => {},
|
||||
logSubtitleTimingError: () => {},
|
||||
broadcastToOverlayWindows: () => {},
|
||||
onSubtitleChange: () => {},
|
||||
refreshDiscordPresence: () => {},
|
||||
updateCurrentMediaPath: () => {},
|
||||
restoreMpvSubVisibilityForInvisibleOverlay: () => {},
|
||||
getCurrentAnilistMediaKey: () => null,
|
||||
resetAnilistMediaTracking: () => {},
|
||||
maybeProbeAnilistDuration: () => {},
|
||||
|
||||
@@ -14,7 +14,6 @@ test('composeShortcutRuntimes returns callable shortcut runtime handlers', () =>
|
||||
getConfiguredShortcuts: () => ({}) as never,
|
||||
registerGlobalShortcutsCore: () => {},
|
||||
toggleVisibleOverlay: () => {},
|
||||
toggleInvisibleOverlay: () => {},
|
||||
openYomitanSettings: () => {},
|
||||
isDev: false,
|
||||
getMainWindow: () => null,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import type { RuntimeOptionsManager } from '../../runtime-options';
|
||||
import type { JimakuApiResponse, JimakuLanguagePreference, ResolvedConfig } from '../../types';
|
||||
import {
|
||||
getInitialInvisibleOverlayVisibility as getInitialInvisibleOverlayVisibilityCore,
|
||||
getJimakuLanguagePreference as getJimakuLanguagePreferenceCore,
|
||||
getJimakuMaxEntryResults as getJimakuMaxEntryResultsCore,
|
||||
isAutoUpdateEnabledRuntime as isAutoUpdateEnabledRuntimeCore,
|
||||
@@ -14,14 +13,12 @@ import {
|
||||
export type ConfigDerivedRuntimeDeps = {
|
||||
getResolvedConfig: () => ResolvedConfig;
|
||||
getRuntimeOptionsManager: () => RuntimeOptionsManager | null;
|
||||
platform: NodeJS.Platform;
|
||||
defaultJimakuLanguagePreference: JimakuLanguagePreference;
|
||||
defaultJimakuMaxEntryResults: number;
|
||||
defaultJimakuApiBaseUrl: string;
|
||||
};
|
||||
|
||||
export function createConfigDerivedRuntime(deps: ConfigDerivedRuntimeDeps): {
|
||||
getInitialInvisibleOverlayVisibility: () => boolean;
|
||||
shouldAutoInitializeOverlayRuntimeFromConfig: () => boolean;
|
||||
shouldBindVisibleOverlayToMpvSubVisibility: () => boolean;
|
||||
isAutoUpdateEnabledRuntime: () => boolean;
|
||||
@@ -34,8 +31,6 @@ export function createConfigDerivedRuntime(deps: ConfigDerivedRuntimeDeps): {
|
||||
) => Promise<JimakuApiResponse<T>>;
|
||||
} {
|
||||
return {
|
||||
getInitialInvisibleOverlayVisibility: () =>
|
||||
getInitialInvisibleOverlayVisibilityCore(deps.getResolvedConfig(), deps.platform),
|
||||
shouldAutoInitializeOverlayRuntimeFromConfig: () =>
|
||||
shouldAutoInitializeOverlayRuntimeFromConfigCore(deps.getResolvedConfig()),
|
||||
shouldBindVisibleOverlayToMpvSubVisibility: () =>
|
||||
|
||||
@@ -15,9 +15,7 @@ test('field grouping overlay main deps builder maps window visibility and resolv
|
||||
},
|
||||
}),
|
||||
getVisibleOverlayVisible: () => true,
|
||||
getInvisibleOverlayVisible: () => false,
|
||||
setVisibleOverlayVisible: (visible) => calls.push(`visible:${visible}`),
|
||||
setInvisibleOverlayVisible: (visible) => calls.push(`invisible:${visible}`),
|
||||
getResolver: () => resolver,
|
||||
setResolver: (nextResolver) => {
|
||||
calls.push(`set-resolver:${nextResolver ? 'set' : 'null'}`);
|
||||
@@ -31,17 +29,10 @@ test('field grouping overlay main deps builder maps window visibility and resolv
|
||||
|
||||
assert.equal(deps.getMainWindow()?.isDestroyed(), false);
|
||||
assert.equal(deps.getVisibleOverlayVisible(), true);
|
||||
assert.equal(deps.getInvisibleOverlayVisible(), false);
|
||||
assert.equal(deps.getResolver(), resolver);
|
||||
assert.equal(deps.getRestoreVisibleOverlayOnModalClose(), modalSet);
|
||||
deps.setVisibleOverlayVisible(true);
|
||||
deps.setInvisibleOverlayVisible(false);
|
||||
deps.setResolver(null);
|
||||
assert.equal(deps.sendToVisibleOverlay('kiku:open', 1), true);
|
||||
assert.deepEqual(calls, [
|
||||
'visible:true',
|
||||
'invisible:false',
|
||||
'set-resolver:null',
|
||||
'send:kiku:open:1',
|
||||
]);
|
||||
assert.deepEqual(calls, ['visible:true', 'set-resolver:null', 'send:kiku:open:1']);
|
||||
});
|
||||
|
||||
@@ -24,9 +24,7 @@ export function createBuildFieldGroupingOverlayMainDepsHandler<TModal extends st
|
||||
return (): BuiltFieldGroupingOverlayMainDeps<TModal> => ({
|
||||
getMainWindow: () => deps.getMainWindow(),
|
||||
getVisibleOverlayVisible: () => deps.getVisibleOverlayVisible(),
|
||||
getInvisibleOverlayVisible: () => deps.getInvisibleOverlayVisible(),
|
||||
setVisibleOverlayVisible: (visible: boolean) => deps.setVisibleOverlayVisible(visible),
|
||||
setInvisibleOverlayVisible: (visible: boolean) => deps.setInvisibleOverlayVisible(visible),
|
||||
getResolver: () => deps.getResolver(),
|
||||
setResolver: (resolver) => deps.setResolver(resolver),
|
||||
getRestoreVisibleOverlayOnModalClose: () => deps.getRestoreVisibleOverlayOnModalClose(),
|
||||
|
||||
@@ -28,7 +28,6 @@ test('register global shortcuts main deps map callbacks and flags', () => {
|
||||
getConfiguredShortcuts: () => ({ copySubtitle: 's' } as never),
|
||||
registerGlobalShortcutsCore: () => calls.push('register'),
|
||||
toggleVisibleOverlay: () => calls.push('toggle-visible'),
|
||||
toggleInvisibleOverlay: () => calls.push('toggle-invisible'),
|
||||
openYomitanSettings: () => calls.push('open-yomitan'),
|
||||
isDev: true,
|
||||
getMainWindow: () => mainWindow as never,
|
||||
@@ -38,17 +37,15 @@ test('register global shortcuts main deps map callbacks and flags', () => {
|
||||
deps.registerGlobalShortcutsCore({
|
||||
shortcuts: deps.getConfiguredShortcuts(),
|
||||
onToggleVisibleOverlay: () => undefined,
|
||||
onToggleInvisibleOverlay: () => undefined,
|
||||
onOpenYomitanSettings: () => undefined,
|
||||
isDev: deps.isDev,
|
||||
getMainWindow: deps.getMainWindow,
|
||||
});
|
||||
deps.onToggleVisibleOverlay();
|
||||
deps.onToggleInvisibleOverlay();
|
||||
deps.onOpenYomitanSettings();
|
||||
assert.equal(deps.isDev, true);
|
||||
assert.deepEqual(deps.getMainWindow(), mainWindow);
|
||||
assert.deepEqual(calls, ['register', 'toggle-visible', 'toggle-invisible', 'open-yomitan']);
|
||||
assert.deepEqual(calls, ['register', 'toggle-visible', 'open-yomitan']);
|
||||
});
|
||||
|
||||
test('refresh global shortcuts main deps map passthrough handlers', () => {
|
||||
|
||||
@@ -19,7 +19,6 @@ export function createBuildRegisterGlobalShortcutsMainDepsHandler(deps: {
|
||||
getConfiguredShortcuts: () => RegisterGlobalShortcutsServiceOptions['shortcuts'];
|
||||
registerGlobalShortcutsCore: (options: RegisterGlobalShortcutsServiceOptions) => void;
|
||||
toggleVisibleOverlay: () => void;
|
||||
toggleInvisibleOverlay: () => void;
|
||||
openYomitanSettings: () => void;
|
||||
isDev: boolean;
|
||||
getMainWindow: RegisterGlobalShortcutsServiceOptions['getMainWindow'];
|
||||
@@ -29,7 +28,6 @@ export function createBuildRegisterGlobalShortcutsMainDepsHandler(deps: {
|
||||
registerGlobalShortcutsCore: (options: RegisterGlobalShortcutsServiceOptions) =>
|
||||
deps.registerGlobalShortcutsCore(options),
|
||||
onToggleVisibleOverlay: () => deps.toggleVisibleOverlay(),
|
||||
onToggleInvisibleOverlay: () => deps.toggleInvisibleOverlay(),
|
||||
onOpenYomitanSettings: () => deps.openYomitanSettings(),
|
||||
isDev: deps.isDev,
|
||||
getMainWindow: deps.getMainWindow,
|
||||
|
||||
@@ -6,7 +6,6 @@ import { createGlobalShortcutsRuntimeHandlers } from './global-shortcuts-runtime
|
||||
function createShortcuts(): ConfiguredShortcuts {
|
||||
return {
|
||||
toggleVisibleOverlayGlobal: 'CommandOrControl+Shift+O',
|
||||
toggleInvisibleOverlayGlobal: 'CommandOrControl+Shift+I',
|
||||
copySubtitle: 's',
|
||||
copySubtitleMultiple: 'CommandOrControl+s',
|
||||
updateLastCardFromClipboard: 'c',
|
||||
@@ -38,7 +37,6 @@ test('global shortcuts runtime handlers compose get/register/refresh flow', () =
|
||||
assert.equal(options.shortcuts, shortcuts);
|
||||
},
|
||||
toggleVisibleOverlay: () => calls.push('toggle-visible'),
|
||||
toggleInvisibleOverlay: () => calls.push('toggle-invisible'),
|
||||
openYomitanSettings: () => calls.push('open-yomitan'),
|
||||
isDev: false,
|
||||
getMainWindow: () => null,
|
||||
|
||||
@@ -10,7 +10,6 @@ import type { ConfiguredShortcuts } from '../../core/utils/shortcut-config';
|
||||
function createShortcuts(): ConfiguredShortcuts {
|
||||
return {
|
||||
toggleVisibleOverlayGlobal: 'CommandOrControl+Shift+O',
|
||||
toggleInvisibleOverlayGlobal: 'CommandOrControl+Shift+I',
|
||||
copySubtitle: 's',
|
||||
copySubtitleMultiple: 'CommandOrControl+s',
|
||||
updateLastCardFromClipboard: 'c',
|
||||
@@ -58,18 +57,16 @@ test('register global shortcuts handler passes through callbacks and shortcuts',
|
||||
assert.equal(options.isDev, true);
|
||||
assert.equal(options.getMainWindow(), mainWindow);
|
||||
options.onToggleVisibleOverlay();
|
||||
options.onToggleInvisibleOverlay();
|
||||
options.onOpenYomitanSettings();
|
||||
},
|
||||
onToggleVisibleOverlay: () => calls.push('toggle-visible'),
|
||||
onToggleInvisibleOverlay: () => calls.push('toggle-invisible'),
|
||||
onOpenYomitanSettings: () => calls.push('open-yomitan'),
|
||||
isDev: true,
|
||||
getMainWindow: () => mainWindow,
|
||||
});
|
||||
|
||||
registerGlobalShortcuts();
|
||||
assert.deepEqual(calls, ['register', 'toggle-visible', 'toggle-invisible', 'open-yomitan']);
|
||||
assert.deepEqual(calls, ['register', 'toggle-visible', 'open-yomitan']);
|
||||
});
|
||||
|
||||
test('refresh global and overlay shortcuts unregisters then re-registers', () => {
|
||||
|
||||
@@ -18,7 +18,6 @@ export function createRegisterGlobalShortcutsHandler(deps: {
|
||||
getConfiguredShortcuts: () => RegisterGlobalShortcutsServiceOptions['shortcuts'];
|
||||
registerGlobalShortcutsCore: (options: RegisterGlobalShortcutsServiceOptions) => void;
|
||||
onToggleVisibleOverlay: () => void;
|
||||
onToggleInvisibleOverlay: () => void;
|
||||
onOpenYomitanSettings: () => void;
|
||||
isDev: boolean;
|
||||
getMainWindow: RegisterGlobalShortcutsServiceOptions['getMainWindow'];
|
||||
@@ -27,7 +26,6 @@ export function createRegisterGlobalShortcutsHandler(deps: {
|
||||
deps.registerGlobalShortcutsCore({
|
||||
shortcuts: deps.getConfiguredShortcuts(),
|
||||
onToggleVisibleOverlay: deps.onToggleVisibleOverlay,
|
||||
onToggleInvisibleOverlay: deps.onToggleInvisibleOverlay,
|
||||
onOpenYomitanSettings: deps.onOpenYomitanSettings,
|
||||
isDev: deps.isDev,
|
||||
getMainWindow: deps.getMainWindow,
|
||||
|
||||
@@ -24,9 +24,6 @@ function createArgs(overrides: Partial<CliArgs> = {}): CliArgs {
|
||||
toggleOverlay: false,
|
||||
hideOverlay: false,
|
||||
showOverlay: false,
|
||||
toggleInvisibleOverlay: false,
|
||||
hideInvisibleOverlay: false,
|
||||
showInvisibleOverlay: false,
|
||||
copyCurrentSubtitle: false,
|
||||
multiCopy: false,
|
||||
mineSentence: false,
|
||||
|
||||
@@ -11,6 +11,7 @@ test('mpv connection handler reports stop and quits when disconnect guard passes
|
||||
const handler = createHandleMpvConnectionChangeHandler({
|
||||
reportJellyfinRemoteStopped: () => calls.push('report-stop'),
|
||||
refreshDiscordPresence: () => calls.push('presence-refresh'),
|
||||
syncOverlayMpvSubtitleSuppression: () => calls.push('sync-overlay-mpv-sub'),
|
||||
hasInitialJellyfinPlayArg: () => true,
|
||||
isOverlayRuntimeInitialized: () => false,
|
||||
isQuitOnDisconnectArmed: () => true,
|
||||
@@ -26,6 +27,27 @@ test('mpv connection handler reports stop and quits when disconnect guard passes
|
||||
assert.deepEqual(calls, ['presence-refresh', 'report-stop', 'schedule', 'quit']);
|
||||
});
|
||||
|
||||
test('mpv connection handler syncs overlay subtitle suppression on connect', () => {
|
||||
const calls: string[] = [];
|
||||
const handler = createHandleMpvConnectionChangeHandler({
|
||||
reportJellyfinRemoteStopped: () => calls.push('report-stop'),
|
||||
refreshDiscordPresence: () => calls.push('presence-refresh'),
|
||||
syncOverlayMpvSubtitleSuppression: () => calls.push('sync-overlay-mpv-sub'),
|
||||
hasInitialJellyfinPlayArg: () => true,
|
||||
isOverlayRuntimeInitialized: () => false,
|
||||
isQuitOnDisconnectArmed: () => true,
|
||||
scheduleQuitCheck: () => {
|
||||
calls.push('schedule');
|
||||
},
|
||||
isMpvConnected: () => false,
|
||||
quitApp: () => calls.push('quit'),
|
||||
});
|
||||
|
||||
handler({ connected: true });
|
||||
|
||||
assert.deepEqual(calls, ['presence-refresh', 'sync-overlay-mpv-sub']);
|
||||
});
|
||||
|
||||
test('mpv subtitle timing handler ignores blank subtitle lines', () => {
|
||||
const calls: string[] = [];
|
||||
const handler = createHandleMpvSubtitleTimingHandler({
|
||||
|
||||
@@ -18,6 +18,7 @@ type MpvEventClient = {
|
||||
export function createHandleMpvConnectionChangeHandler(deps: {
|
||||
reportJellyfinRemoteStopped: () => void;
|
||||
refreshDiscordPresence: () => void;
|
||||
syncOverlayMpvSubtitleSuppression: () => void;
|
||||
hasInitialJellyfinPlayArg: () => boolean;
|
||||
isOverlayRuntimeInitialized: () => boolean;
|
||||
isQuitOnDisconnectArmed: () => boolean;
|
||||
@@ -27,7 +28,10 @@ export function createHandleMpvConnectionChangeHandler(deps: {
|
||||
}) {
|
||||
return ({ connected }: { connected: boolean }): void => {
|
||||
deps.refreshDiscordPresence();
|
||||
if (connected) return;
|
||||
if (connected) {
|
||||
deps.syncOverlayMpvSubtitleSuppression();
|
||||
return;
|
||||
}
|
||||
deps.reportJellyfinRemoteStopped();
|
||||
if (!deps.hasInitialJellyfinPlayArg()) return;
|
||||
if (deps.isOverlayRuntimeInitialized()) return;
|
||||
|
||||
@@ -1,161 +0,0 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
|
||||
import { PartOfSpeech, type SubtitleData } from '../../types';
|
||||
import {
|
||||
HOVER_TOKEN_MESSAGE,
|
||||
HOVER_SCRIPT_NAME,
|
||||
buildHoveredTokenMessageCommand,
|
||||
buildHoveredTokenPayload,
|
||||
createApplyHoveredTokenOverlayHandler,
|
||||
} from './mpv-hover-highlight';
|
||||
|
||||
const SUBTITLE: SubtitleData = {
|
||||
text: '昨日は雨だった。',
|
||||
tokens: [
|
||||
{
|
||||
surface: '昨日',
|
||||
reading: 'きのう',
|
||||
headword: '昨日',
|
||||
startPos: 0,
|
||||
endPos: 2,
|
||||
partOfSpeech: PartOfSpeech.noun,
|
||||
isMerged: false,
|
||||
isKnown: false,
|
||||
isNPlusOneTarget: false,
|
||||
},
|
||||
{
|
||||
surface: 'は',
|
||||
reading: 'は',
|
||||
headword: 'は',
|
||||
startPos: 2,
|
||||
endPos: 3,
|
||||
partOfSpeech: PartOfSpeech.particle,
|
||||
isMerged: false,
|
||||
isKnown: true,
|
||||
isNPlusOneTarget: false,
|
||||
},
|
||||
{
|
||||
surface: '雨',
|
||||
reading: 'あめ',
|
||||
headword: '雨',
|
||||
startPos: 3,
|
||||
endPos: 4,
|
||||
partOfSpeech: PartOfSpeech.noun,
|
||||
isMerged: false,
|
||||
isKnown: false,
|
||||
isNPlusOneTarget: true,
|
||||
},
|
||||
{
|
||||
surface: 'だった。',
|
||||
reading: 'だった。',
|
||||
headword: 'だ',
|
||||
startPos: 4,
|
||||
endPos: 8,
|
||||
partOfSpeech: PartOfSpeech.other,
|
||||
isMerged: false,
|
||||
isKnown: false,
|
||||
isNPlusOneTarget: false,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
test('buildHoveredTokenPayload normalizes metadata and strips empty tokens', () => {
|
||||
const payload = buildHoveredTokenPayload({
|
||||
subtitle: SUBTITLE,
|
||||
hoveredTokenIndex: 2,
|
||||
revision: 5,
|
||||
});
|
||||
|
||||
assert.equal(payload.revision, 5);
|
||||
assert.equal(payload.subtitle, '昨日は雨だった。');
|
||||
assert.equal(payload.hoveredTokenIndex, 2);
|
||||
assert.equal(payload.tokens.length, 4);
|
||||
assert.equal(payload.tokens[0]?.text, '昨日');
|
||||
assert.equal(payload.tokens[0]?.index, 0);
|
||||
assert.equal(payload.tokens[1]?.index, 1);
|
||||
assert.equal(payload.colors.hover, 'C6A0F6');
|
||||
});
|
||||
|
||||
test('buildHoveredTokenPayload normalizes hover color override', () => {
|
||||
const payload = buildHoveredTokenPayload({
|
||||
subtitle: SUBTITLE,
|
||||
hoveredTokenIndex: 1,
|
||||
revision: 7,
|
||||
hoverColor: '#c6a0f6',
|
||||
});
|
||||
|
||||
assert.equal(payload.colors.hover, 'C6A0F6');
|
||||
});
|
||||
|
||||
test('buildHoveredTokenMessageCommand sends script-message-to subminer payload', () => {
|
||||
const payload = buildHoveredTokenPayload({
|
||||
subtitle: SUBTITLE,
|
||||
hoveredTokenIndex: 0,
|
||||
revision: 1,
|
||||
});
|
||||
|
||||
const command = buildHoveredTokenMessageCommand(payload);
|
||||
|
||||
assert.equal(command[0], 'script-message-to');
|
||||
assert.equal(command[1], HOVER_SCRIPT_NAME);
|
||||
assert.equal(command[2], HOVER_TOKEN_MESSAGE);
|
||||
|
||||
const raw = command[3] as string;
|
||||
const parsed = JSON.parse(raw);
|
||||
assert.equal(parsed.revision, 1);
|
||||
assert.equal(parsed.hoveredTokenIndex, 0);
|
||||
assert.equal(parsed.subtitle, '昨日は雨だった。');
|
||||
assert.equal(parsed.tokens.length, 4);
|
||||
});
|
||||
|
||||
test('createApplyHoveredTokenOverlayHandler sends clear payload when hovered token is missing', () => {
|
||||
const commands: Array<(string | number)[]> = [];
|
||||
const apply = createApplyHoveredTokenOverlayHandler({
|
||||
getMpvClient: () => ({
|
||||
connected: true,
|
||||
send: ({ command }: { command: (string | number)[] }) => {
|
||||
commands.push(command);
|
||||
return true;
|
||||
},
|
||||
}),
|
||||
getCurrentSubtitleData: () => SUBTITLE,
|
||||
getHoveredTokenIndex: () => null,
|
||||
getHoveredSubtitleRevision: () => 3,
|
||||
getHoverTokenColor: () => null,
|
||||
});
|
||||
|
||||
apply();
|
||||
|
||||
const parsed = JSON.parse(commands[0]?.[3] as string);
|
||||
assert.equal(parsed.hoveredTokenIndex, null);
|
||||
assert.equal(parsed.subtitle, null);
|
||||
assert.equal(parsed.tokens.length, 0);
|
||||
});
|
||||
|
||||
test('createApplyHoveredTokenOverlayHandler sends highlight payload when hover is active', () => {
|
||||
const commands: Array<(string | number)[]> = [];
|
||||
const apply = createApplyHoveredTokenOverlayHandler({
|
||||
getMpvClient: () => ({
|
||||
connected: true,
|
||||
send: ({ command }: { command: (string | number)[] }) => {
|
||||
commands.push(command);
|
||||
return true;
|
||||
},
|
||||
}),
|
||||
getCurrentSubtitleData: () => SUBTITLE,
|
||||
getHoveredTokenIndex: () => 0,
|
||||
getHoveredSubtitleRevision: () => 3,
|
||||
getHoverTokenColor: () => '#c6a0f6',
|
||||
});
|
||||
|
||||
apply();
|
||||
|
||||
const parsed = JSON.parse(commands[0]?.[3] as string);
|
||||
assert.equal(parsed.hoveredTokenIndex, 0);
|
||||
assert.equal(parsed.subtitle, '昨日は雨だった。');
|
||||
assert.equal(parsed.tokens.length, 4);
|
||||
assert.equal(parsed.colors.hover, 'C6A0F6');
|
||||
assert.equal(commands[0]?.[0], 'script-message-to');
|
||||
assert.equal(commands[0]?.[1], HOVER_SCRIPT_NAME);
|
||||
});
|
||||
@@ -1,138 +0,0 @@
|
||||
import type { SubtitleData } from '../../types';
|
||||
|
||||
export const HOVER_SCRIPT_NAME = 'subminer';
|
||||
export const HOVER_TOKEN_MESSAGE = 'subminer-hover-token';
|
||||
|
||||
const DEFAULT_HOVER_TOKEN_COLOR = 'C6A0F6';
|
||||
const DEFAULT_TOKEN_COLOR = 'FFFFFF';
|
||||
|
||||
export type HoverPayloadToken = {
|
||||
text: string;
|
||||
index: number;
|
||||
startPos: number | null;
|
||||
endPos: number | null;
|
||||
};
|
||||
|
||||
export type HoverTokenPayload = {
|
||||
revision: number;
|
||||
subtitle: string | null;
|
||||
hoveredTokenIndex: number | null;
|
||||
tokens: HoverPayloadToken[];
|
||||
colors: {
|
||||
base: string;
|
||||
hover: string;
|
||||
};
|
||||
};
|
||||
|
||||
type HoverTokenInput = {
|
||||
subtitle: SubtitleData | null;
|
||||
hoveredTokenIndex: number | null;
|
||||
revision: number;
|
||||
hoverColor?: string | null;
|
||||
};
|
||||
|
||||
function normalizeHexColor(color: string | null | undefined, fallback: string): string {
|
||||
if (typeof color !== 'string') {
|
||||
return fallback;
|
||||
}
|
||||
const normalized = color.trim().replace(/^#/, '').toUpperCase();
|
||||
return /^[0-9A-F]{6}$/.test(normalized) ? normalized : fallback;
|
||||
}
|
||||
|
||||
function sanitizeSubtitleText(text: string): string {
|
||||
return text
|
||||
.replace(/\\N/g, '\n')
|
||||
.replace(/\\n/g, '\n')
|
||||
.replace(/\{[^}]*\}/g, '')
|
||||
.trim();
|
||||
}
|
||||
|
||||
function sanitizeTokenSurface(surface: unknown): string {
|
||||
return typeof surface === 'string' ? surface : '';
|
||||
}
|
||||
|
||||
function hasHoveredToken(subtitle: SubtitleData | null, hoveredTokenIndex: number | null): boolean {
|
||||
if (!subtitle || hoveredTokenIndex === null || hoveredTokenIndex < 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return subtitle.tokens?.some((token, index) => index === hoveredTokenIndex) ?? false;
|
||||
}
|
||||
|
||||
export function buildHoveredTokenPayload(input: HoverTokenInput): HoverTokenPayload {
|
||||
const { subtitle, hoveredTokenIndex, revision, hoverColor } = input;
|
||||
|
||||
const tokens: HoverPayloadToken[] = [];
|
||||
|
||||
if (subtitle?.tokens && subtitle.tokens.length > 0) {
|
||||
for (let tokenIndex = 0; tokenIndex < subtitle.tokens.length; tokenIndex += 1) {
|
||||
const token = subtitle.tokens[tokenIndex];
|
||||
if (!token) {
|
||||
continue;
|
||||
}
|
||||
const surface = sanitizeTokenSurface(token?.surface);
|
||||
if (!surface || surface.trim().length === 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
tokens.push({
|
||||
text: surface,
|
||||
index: tokenIndex,
|
||||
startPos: Number.isFinite(token.startPos) ? token.startPos : null,
|
||||
endPos: Number.isFinite(token.endPos) ? token.endPos : null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
revision,
|
||||
subtitle: subtitle ? sanitizeSubtitleText(subtitle.text) : null,
|
||||
hoveredTokenIndex:
|
||||
hoveredTokenIndex !== null && hoveredTokenIndex >= 0 ? hoveredTokenIndex : null,
|
||||
tokens,
|
||||
colors: {
|
||||
base: DEFAULT_TOKEN_COLOR,
|
||||
hover: normalizeHexColor(hoverColor, DEFAULT_HOVER_TOKEN_COLOR),
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function buildHoveredTokenMessageCommand(payload: HoverTokenPayload): (string | number)[] {
|
||||
return [
|
||||
'script-message-to',
|
||||
HOVER_SCRIPT_NAME,
|
||||
HOVER_TOKEN_MESSAGE,
|
||||
JSON.stringify(payload),
|
||||
];
|
||||
}
|
||||
|
||||
export function createApplyHoveredTokenOverlayHandler(deps: {
|
||||
getMpvClient: () => {
|
||||
connected: boolean;
|
||||
send: (payload: { command: (string | number)[] }) => boolean;
|
||||
} | null;
|
||||
getCurrentSubtitleData: () => SubtitleData | null;
|
||||
getHoveredTokenIndex: () => number | null;
|
||||
getHoveredSubtitleRevision: () => number;
|
||||
getHoverTokenColor: () => string | null;
|
||||
}) {
|
||||
return (): void => {
|
||||
const mpvClient = deps.getMpvClient();
|
||||
if (!mpvClient || !mpvClient.connected) {
|
||||
return;
|
||||
}
|
||||
|
||||
const subtitle = deps.getCurrentSubtitleData();
|
||||
const hoveredTokenIndex = deps.getHoveredTokenIndex();
|
||||
const revision = deps.getHoveredSubtitleRevision();
|
||||
const hoverColor = deps.getHoverTokenColor();
|
||||
const payload = buildHoveredTokenPayload({
|
||||
subtitle: subtitle && hasHoveredToken(subtitle, hoveredTokenIndex) ? subtitle : null,
|
||||
hoveredTokenIndex: hoveredTokenIndex,
|
||||
revision,
|
||||
hoverColor,
|
||||
});
|
||||
|
||||
mpvClient.send({ command: buildHoveredTokenMessageCommand(payload) });
|
||||
};
|
||||
}
|
||||
@@ -51,6 +51,7 @@ test('media path change handler reports stop for empty path and probes media key
|
||||
const handler = createHandleMpvMediaPathChangeHandler({
|
||||
updateCurrentMediaPath: (path) => calls.push(`path:${path}`),
|
||||
reportJellyfinRemoteStopped: () => calls.push('stopped'),
|
||||
restoreMpvSubVisibilityForInvisibleOverlay: () => calls.push('restore-mpv-sub'),
|
||||
getCurrentAnilistMediaKey: () => 'show:1',
|
||||
resetAnilistMediaTracking: (mediaKey) => calls.push(`reset:${String(mediaKey)}`),
|
||||
maybeProbeAnilistDuration: (mediaKey) => calls.push(`probe:${mediaKey}`),
|
||||
@@ -63,6 +64,7 @@ test('media path change handler reports stop for empty path and probes media key
|
||||
assert.deepEqual(calls, [
|
||||
'path:',
|
||||
'stopped',
|
||||
'restore-mpv-sub',
|
||||
'reset:show:1',
|
||||
'probe:show:1',
|
||||
'guess:show:1',
|
||||
|
||||
@@ -19,12 +19,10 @@ test('overlay content measurement store main deps builder maps callbacks', () =>
|
||||
|
||||
test('overlay modal runtime main deps builder maps window resolvers', () => {
|
||||
const mainWindow = { id: 'main' };
|
||||
const invisibleWindow = { id: 'invisible' };
|
||||
const modalWindow = { id: 'modal' };
|
||||
const calls: string[] = [];
|
||||
const deps = createBuildOverlayModalRuntimeMainDepsHandler({
|
||||
getMainWindow: () => mainWindow as never,
|
||||
getInvisibleWindow: () => invisibleWindow as never,
|
||||
getModalWindow: () => modalWindow as never,
|
||||
createModalWindow: () => modalWindow as never,
|
||||
getModalGeometry: () => ({ x: 1, y: 2, width: 3, height: 4 }),
|
||||
@@ -33,7 +31,6 @@ test('overlay modal runtime main deps builder maps window resolvers', () => {
|
||||
})();
|
||||
|
||||
assert.equal(deps.getMainWindow(), mainWindow);
|
||||
assert.equal(deps.getInvisibleWindow(), invisibleWindow);
|
||||
assert.equal(deps.getModalWindow(), modalWindow);
|
||||
assert.equal(deps.createModalWindow(), modalWindow);
|
||||
assert.deepEqual(deps.getModalGeometry(), { x: 1, y: 2, width: 3, height: 4 });
|
||||
|
||||
@@ -19,7 +19,6 @@ export function createBuildOverlayModalRuntimeMainDepsHandler(
|
||||
) {
|
||||
return (): OverlayWindowResolver => ({
|
||||
getMainWindow: () => deps.getMainWindow(),
|
||||
getInvisibleWindow: () => deps.getInvisibleWindow(),
|
||||
getModalWindow: () => deps.getModalWindow(),
|
||||
createModalWindow: () => deps.createModalWindow(),
|
||||
getModalGeometry: () => deps.getModalGeometry(),
|
||||
|
||||
@@ -15,7 +15,6 @@ test('overlay runtime bootstrap handlers compose options builder and bootstrap h
|
||||
ankiIntegration: null as unknown,
|
||||
};
|
||||
let initialized = false;
|
||||
let invisibleOverlayVisible = false;
|
||||
let warmupsStarted = 0;
|
||||
|
||||
const { initializeOverlayRuntime } = createOverlayRuntimeBootstrapHandlers({
|
||||
@@ -23,21 +22,16 @@ test('overlay runtime bootstrap handlers compose options builder and bootstrap h
|
||||
appState,
|
||||
overlayManager: {
|
||||
getVisibleOverlayVisible: () => true,
|
||||
getInvisibleOverlayVisible: () => false,
|
||||
},
|
||||
overlayVisibilityRuntime: {
|
||||
updateVisibleOverlayVisibility: () => {},
|
||||
updateInvisibleOverlayVisibility: () => {},
|
||||
},
|
||||
overlayShortcutsRuntime: {
|
||||
syncOverlayShortcuts: () => {},
|
||||
},
|
||||
getInitialInvisibleOverlayVisibility: () => false,
|
||||
createMainWindow: () => {},
|
||||
createInvisibleWindow: () => {},
|
||||
registerGlobalShortcuts: () => {},
|
||||
updateVisibleOverlayBounds: () => {},
|
||||
updateInvisibleOverlayBounds: () => {},
|
||||
getOverlayWindows: () => [],
|
||||
getResolvedConfig: () => ({}),
|
||||
showDesktopNotification: () => {},
|
||||
@@ -52,10 +46,7 @@ test('overlay runtime bootstrap handlers compose options builder and bootstrap h
|
||||
},
|
||||
initializeOverlayRuntimeBootstrapDeps: {
|
||||
isOverlayRuntimeInitialized: () => initialized,
|
||||
initializeOverlayRuntimeCore: () => ({ invisibleOverlayVisible: true }),
|
||||
setInvisibleOverlayVisible: (visible) => {
|
||||
invisibleOverlayVisible = visible;
|
||||
},
|
||||
initializeOverlayRuntimeCore: () => {},
|
||||
setOverlayRuntimeInitialized: (next) => {
|
||||
initialized = next;
|
||||
},
|
||||
@@ -68,7 +59,6 @@ test('overlay runtime bootstrap handlers compose options builder and bootstrap h
|
||||
initializeOverlayRuntime();
|
||||
initializeOverlayRuntime();
|
||||
|
||||
assert.equal(invisibleOverlayVisible, true);
|
||||
assert.equal(initialized, true);
|
||||
assert.equal(warmupsStarted, 1);
|
||||
});
|
||||
|
||||
@@ -8,10 +8,8 @@ test('overlay runtime bootstrap no-ops when already initialized', () => {
|
||||
isOverlayRuntimeInitialized: () => true,
|
||||
initializeOverlayRuntimeCore: () => {
|
||||
coreCalls += 1;
|
||||
return { invisibleOverlayVisible: false };
|
||||
},
|
||||
buildOptions: () => ({} as never),
|
||||
setInvisibleOverlayVisible: () => {},
|
||||
setOverlayRuntimeInitialized: () => {},
|
||||
startBackgroundWarmups: () => {},
|
||||
});
|
||||
@@ -27,15 +25,11 @@ test('overlay runtime bootstrap runs core init and applies post-init state', ()
|
||||
isOverlayRuntimeInitialized: () => initialized,
|
||||
initializeOverlayRuntimeCore: () => {
|
||||
calls.push('core');
|
||||
return { invisibleOverlayVisible: true };
|
||||
},
|
||||
buildOptions: () => {
|
||||
calls.push('options');
|
||||
return {} as never;
|
||||
},
|
||||
setInvisibleOverlayVisible: (visible) => {
|
||||
calls.push(`invisible:${visible ? 'yes' : 'no'}`);
|
||||
},
|
||||
setOverlayRuntimeInitialized: (value) => {
|
||||
initialized = value;
|
||||
calls.push(`initialized:${value ? 'yes' : 'no'}`);
|
||||
@@ -47,5 +41,5 @@ test('overlay runtime bootstrap runs core init and applies post-init state', ()
|
||||
|
||||
initialize();
|
||||
assert.equal(initialized, true);
|
||||
assert.deepEqual(calls, ['options', 'core', 'invisible:yes', 'initialized:yes', 'warmups']);
|
||||
assert.deepEqual(calls, ['options', 'core', 'initialized:yes', 'warmups']);
|
||||
});
|
||||
|
||||
@@ -9,16 +9,11 @@ import type {
|
||||
|
||||
type InitializeOverlayRuntimeCore = (options: {
|
||||
backendOverride: string | null;
|
||||
getInitialInvisibleOverlayVisibility: () => boolean;
|
||||
createMainWindow: () => void;
|
||||
createInvisibleWindow: () => void;
|
||||
registerGlobalShortcuts: () => void;
|
||||
updateVisibleOverlayBounds: (geometry: WindowGeometry) => void;
|
||||
updateInvisibleOverlayBounds: (geometry: WindowGeometry) => void;
|
||||
isVisibleOverlayVisible: () => boolean;
|
||||
isInvisibleOverlayVisible: () => boolean;
|
||||
updateVisibleOverlayVisibility: () => void;
|
||||
updateInvisibleOverlayVisibility: () => void;
|
||||
getOverlayWindows: () => BrowserWindow[];
|
||||
syncOverlayShortcuts: () => void;
|
||||
setWindowTracker: (tracker: BaseWindowTracker | null) => void;
|
||||
@@ -35,20 +30,18 @@ type InitializeOverlayRuntimeCore = (options: {
|
||||
data: KikuFieldGroupingRequestData,
|
||||
) => Promise<KikuFieldGroupingChoice>;
|
||||
getKnownWordCacheStatePath: () => string;
|
||||
}) => { invisibleOverlayVisible: boolean };
|
||||
}) => void;
|
||||
|
||||
export function createInitializeOverlayRuntimeHandler(deps: {
|
||||
isOverlayRuntimeInitialized: () => boolean;
|
||||
initializeOverlayRuntimeCore: InitializeOverlayRuntimeCore;
|
||||
buildOptions: () => Parameters<InitializeOverlayRuntimeCore>[0];
|
||||
setInvisibleOverlayVisible: (visible: boolean) => void;
|
||||
setOverlayRuntimeInitialized: (initialized: boolean) => void;
|
||||
startBackgroundWarmups: () => void;
|
||||
}) {
|
||||
return (): void => {
|
||||
if (deps.isOverlayRuntimeInitialized()) return;
|
||||
const result = deps.initializeOverlayRuntimeCore(deps.buildOptions());
|
||||
deps.setInvisibleOverlayVisible(result.invisibleOverlayVisible);
|
||||
deps.initializeOverlayRuntimeCore(deps.buildOptions());
|
||||
deps.setOverlayRuntimeInitialized(true);
|
||||
deps.startBackgroundWarmups();
|
||||
};
|
||||
|
||||
@@ -57,14 +57,12 @@ test('set overlay debug visualization main deps builder maps callbacks', () => {
|
||||
setOverlayDebugVisualizationEnabledRuntime: () => calls.push('set-runtime'),
|
||||
getCurrentEnabled: () => false,
|
||||
setCurrentEnabled: () => calls.push('set-current'),
|
||||
broadcastToOverlayWindows: () => calls.push('broadcast'),
|
||||
})();
|
||||
|
||||
deps.setOverlayDebugVisualizationEnabledRuntime(false, true, () => {}, () => {});
|
||||
deps.setOverlayDebugVisualizationEnabledRuntime(false, true, () => {});
|
||||
assert.equal(deps.getCurrentEnabled(), false);
|
||||
deps.setCurrentEnabled(true);
|
||||
deps.broadcastToOverlayWindows('overlay:debug');
|
||||
assert.deepEqual(calls, ['set-runtime', 'set-current', 'broadcast']);
|
||||
assert.deepEqual(calls, ['set-runtime', 'set-current']);
|
||||
});
|
||||
|
||||
test('open runtime options palette main deps builder maps callbacks', () => {
|
||||
|
||||
@@ -65,18 +65,14 @@ export function createBuildSetOverlayDebugVisualizationEnabledMainDepsHandler(
|
||||
currentEnabled,
|
||||
nextEnabled,
|
||||
setCurrentEnabled,
|
||||
broadcastToOverlayWindows,
|
||||
) =>
|
||||
deps.setOverlayDebugVisualizationEnabledRuntime(
|
||||
currentEnabled,
|
||||
nextEnabled,
|
||||
setCurrentEnabled,
|
||||
broadcastToOverlayWindows,
|
||||
),
|
||||
getCurrentEnabled: () => deps.getCurrentEnabled(),
|
||||
setCurrentEnabled: (enabled: boolean) => deps.setCurrentEnabled(enabled),
|
||||
broadcastToOverlayWindows: (channel: string, ...args: unknown[]) =>
|
||||
deps.broadcastToOverlayWindows(channel, ...args),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -104,22 +104,21 @@ test('set overlay debug visualization enabled delegates with current state and b
|
||||
const calls: string[] = [];
|
||||
let current = false;
|
||||
const setEnabled = createSetOverlayDebugVisualizationEnabledHandler({
|
||||
setOverlayDebugVisualizationEnabledRuntime: (curr, next, setCurrent, broadcast) => {
|
||||
setOverlayDebugVisualizationEnabledRuntime: (curr, next, setCurrent) => {
|
||||
calls.push(`runtime:${curr}->${next}`);
|
||||
setCurrent(next);
|
||||
broadcast('overlay-debug:set', next);
|
||||
// no renderer-level side effects for this legacy debug path.
|
||||
},
|
||||
getCurrentEnabled: () => current,
|
||||
setCurrentEnabled: (enabled) => {
|
||||
current = enabled;
|
||||
calls.push(`set:${enabled}`);
|
||||
},
|
||||
broadcastToOverlayWindows: (channel, value) => calls.push(`emit:${channel}:${value}`),
|
||||
});
|
||||
|
||||
setEnabled(true);
|
||||
assert.equal(current, true);
|
||||
assert.deepEqual(calls, ['runtime:false->true', 'set:true', 'emit:overlay-debug:set:true']);
|
||||
assert.deepEqual(calls, ['runtime:false->true', 'set:true']);
|
||||
});
|
||||
|
||||
test('open runtime options palette handler delegates to runtime', () => {
|
||||
|
||||
@@ -65,18 +65,15 @@ export function createSetOverlayDebugVisualizationEnabledHandler(deps: {
|
||||
currentEnabled: boolean,
|
||||
nextEnabled: boolean,
|
||||
setCurrentEnabled: (enabled: boolean) => void,
|
||||
broadcastToOverlayWindows: (channel: string, ...args: unknown[]) => void,
|
||||
) => void;
|
||||
getCurrentEnabled: () => boolean;
|
||||
setCurrentEnabled: (enabled: boolean) => void;
|
||||
broadcastToOverlayWindows: (channel: string, ...args: unknown[]) => void;
|
||||
}) {
|
||||
return (enabled: boolean): void => {
|
||||
deps.setOverlayDebugVisualizationEnabledRuntime(
|
||||
deps.getCurrentEnabled(),
|
||||
enabled,
|
||||
(next) => deps.setCurrentEnabled(next),
|
||||
(channel, ...args) => deps.broadcastToOverlayWindows(channel, ...args),
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -19,21 +19,16 @@ test('overlay runtime main deps builder maps runtime state and callbacks', () =>
|
||||
appState,
|
||||
overlayManager: {
|
||||
getVisibleOverlayVisible: () => true,
|
||||
getInvisibleOverlayVisible: () => false,
|
||||
},
|
||||
overlayVisibilityRuntime: {
|
||||
updateVisibleOverlayVisibility: () => calls.push('update-visible'),
|
||||
updateInvisibleOverlayVisibility: () => calls.push('update-invisible'),
|
||||
},
|
||||
overlayShortcutsRuntime: {
|
||||
syncOverlayShortcuts: () => calls.push('sync-shortcuts'),
|
||||
},
|
||||
getInitialInvisibleOverlayVisibility: () => true,
|
||||
createMainWindow: () => calls.push('create-main'),
|
||||
createInvisibleWindow: () => calls.push('create-invisible'),
|
||||
registerGlobalShortcuts: () => calls.push('register-shortcuts'),
|
||||
updateVisibleOverlayBounds: () => calls.push('visible-bounds'),
|
||||
updateInvisibleOverlayBounds: () => calls.push('invisible-bounds'),
|
||||
getOverlayWindows: () => [],
|
||||
getResolvedConfig: () => ({}),
|
||||
showDesktopNotification: () => calls.push('notify'),
|
||||
@@ -48,19 +43,14 @@ test('overlay runtime main deps builder maps runtime state and callbacks', () =>
|
||||
|
||||
const deps = build();
|
||||
assert.equal(deps.getBackendOverride(), 'x11');
|
||||
assert.equal(deps.getInitialInvisibleOverlayVisibility(), true);
|
||||
assert.equal(deps.isVisibleOverlayVisible(), true);
|
||||
assert.equal(deps.isInvisibleOverlayVisible(), false);
|
||||
assert.equal(deps.getMpvSocketPath(), '/tmp/mpv.sock');
|
||||
assert.equal(deps.getKnownWordCacheStatePath(), '/tmp/known-words-cache.json');
|
||||
|
||||
deps.createMainWindow();
|
||||
deps.createInvisibleWindow();
|
||||
deps.registerGlobalShortcuts();
|
||||
deps.updateVisibleOverlayBounds({ x: 0, y: 0, width: 10, height: 10 });
|
||||
deps.updateInvisibleOverlayBounds({ x: 0, y: 0, width: 10, height: 10 });
|
||||
deps.updateVisibleOverlayVisibility();
|
||||
deps.updateInvisibleOverlayVisibility();
|
||||
deps.syncOverlayShortcuts();
|
||||
deps.showDesktopNotification('title', {});
|
||||
|
||||
@@ -73,12 +63,9 @@ test('overlay runtime main deps builder maps runtime state and callbacks', () =>
|
||||
|
||||
assert.deepEqual(calls, [
|
||||
'create-main',
|
||||
'create-invisible',
|
||||
'register-shortcuts',
|
||||
'visible-bounds',
|
||||
'invisible-bounds',
|
||||
'update-visible',
|
||||
'update-invisible',
|
||||
'sync-shortcuts',
|
||||
'notify',
|
||||
]);
|
||||
|
||||
@@ -17,18 +17,14 @@ export function createBuildInitializeOverlayRuntimeMainDepsHandler(deps: {
|
||||
};
|
||||
overlayManager: {
|
||||
getVisibleOverlayVisible: () => boolean;
|
||||
getInvisibleOverlayVisible: () => boolean;
|
||||
};
|
||||
overlayVisibilityRuntime: {
|
||||
updateVisibleOverlayVisibility: () => void;
|
||||
updateInvisibleOverlayVisibility: () => void;
|
||||
};
|
||||
overlayShortcutsRuntime: {
|
||||
syncOverlayShortcuts: () => void;
|
||||
};
|
||||
getInitialInvisibleOverlayVisibility: () => boolean;
|
||||
createMainWindow: () => void;
|
||||
createInvisibleWindow: () => void;
|
||||
registerGlobalShortcuts: () => void;
|
||||
updateVisibleOverlayBounds: (geometry: {
|
||||
x: number;
|
||||
@@ -36,12 +32,6 @@ export function createBuildInitializeOverlayRuntimeMainDepsHandler(deps: {
|
||||
width: number;
|
||||
height: number;
|
||||
}) => void;
|
||||
updateInvisibleOverlayBounds: (geometry: {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
}) => void;
|
||||
getOverlayWindows: OverlayRuntimeOptionsMainDeps['getOverlayWindows'];
|
||||
getResolvedConfig: () => { ankiConnect?: AnkiConnectConfig };
|
||||
showDesktopNotification: (title: string, options: { body?: string; icon?: string }) => void;
|
||||
@@ -50,9 +40,7 @@ export function createBuildInitializeOverlayRuntimeMainDepsHandler(deps: {
|
||||
}) {
|
||||
return (): OverlayRuntimeOptionsMainDeps => ({
|
||||
getBackendOverride: () => deps.appState.backendOverride,
|
||||
getInitialInvisibleOverlayVisibility: () => deps.getInitialInvisibleOverlayVisibility(),
|
||||
createMainWindow: () => deps.createMainWindow(),
|
||||
createInvisibleWindow: () => deps.createInvisibleWindow(),
|
||||
registerGlobalShortcuts: () => deps.registerGlobalShortcuts(),
|
||||
updateVisibleOverlayBounds: (geometry: {
|
||||
x: number;
|
||||
@@ -60,18 +48,9 @@ export function createBuildInitializeOverlayRuntimeMainDepsHandler(deps: {
|
||||
width: number;
|
||||
height: number;
|
||||
}) => deps.updateVisibleOverlayBounds(geometry),
|
||||
updateInvisibleOverlayBounds: (geometry: {
|
||||
x: number;
|
||||
y: number;
|
||||
width: number;
|
||||
height: number;
|
||||
}) => deps.updateInvisibleOverlayBounds(geometry),
|
||||
isVisibleOverlayVisible: () => deps.overlayManager.getVisibleOverlayVisible(),
|
||||
isInvisibleOverlayVisible: () => deps.overlayManager.getInvisibleOverlayVisible(),
|
||||
updateVisibleOverlayVisibility: () =>
|
||||
deps.overlayVisibilityRuntime.updateVisibleOverlayVisibility(),
|
||||
updateInvisibleOverlayVisibility: () =>
|
||||
deps.overlayVisibilityRuntime.updateInvisibleOverlayVisibility(),
|
||||
getOverlayWindows: () => deps.getOverlayWindows(),
|
||||
syncOverlayShortcuts: () => deps.overlayShortcutsRuntime.syncOverlayShortcuts(),
|
||||
setWindowTracker: (tracker) => {
|
||||
|
||||
@@ -6,16 +6,11 @@ test('build initialize overlay runtime options maps dependencies', () => {
|
||||
const calls: string[] = [];
|
||||
const buildOptions = createBuildInitializeOverlayRuntimeOptionsHandler({
|
||||
getBackendOverride: () => 'x11',
|
||||
getInitialInvisibleOverlayVisibility: () => true,
|
||||
createMainWindow: () => calls.push('create-main'),
|
||||
createInvisibleWindow: () => calls.push('create-invisible'),
|
||||
registerGlobalShortcuts: () => calls.push('register-shortcuts'),
|
||||
updateVisibleOverlayBounds: () => calls.push('update-visible-bounds'),
|
||||
updateInvisibleOverlayBounds: () => calls.push('update-invisible-bounds'),
|
||||
isVisibleOverlayVisible: () => true,
|
||||
isInvisibleOverlayVisible: () => false,
|
||||
updateVisibleOverlayVisibility: () => calls.push('update-visible'),
|
||||
updateInvisibleOverlayVisibility: () => calls.push('update-invisible'),
|
||||
getOverlayWindows: () => [],
|
||||
syncOverlayShortcuts: () => calls.push('sync-shortcuts'),
|
||||
setWindowTracker: () => calls.push('set-tracker'),
|
||||
@@ -37,18 +32,13 @@ test('build initialize overlay runtime options maps dependencies', () => {
|
||||
|
||||
const options = buildOptions();
|
||||
assert.equal(options.backendOverride, 'x11');
|
||||
assert.equal(options.getInitialInvisibleOverlayVisibility(), true);
|
||||
assert.equal(options.isVisibleOverlayVisible(), true);
|
||||
assert.equal(options.isInvisibleOverlayVisible(), false);
|
||||
assert.equal(options.getMpvSocketPath(), '/tmp/mpv.sock');
|
||||
assert.equal(options.getKnownWordCacheStatePath(), '/tmp/known-words-cache.json');
|
||||
options.createMainWindow();
|
||||
options.createInvisibleWindow();
|
||||
options.registerGlobalShortcuts();
|
||||
options.updateVisibleOverlayBounds({ x: 0, y: 0, width: 10, height: 10 });
|
||||
options.updateInvisibleOverlayBounds({ x: 0, y: 0, width: 10, height: 10 });
|
||||
options.updateVisibleOverlayVisibility();
|
||||
options.updateInvisibleOverlayVisibility();
|
||||
options.syncOverlayShortcuts();
|
||||
options.setWindowTracker(null);
|
||||
options.setAnkiIntegration(null);
|
||||
@@ -56,12 +46,9 @@ test('build initialize overlay runtime options maps dependencies', () => {
|
||||
|
||||
assert.deepEqual(calls, [
|
||||
'create-main',
|
||||
'create-invisible',
|
||||
'register-shortcuts',
|
||||
'update-visible-bounds',
|
||||
'update-invisible-bounds',
|
||||
'update-visible',
|
||||
'update-invisible',
|
||||
'sync-shortcuts',
|
||||
'set-tracker',
|
||||
'set-anki',
|
||||
|
||||
@@ -9,16 +9,11 @@ import type { BaseWindowTracker } from '../../window-trackers';
|
||||
|
||||
type OverlayRuntimeOptions = {
|
||||
backendOverride: string | null;
|
||||
getInitialInvisibleOverlayVisibility: () => boolean;
|
||||
createMainWindow: () => void;
|
||||
createInvisibleWindow: () => void;
|
||||
registerGlobalShortcuts: () => void;
|
||||
updateVisibleOverlayBounds: (geometry: WindowGeometry) => void;
|
||||
updateInvisibleOverlayBounds: (geometry: WindowGeometry) => void;
|
||||
isVisibleOverlayVisible: () => boolean;
|
||||
isInvisibleOverlayVisible: () => boolean;
|
||||
updateVisibleOverlayVisibility: () => void;
|
||||
updateInvisibleOverlayVisibility: () => void;
|
||||
getOverlayWindows: () => BrowserWindow[];
|
||||
syncOverlayShortcuts: () => void;
|
||||
setWindowTracker: (tracker: BaseWindowTracker | null) => void;
|
||||
@@ -39,16 +34,11 @@ type OverlayRuntimeOptions = {
|
||||
|
||||
export function createBuildInitializeOverlayRuntimeOptionsHandler(deps: {
|
||||
getBackendOverride: () => string | null;
|
||||
getInitialInvisibleOverlayVisibility: () => boolean;
|
||||
createMainWindow: () => void;
|
||||
createInvisibleWindow: () => void;
|
||||
registerGlobalShortcuts: () => void;
|
||||
updateVisibleOverlayBounds: (geometry: WindowGeometry) => void;
|
||||
updateInvisibleOverlayBounds: (geometry: WindowGeometry) => void;
|
||||
isVisibleOverlayVisible: () => boolean;
|
||||
isInvisibleOverlayVisible: () => boolean;
|
||||
updateVisibleOverlayVisibility: () => void;
|
||||
updateInvisibleOverlayVisibility: () => void;
|
||||
getOverlayWindows: () => BrowserWindow[];
|
||||
syncOverlayShortcuts: () => void;
|
||||
setWindowTracker: (tracker: BaseWindowTracker | null) => void;
|
||||
@@ -68,16 +58,11 @@ export function createBuildInitializeOverlayRuntimeOptionsHandler(deps: {
|
||||
}) {
|
||||
return (): OverlayRuntimeOptions => ({
|
||||
backendOverride: deps.getBackendOverride(),
|
||||
getInitialInvisibleOverlayVisibility: deps.getInitialInvisibleOverlayVisibility,
|
||||
createMainWindow: deps.createMainWindow,
|
||||
createInvisibleWindow: deps.createInvisibleWindow,
|
||||
registerGlobalShortcuts: deps.registerGlobalShortcuts,
|
||||
updateVisibleOverlayBounds: deps.updateVisibleOverlayBounds,
|
||||
updateInvisibleOverlayBounds: deps.updateInvisibleOverlayBounds,
|
||||
isVisibleOverlayVisible: deps.isVisibleOverlayVisible,
|
||||
isInvisibleOverlayVisible: deps.isInvisibleOverlayVisible,
|
||||
updateVisibleOverlayVisibility: deps.updateVisibleOverlayVisibility,
|
||||
updateInvisibleOverlayVisibility: deps.updateInvisibleOverlayVisibility,
|
||||
getOverlayWindows: deps.getOverlayWindows,
|
||||
syncOverlayShortcuts: deps.syncOverlayShortcuts,
|
||||
setWindowTracker: deps.setWindowTracker,
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import {
|
||||
createBuildSetInvisibleOverlayVisibleMainDepsHandler,
|
||||
createBuildSetVisibleOverlayVisibleMainDepsHandler,
|
||||
createBuildToggleInvisibleOverlayMainDepsHandler,
|
||||
createBuildToggleVisibleOverlayMainDepsHandler,
|
||||
} from './overlay-visibility-actions-main-deps';
|
||||
|
||||
@@ -14,45 +12,14 @@ test('overlay visibility action main deps builders map callbacks', () => {
|
||||
setVisibleOverlayVisibleCore: () => calls.push('visible-core'),
|
||||
setVisibleOverlayVisibleState: (visible) => calls.push(`visible-state:${visible}`),
|
||||
updateVisibleOverlayVisibility: () => calls.push('update-visible'),
|
||||
updateInvisibleOverlayVisibility: () => calls.push('update-invisible'),
|
||||
syncInvisibleOverlayMousePassthrough: () => calls.push('sync'),
|
||||
shouldBindVisibleOverlayToMpvSubVisibility: () => true,
|
||||
isMpvConnected: () => true,
|
||||
setMpvSubVisibility: (visible) => calls.push(`mpv:${visible}`),
|
||||
})();
|
||||
setVisible.setVisibleOverlayVisibleCore({
|
||||
visible: true,
|
||||
setVisibleOverlayVisibleState: () => {},
|
||||
updateVisibleOverlayVisibility: () => {},
|
||||
updateInvisibleOverlayVisibility: () => {},
|
||||
syncInvisibleOverlayMousePassthrough: () => {},
|
||||
shouldBindVisibleOverlayToMpvSubVisibility: () => true,
|
||||
isMpvConnected: () => true,
|
||||
setMpvSubVisibility: () => {},
|
||||
});
|
||||
setVisible.setVisibleOverlayVisibleState(true);
|
||||
setVisible.updateVisibleOverlayVisibility();
|
||||
setVisible.updateInvisibleOverlayVisibility();
|
||||
setVisible.syncInvisibleOverlayMousePassthrough();
|
||||
assert.equal(setVisible.shouldBindVisibleOverlayToMpvSubVisibility(), true);
|
||||
assert.equal(setVisible.isMpvConnected(), true);
|
||||
setVisible.setMpvSubVisibility(false);
|
||||
|
||||
const setInvisible = createBuildSetInvisibleOverlayVisibleMainDepsHandler({
|
||||
setInvisibleOverlayVisibleCore: () => calls.push('invisible-core'),
|
||||
setInvisibleOverlayVisibleState: (visible) => calls.push(`invisible-state:${visible}`),
|
||||
updateInvisibleOverlayVisibility: () => calls.push('update-only-invisible'),
|
||||
syncInvisibleOverlayMousePassthrough: () => calls.push('sync-only'),
|
||||
})();
|
||||
setInvisible.setInvisibleOverlayVisibleCore({
|
||||
visible: false,
|
||||
setInvisibleOverlayVisibleState: () => {},
|
||||
updateInvisibleOverlayVisibility: () => {},
|
||||
syncInvisibleOverlayMousePassthrough: () => {},
|
||||
});
|
||||
setInvisible.setInvisibleOverlayVisibleState(false);
|
||||
setInvisible.updateInvisibleOverlayVisibility();
|
||||
setInvisible.syncInvisibleOverlayMousePassthrough();
|
||||
|
||||
const toggleVisible = createBuildToggleVisibleOverlayMainDepsHandler({
|
||||
getVisibleOverlayVisible: () => false,
|
||||
@@ -61,25 +28,10 @@ test('overlay visibility action main deps builders map callbacks', () => {
|
||||
assert.equal(toggleVisible.getVisibleOverlayVisible(), false);
|
||||
toggleVisible.setVisibleOverlayVisible(true);
|
||||
|
||||
const toggleInvisible = createBuildToggleInvisibleOverlayMainDepsHandler({
|
||||
getInvisibleOverlayVisible: () => true,
|
||||
setInvisibleOverlayVisible: (visible) => calls.push(`toggle-invisible:${visible}`),
|
||||
})();
|
||||
assert.equal(toggleInvisible.getInvisibleOverlayVisible(), true);
|
||||
toggleInvisible.setInvisibleOverlayVisible(false);
|
||||
|
||||
assert.deepEqual(calls, [
|
||||
'visible-core',
|
||||
'visible-state:true',
|
||||
'update-visible',
|
||||
'update-invisible',
|
||||
'sync',
|
||||
'mpv:false',
|
||||
'invisible-core',
|
||||
'invisible-state:false',
|
||||
'update-only-invisible',
|
||||
'sync-only',
|
||||
'toggle-visible:true',
|
||||
'toggle-invisible:false',
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -1,14 +1,10 @@
|
||||
import type {
|
||||
createSetInvisibleOverlayVisibleHandler,
|
||||
createSetVisibleOverlayVisibleHandler,
|
||||
createToggleInvisibleOverlayHandler,
|
||||
createToggleVisibleOverlayHandler,
|
||||
} from './overlay-visibility-actions';
|
||||
|
||||
type SetVisibleOverlayVisibleMainDeps = Parameters<typeof createSetVisibleOverlayVisibleHandler>[0];
|
||||
type SetInvisibleOverlayVisibleMainDeps = Parameters<typeof createSetInvisibleOverlayVisibleHandler>[0];
|
||||
type ToggleVisibleOverlayMainDeps = Parameters<typeof createToggleVisibleOverlayHandler>[0];
|
||||
type ToggleInvisibleOverlayMainDeps = Parameters<typeof createToggleInvisibleOverlayHandler>[0];
|
||||
|
||||
export function createBuildSetVisibleOverlayVisibleMainDepsHandler(
|
||||
deps: SetVisibleOverlayVisibleMainDeps,
|
||||
@@ -17,22 +13,6 @@ export function createBuildSetVisibleOverlayVisibleMainDepsHandler(
|
||||
setVisibleOverlayVisibleCore: (options) => deps.setVisibleOverlayVisibleCore(options),
|
||||
setVisibleOverlayVisibleState: (visible: boolean) => deps.setVisibleOverlayVisibleState(visible),
|
||||
updateVisibleOverlayVisibility: () => deps.updateVisibleOverlayVisibility(),
|
||||
updateInvisibleOverlayVisibility: () => deps.updateInvisibleOverlayVisibility(),
|
||||
syncInvisibleOverlayMousePassthrough: () => deps.syncInvisibleOverlayMousePassthrough(),
|
||||
shouldBindVisibleOverlayToMpvSubVisibility: () => deps.shouldBindVisibleOverlayToMpvSubVisibility(),
|
||||
isMpvConnected: () => deps.isMpvConnected(),
|
||||
setMpvSubVisibility: (visible: boolean) => deps.setMpvSubVisibility(visible),
|
||||
});
|
||||
}
|
||||
|
||||
export function createBuildSetInvisibleOverlayVisibleMainDepsHandler(
|
||||
deps: SetInvisibleOverlayVisibleMainDeps,
|
||||
) {
|
||||
return (): SetInvisibleOverlayVisibleMainDeps => ({
|
||||
setInvisibleOverlayVisibleCore: (options) => deps.setInvisibleOverlayVisibleCore(options),
|
||||
setInvisibleOverlayVisibleState: (visible: boolean) => deps.setInvisibleOverlayVisibleState(visible),
|
||||
updateInvisibleOverlayVisibility: () => deps.updateInvisibleOverlayVisibility(),
|
||||
syncInvisibleOverlayMousePassthrough: () => deps.syncInvisibleOverlayMousePassthrough(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -42,12 +22,3 @@ export function createBuildToggleVisibleOverlayMainDepsHandler(deps: ToggleVisib
|
||||
setVisibleOverlayVisible: (visible: boolean) => deps.setVisibleOverlayVisible(visible),
|
||||
});
|
||||
}
|
||||
|
||||
export function createBuildToggleInvisibleOverlayMainDepsHandler(
|
||||
deps: ToggleInvisibleOverlayMainDeps,
|
||||
) {
|
||||
return (): ToggleInvisibleOverlayMainDeps => ({
|
||||
getInvisibleOverlayVisible: () => deps.getInvisibleOverlayVisible(),
|
||||
setInvisibleOverlayVisible: (visible: boolean) => deps.setInvisibleOverlayVisible(visible),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import {
|
||||
createSetInvisibleOverlayVisibleHandler,
|
||||
createSetVisibleOverlayVisibleHandler,
|
||||
createToggleInvisibleOverlayHandler,
|
||||
createToggleVisibleOverlayHandler,
|
||||
} from './overlay-visibility-actions';
|
||||
|
||||
@@ -14,17 +12,9 @@ test('set visible overlay handler forwards dependencies to core', () => {
|
||||
calls.push(`core:${options.visible}`);
|
||||
options.setVisibleOverlayVisibleState(options.visible);
|
||||
options.updateVisibleOverlayVisibility();
|
||||
options.updateInvisibleOverlayVisibility();
|
||||
options.syncInvisibleOverlayMousePassthrough();
|
||||
options.setMpvSubVisibility(!options.visible);
|
||||
},
|
||||
setVisibleOverlayVisibleState: (visible) => calls.push(`state:${visible}`),
|
||||
updateVisibleOverlayVisibility: () => calls.push('update-visible'),
|
||||
updateInvisibleOverlayVisibility: () => calls.push('update-invisible'),
|
||||
syncInvisibleOverlayMousePassthrough: () => calls.push('sync-mouse'),
|
||||
shouldBindVisibleOverlayToMpvSubVisibility: () => true,
|
||||
isMpvConnected: () => true,
|
||||
setMpvSubVisibility: (visible) => calls.push(`mpv-sub:${visible}`),
|
||||
});
|
||||
|
||||
setVisible(true);
|
||||
@@ -32,30 +22,9 @@ test('set visible overlay handler forwards dependencies to core', () => {
|
||||
'core:true',
|
||||
'state:true',
|
||||
'update-visible',
|
||||
'update-invisible',
|
||||
'sync-mouse',
|
||||
'mpv-sub:false',
|
||||
]);
|
||||
});
|
||||
|
||||
test('set invisible overlay handler forwards dependencies to core', () => {
|
||||
const calls: string[] = [];
|
||||
const setInvisible = createSetInvisibleOverlayVisibleHandler({
|
||||
setInvisibleOverlayVisibleCore: (options) => {
|
||||
calls.push(`core:${options.visible}`);
|
||||
options.setInvisibleOverlayVisibleState(options.visible);
|
||||
options.updateInvisibleOverlayVisibility();
|
||||
options.syncInvisibleOverlayMousePassthrough();
|
||||
},
|
||||
setInvisibleOverlayVisibleState: (visible) => calls.push(`state:${visible}`),
|
||||
updateInvisibleOverlayVisibility: () => calls.push('update-invisible'),
|
||||
syncInvisibleOverlayMousePassthrough: () => calls.push('sync-mouse'),
|
||||
});
|
||||
|
||||
setInvisible(false);
|
||||
assert.deepEqual(calls, ['core:false', 'state:false', 'update-invisible', 'sync-mouse']);
|
||||
});
|
||||
|
||||
test('toggle visible overlay flips current visible state', () => {
|
||||
const calls: string[] = [];
|
||||
let current = false;
|
||||
@@ -71,19 +40,3 @@ test('toggle visible overlay flips current visible state', () => {
|
||||
toggle();
|
||||
assert.deepEqual(calls, ['set:true', 'set:false']);
|
||||
});
|
||||
|
||||
test('toggle invisible overlay flips current invisible state', () => {
|
||||
const calls: string[] = [];
|
||||
let current = true;
|
||||
const toggle = createToggleInvisibleOverlayHandler({
|
||||
getInvisibleOverlayVisible: () => current,
|
||||
setInvisibleOverlayVisible: (visible) => {
|
||||
current = visible;
|
||||
calls.push(`set:${visible}`);
|
||||
},
|
||||
});
|
||||
|
||||
toggle();
|
||||
toggle();
|
||||
assert.deepEqual(calls, ['set:false', 'set:true']);
|
||||
});
|
||||
|
||||
@@ -3,52 +3,15 @@ export function createSetVisibleOverlayVisibleHandler(deps: {
|
||||
visible: boolean;
|
||||
setVisibleOverlayVisibleState: (visible: boolean) => void;
|
||||
updateVisibleOverlayVisibility: () => void;
|
||||
updateInvisibleOverlayVisibility: () => void;
|
||||
syncInvisibleOverlayMousePassthrough: () => void;
|
||||
shouldBindVisibleOverlayToMpvSubVisibility: () => boolean;
|
||||
isMpvConnected: () => boolean;
|
||||
setMpvSubVisibility: (visible: boolean) => void;
|
||||
}) => void;
|
||||
setVisibleOverlayVisibleState: (visible: boolean) => void;
|
||||
updateVisibleOverlayVisibility: () => void;
|
||||
updateInvisibleOverlayVisibility: () => void;
|
||||
syncInvisibleOverlayMousePassthrough: () => void;
|
||||
shouldBindVisibleOverlayToMpvSubVisibility: () => boolean;
|
||||
isMpvConnected: () => boolean;
|
||||
setMpvSubVisibility: (visible: boolean) => void;
|
||||
}) {
|
||||
return (visible: boolean): void => {
|
||||
deps.setVisibleOverlayVisibleCore({
|
||||
visible,
|
||||
setVisibleOverlayVisibleState: deps.setVisibleOverlayVisibleState,
|
||||
updateVisibleOverlayVisibility: deps.updateVisibleOverlayVisibility,
|
||||
updateInvisibleOverlayVisibility: deps.updateInvisibleOverlayVisibility,
|
||||
syncInvisibleOverlayMousePassthrough: deps.syncInvisibleOverlayMousePassthrough,
|
||||
shouldBindVisibleOverlayToMpvSubVisibility:
|
||||
deps.shouldBindVisibleOverlayToMpvSubVisibility,
|
||||
isMpvConnected: deps.isMpvConnected,
|
||||
setMpvSubVisibility: deps.setMpvSubVisibility,
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
export function createSetInvisibleOverlayVisibleHandler(deps: {
|
||||
setInvisibleOverlayVisibleCore: (options: {
|
||||
visible: boolean;
|
||||
setInvisibleOverlayVisibleState: (visible: boolean) => void;
|
||||
updateInvisibleOverlayVisibility: () => void;
|
||||
syncInvisibleOverlayMousePassthrough: () => void;
|
||||
}) => void;
|
||||
setInvisibleOverlayVisibleState: (visible: boolean) => void;
|
||||
updateInvisibleOverlayVisibility: () => void;
|
||||
syncInvisibleOverlayMousePassthrough: () => void;
|
||||
}) {
|
||||
return (visible: boolean): void => {
|
||||
deps.setInvisibleOverlayVisibleCore({
|
||||
visible,
|
||||
setInvisibleOverlayVisibleState: deps.setInvisibleOverlayVisibleState,
|
||||
updateInvisibleOverlayVisibility: deps.updateInvisibleOverlayVisibility,
|
||||
syncInvisibleOverlayMousePassthrough: deps.syncInvisibleOverlayMousePassthrough,
|
||||
});
|
||||
};
|
||||
}
|
||||
@@ -61,12 +24,3 @@ export function createToggleVisibleOverlayHandler(deps: {
|
||||
deps.setVisibleOverlayVisible(!deps.getVisibleOverlayVisible());
|
||||
};
|
||||
}
|
||||
|
||||
export function createToggleInvisibleOverlayHandler(deps: {
|
||||
getInvisibleOverlayVisible: () => boolean;
|
||||
setInvisibleOverlayVisible: (visible: boolean) => void;
|
||||
}) {
|
||||
return (): void => {
|
||||
deps.setInvisibleOverlayVisible(!deps.getInvisibleOverlayVisible());
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,14 +8,11 @@ test('overlay visibility runtime main deps builder maps state and geometry callb
|
||||
const calls: string[] = [];
|
||||
let trackerNotReadyWarningShown = false;
|
||||
const mainWindow = { id: 'main' } as never;
|
||||
const invisibleWindow = { id: 'invisible' } as never;
|
||||
const tracker = { id: 'tracker' } as unknown as BaseWindowTracker;
|
||||
|
||||
const deps = createBuildOverlayVisibilityRuntimeMainDepsHandler({
|
||||
getMainWindow: () => mainWindow,
|
||||
getInvisibleWindow: () => invisibleWindow,
|
||||
getVisibleOverlayVisible: () => true,
|
||||
getInvisibleOverlayVisible: () => false,
|
||||
getWindowTracker: () => tracker,
|
||||
getTrackerNotReadyWarningShown: () => trackerNotReadyWarningShown,
|
||||
setTrackerNotReadyWarningShown: (shown) => {
|
||||
@@ -23,30 +20,35 @@ test('overlay visibility runtime main deps builder maps state and geometry callb
|
||||
calls.push(`tracker-warning:${shown}`);
|
||||
},
|
||||
updateVisibleOverlayBounds: () => calls.push('visible-bounds'),
|
||||
updateInvisibleOverlayBounds: () => calls.push('invisible-bounds'),
|
||||
ensureOverlayWindowLevel: () => calls.push('ensure-level'),
|
||||
syncPrimaryOverlayWindowLayer: (layer) => calls.push(`primary-layer:${layer}`),
|
||||
enforceOverlayLayerOrder: () => calls.push('enforce-order'),
|
||||
syncOverlayShortcuts: () => calls.push('sync-shortcuts'),
|
||||
isMacOSPlatform: () => true,
|
||||
showOverlayLoadingOsd: () => calls.push('overlay-loading-osd'),
|
||||
resolveFallbackBounds: () => ({ x: 0, y: 0, width: 20, height: 20 }),
|
||||
})();
|
||||
|
||||
assert.equal(deps.getMainWindow(), mainWindow);
|
||||
assert.equal(deps.getInvisibleWindow(), invisibleWindow);
|
||||
assert.equal(deps.getVisibleOverlayVisible(), true);
|
||||
assert.equal(deps.getInvisibleOverlayVisible(), false);
|
||||
assert.equal(deps.getTrackerNotReadyWarningShown(), false);
|
||||
deps.setTrackerNotReadyWarningShown(true);
|
||||
deps.updateVisibleOverlayBounds({ x: 0, y: 0, width: 10, height: 10 });
|
||||
deps.updateInvisibleOverlayBounds({ x: 0, y: 0, width: 10, height: 10 });
|
||||
deps.ensureOverlayWindowLevel(mainWindow);
|
||||
deps.syncPrimaryOverlayWindowLayer('visible');
|
||||
deps.enforceOverlayLayerOrder();
|
||||
deps.syncOverlayShortcuts();
|
||||
assert.equal(deps.isMacOSPlatform(), true);
|
||||
deps.showOverlayLoadingOsd('Overlay loading...');
|
||||
assert.deepEqual(deps.resolveFallbackBounds(), { x: 0, y: 0, width: 20, height: 20 });
|
||||
assert.equal(trackerNotReadyWarningShown, true);
|
||||
assert.deepEqual(calls, [
|
||||
'tracker-warning:true',
|
||||
'visible-bounds',
|
||||
'invisible-bounds',
|
||||
'ensure-level',
|
||||
'primary-layer:visible',
|
||||
'enforce-order',
|
||||
'sync-shortcuts',
|
||||
'overlay-loading-osd',
|
||||
]);
|
||||
});
|
||||
|
||||
@@ -7,18 +7,19 @@ export function createBuildOverlayVisibilityRuntimeMainDepsHandler(
|
||||
) {
|
||||
return (): OverlayVisibilityRuntimeDeps => ({
|
||||
getMainWindow: () => deps.getMainWindow(),
|
||||
getInvisibleWindow: () => deps.getInvisibleWindow(),
|
||||
getVisibleOverlayVisible: () => deps.getVisibleOverlayVisible(),
|
||||
getInvisibleOverlayVisible: () => deps.getInvisibleOverlayVisible(),
|
||||
getWindowTracker: () => deps.getWindowTracker(),
|
||||
getTrackerNotReadyWarningShown: () => deps.getTrackerNotReadyWarningShown(),
|
||||
setTrackerNotReadyWarningShown: (shown: boolean) => deps.setTrackerNotReadyWarningShown(shown),
|
||||
updateVisibleOverlayBounds: (geometry: WindowGeometry) =>
|
||||
deps.updateVisibleOverlayBounds(geometry),
|
||||
updateInvisibleOverlayBounds: (geometry: WindowGeometry) =>
|
||||
deps.updateInvisibleOverlayBounds(geometry),
|
||||
ensureOverlayWindowLevel: (window: BrowserWindow) => deps.ensureOverlayWindowLevel(window),
|
||||
syncPrimaryOverlayWindowLayer: (layer: 'visible') =>
|
||||
deps.syncPrimaryOverlayWindowLayer(layer),
|
||||
enforceOverlayLayerOrder: () => deps.enforceOverlayLayerOrder(),
|
||||
syncOverlayShortcuts: () => deps.syncOverlayShortcuts(),
|
||||
isMacOSPlatform: () => deps.isMacOSPlatform(),
|
||||
showOverlayLoadingOsd: (message: string) => deps.showOverlayLoadingOsd(message),
|
||||
resolveFallbackBounds: () => deps.resolveFallbackBounds(),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -4,10 +4,7 @@ import { createOverlayVisibilityRuntime } from './overlay-visibility-runtime';
|
||||
|
||||
test('overlay visibility runtime wires set/toggle handlers through composed deps', () => {
|
||||
let visible = false;
|
||||
let invisible = true;
|
||||
let setVisibleCoreCalls = 0;
|
||||
let setInvisibleCoreCalls = 0;
|
||||
let lastBoundSubVisibility: boolean | null = null;
|
||||
|
||||
const runtime = createOverlayVisibilityRuntime({
|
||||
setVisibleOverlayVisibleDeps: {
|
||||
@@ -15,44 +12,17 @@ test('overlay visibility runtime wires set/toggle handlers through composed deps
|
||||
setVisibleCoreCalls += 1;
|
||||
options.setVisibleOverlayVisibleState(options.visible);
|
||||
options.updateVisibleOverlayVisibility();
|
||||
options.updateInvisibleOverlayVisibility();
|
||||
options.syncInvisibleOverlayMousePassthrough();
|
||||
if (options.shouldBindVisibleOverlayToMpvSubVisibility() && options.isMpvConnected()) {
|
||||
options.setMpvSubVisibility(options.visible);
|
||||
}
|
||||
},
|
||||
setVisibleOverlayVisibleState: (nextVisible) => {
|
||||
visible = nextVisible;
|
||||
},
|
||||
updateVisibleOverlayVisibility: () => {},
|
||||
updateInvisibleOverlayVisibility: () => {},
|
||||
syncInvisibleOverlayMousePassthrough: () => {},
|
||||
shouldBindVisibleOverlayToMpvSubVisibility: () => true,
|
||||
isMpvConnected: () => true,
|
||||
setMpvSubVisibility: (nextVisible) => {
|
||||
lastBoundSubVisibility = nextVisible;
|
||||
},
|
||||
},
|
||||
setInvisibleOverlayVisibleDeps: {
|
||||
setInvisibleOverlayVisibleCore: (options) => {
|
||||
setInvisibleCoreCalls += 1;
|
||||
options.setInvisibleOverlayVisibleState(options.visible);
|
||||
options.updateInvisibleOverlayVisibility();
|
||||
options.syncInvisibleOverlayMousePassthrough();
|
||||
},
|
||||
setInvisibleOverlayVisibleState: (nextVisible) => {
|
||||
invisible = nextVisible;
|
||||
},
|
||||
updateInvisibleOverlayVisibility: () => {},
|
||||
syncInvisibleOverlayMousePassthrough: () => {},
|
||||
},
|
||||
getVisibleOverlayVisible: () => visible,
|
||||
getInvisibleOverlayVisible: () => invisible,
|
||||
});
|
||||
|
||||
runtime.setVisibleOverlayVisible(true);
|
||||
assert.equal(visible, true);
|
||||
assert.equal(lastBoundSubVisibility, true);
|
||||
|
||||
runtime.toggleVisibleOverlay();
|
||||
assert.equal(visible, false);
|
||||
@@ -63,12 +33,5 @@ test('overlay visibility runtime wires set/toggle handlers through composed deps
|
||||
runtime.toggleOverlay();
|
||||
assert.equal(visible, false);
|
||||
|
||||
runtime.setInvisibleOverlayVisible(false);
|
||||
assert.equal(invisible, false);
|
||||
|
||||
runtime.toggleInvisibleOverlay();
|
||||
assert.equal(invisible, true);
|
||||
|
||||
assert.equal(setVisibleCoreCalls, 4);
|
||||
assert.equal(setInvisibleCoreCalls, 2);
|
||||
});
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
import {
|
||||
createSetInvisibleOverlayVisibleHandler,
|
||||
createSetVisibleOverlayVisibleHandler,
|
||||
createToggleInvisibleOverlayHandler,
|
||||
createToggleVisibleOverlayHandler,
|
||||
} from './overlay-visibility-actions';
|
||||
import {
|
||||
createBuildSetInvisibleOverlayVisibleMainDepsHandler,
|
||||
createBuildSetVisibleOverlayVisibleMainDepsHandler,
|
||||
createBuildToggleInvisibleOverlayMainDepsHandler,
|
||||
createBuildToggleVisibleOverlayMainDepsHandler,
|
||||
} from './overlay-visibility-actions-main-deps';
|
||||
import { createSetOverlayVisibleHandler, createToggleOverlayHandler } from './overlay-main-actions';
|
||||
@@ -19,15 +15,10 @@ import {
|
||||
type SetVisibleOverlayVisibleMainDeps = Parameters<
|
||||
typeof createBuildSetVisibleOverlayVisibleMainDepsHandler
|
||||
>[0];
|
||||
type SetInvisibleOverlayVisibleMainDeps = Parameters<
|
||||
typeof createBuildSetInvisibleOverlayVisibleMainDepsHandler
|
||||
>[0];
|
||||
|
||||
export type OverlayVisibilityRuntimeDeps = {
|
||||
setVisibleOverlayVisibleDeps: SetVisibleOverlayVisibleMainDeps;
|
||||
setInvisibleOverlayVisibleDeps: SetInvisibleOverlayVisibleMainDeps;
|
||||
getVisibleOverlayVisible: () => boolean;
|
||||
getInvisibleOverlayVisible: () => boolean;
|
||||
};
|
||||
|
||||
export function createOverlayVisibilityRuntime(deps: OverlayVisibilityRuntimeDeps) {
|
||||
@@ -38,25 +29,12 @@ export function createOverlayVisibilityRuntime(deps: OverlayVisibilityRuntimeDep
|
||||
setVisibleOverlayVisibleMainDeps,
|
||||
);
|
||||
|
||||
const setInvisibleOverlayVisibleMainDeps = createBuildSetInvisibleOverlayVisibleMainDepsHandler(
|
||||
deps.setInvisibleOverlayVisibleDeps,
|
||||
)();
|
||||
const setInvisibleOverlayVisible = createSetInvisibleOverlayVisibleHandler(
|
||||
setInvisibleOverlayVisibleMainDeps,
|
||||
);
|
||||
|
||||
const toggleVisibleOverlayMainDeps = createBuildToggleVisibleOverlayMainDepsHandler({
|
||||
getVisibleOverlayVisible: deps.getVisibleOverlayVisible,
|
||||
setVisibleOverlayVisible: (visible) => setVisibleOverlayVisible(visible),
|
||||
})();
|
||||
const toggleVisibleOverlay = createToggleVisibleOverlayHandler(toggleVisibleOverlayMainDeps);
|
||||
|
||||
const toggleInvisibleOverlayMainDeps = createBuildToggleInvisibleOverlayMainDepsHandler({
|
||||
getInvisibleOverlayVisible: deps.getInvisibleOverlayVisible,
|
||||
setInvisibleOverlayVisible: (visible) => setInvisibleOverlayVisible(visible),
|
||||
})();
|
||||
const toggleInvisibleOverlay = createToggleInvisibleOverlayHandler(toggleInvisibleOverlayMainDeps);
|
||||
|
||||
const setOverlayVisibleMainDeps = createBuildSetOverlayVisibleMainDepsHandler({
|
||||
setVisibleOverlayVisible: (visible) => setVisibleOverlayVisible(visible),
|
||||
})();
|
||||
@@ -69,9 +47,7 @@ export function createOverlayVisibilityRuntime(deps: OverlayVisibilityRuntimeDep
|
||||
|
||||
return {
|
||||
setVisibleOverlayVisible,
|
||||
setInvisibleOverlayVisible,
|
||||
toggleVisibleOverlay,
|
||||
toggleInvisibleOverlay,
|
||||
setOverlayVisible,
|
||||
toggleOverlay,
|
||||
};
|
||||
|
||||
@@ -3,7 +3,6 @@ import test from 'node:test';
|
||||
import {
|
||||
createBuildEnforceOverlayLayerOrderMainDepsHandler,
|
||||
createBuildEnsureOverlayWindowLevelMainDepsHandler,
|
||||
createBuildUpdateInvisibleOverlayBoundsMainDepsHandler,
|
||||
createBuildUpdateVisibleOverlayBoundsMainDepsHandler,
|
||||
} from './overlay-window-layout-main-deps';
|
||||
|
||||
@@ -11,14 +10,9 @@ test('overlay window layout main deps builders map callbacks', () => {
|
||||
const calls: string[] = [];
|
||||
|
||||
const visible = createBuildUpdateVisibleOverlayBoundsMainDepsHandler({
|
||||
setOverlayWindowBounds: (layer) => calls.push(`visible:${layer}`),
|
||||
setOverlayWindowBounds: () => calls.push('visible'),
|
||||
})();
|
||||
visible.setOverlayWindowBounds('visible', { x: 0, y: 0, width: 1, height: 1 });
|
||||
|
||||
const invisible = createBuildUpdateInvisibleOverlayBoundsMainDepsHandler({
|
||||
setOverlayWindowBounds: (layer) => calls.push(`invisible:${layer}`),
|
||||
})();
|
||||
invisible.setOverlayWindowBounds('invisible', { x: 0, y: 0, width: 1, height: 1 });
|
||||
visible.setOverlayWindowBounds({ x: 0, y: 0, width: 1, height: 1 });
|
||||
|
||||
const level = createBuildEnsureOverlayWindowLevelMainDepsHandler({
|
||||
ensureOverlayWindowLevelCore: () => calls.push('ensure'),
|
||||
@@ -28,27 +22,20 @@ test('overlay window layout main deps builders map callbacks', () => {
|
||||
const order = createBuildEnforceOverlayLayerOrderMainDepsHandler({
|
||||
enforceOverlayLayerOrderCore: () => calls.push('order'),
|
||||
getVisibleOverlayVisible: () => true,
|
||||
getInvisibleOverlayVisible: () => false,
|
||||
getMainWindow: () => ({ kind: 'main' }),
|
||||
getInvisibleWindow: () => ({ kind: 'invisible' }),
|
||||
ensureOverlayWindowLevel: () => calls.push('order-level'),
|
||||
})();
|
||||
order.enforceOverlayLayerOrderCore({
|
||||
visibleOverlayVisible: true,
|
||||
invisibleOverlayVisible: false,
|
||||
mainWindow: null,
|
||||
invisibleWindow: null,
|
||||
ensureOverlayWindowLevel: () => {},
|
||||
});
|
||||
assert.equal(order.getVisibleOverlayVisible(), true);
|
||||
assert.equal(order.getInvisibleOverlayVisible(), false);
|
||||
assert.deepEqual(order.getMainWindow(), { kind: 'main' });
|
||||
assert.deepEqual(order.getInvisibleWindow(), { kind: 'invisible' });
|
||||
order.ensureOverlayWindowLevel({});
|
||||
|
||||
assert.deepEqual(calls, [
|
||||
'visible:visible',
|
||||
'invisible:invisible',
|
||||
'visible',
|
||||
'ensure',
|
||||
'order',
|
||||
'order-level',
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
import type {
|
||||
createEnforceOverlayLayerOrderHandler,
|
||||
createEnsureOverlayWindowLevelHandler,
|
||||
createUpdateInvisibleOverlayBoundsHandler,
|
||||
createUpdateVisibleOverlayBoundsHandler,
|
||||
} from './overlay-window-layout';
|
||||
|
||||
type UpdateVisibleOverlayBoundsMainDeps = Parameters<typeof createUpdateVisibleOverlayBoundsHandler>[0];
|
||||
type UpdateInvisibleOverlayBoundsMainDeps = Parameters<typeof createUpdateInvisibleOverlayBoundsHandler>[0];
|
||||
type EnsureOverlayWindowLevelMainDeps = Parameters<typeof createEnsureOverlayWindowLevelHandler>[0];
|
||||
type EnforceOverlayLayerOrderMainDeps = Parameters<typeof createEnforceOverlayLayerOrderHandler>[0];
|
||||
|
||||
@@ -14,15 +12,7 @@ export function createBuildUpdateVisibleOverlayBoundsMainDepsHandler(
|
||||
deps: UpdateVisibleOverlayBoundsMainDeps,
|
||||
) {
|
||||
return (): UpdateVisibleOverlayBoundsMainDeps => ({
|
||||
setOverlayWindowBounds: (layer, geometry) => deps.setOverlayWindowBounds(layer, geometry),
|
||||
});
|
||||
}
|
||||
|
||||
export function createBuildUpdateInvisibleOverlayBoundsMainDepsHandler(
|
||||
deps: UpdateInvisibleOverlayBoundsMainDeps,
|
||||
) {
|
||||
return (): UpdateInvisibleOverlayBoundsMainDeps => ({
|
||||
setOverlayWindowBounds: (layer, geometry) => deps.setOverlayWindowBounds(layer, geometry),
|
||||
setOverlayWindowBounds: (geometry) => deps.setOverlayWindowBounds(geometry),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -40,9 +30,7 @@ export function createBuildEnforceOverlayLayerOrderMainDepsHandler(
|
||||
return (): EnforceOverlayLayerOrderMainDeps => ({
|
||||
enforceOverlayLayerOrderCore: (params) => deps.enforceOverlayLayerOrderCore(params),
|
||||
getVisibleOverlayVisible: () => deps.getVisibleOverlayVisible(),
|
||||
getInvisibleOverlayVisible: () => deps.getInvisibleOverlayVisible(),
|
||||
getMainWindow: () => deps.getMainWindow(),
|
||||
getInvisibleWindow: () => deps.getInvisibleWindow(),
|
||||
ensureOverlayWindowLevel: (window: unknown) => deps.ensureOverlayWindowLevel(window),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -3,26 +3,17 @@ import assert from 'node:assert/strict';
|
||||
import {
|
||||
createEnforceOverlayLayerOrderHandler,
|
||||
createEnsureOverlayWindowLevelHandler,
|
||||
createUpdateInvisibleOverlayBoundsHandler,
|
||||
createUpdateVisibleOverlayBoundsHandler,
|
||||
} from './overlay-window-layout';
|
||||
|
||||
test('visible bounds handler writes visible layer geometry', () => {
|
||||
const calls: string[] = [];
|
||||
const calls: Array<{ x: number; y: number; width: number; height: number }> = [];
|
||||
const handleVisible = createUpdateVisibleOverlayBoundsHandler({
|
||||
setOverlayWindowBounds: (layer) => calls.push(layer),
|
||||
setOverlayWindowBounds: (geometry) => calls.push(geometry),
|
||||
});
|
||||
handleVisible({ x: 0, y: 0, width: 100, height: 50 });
|
||||
assert.deepEqual(calls, ['visible']);
|
||||
});
|
||||
|
||||
test('invisible bounds handler writes invisible layer geometry', () => {
|
||||
const calls: string[] = [];
|
||||
const handleInvisible = createUpdateInvisibleOverlayBoundsHandler({
|
||||
setOverlayWindowBounds: (layer) => calls.push(layer),
|
||||
});
|
||||
handleInvisible({ x: 0, y: 0, width: 100, height: 50 });
|
||||
assert.deepEqual(calls, ['invisible']);
|
||||
const geometry = { x: 0, y: 0, width: 100, height: 50 };
|
||||
handleVisible(geometry);
|
||||
assert.deepEqual(calls, [geometry]);
|
||||
});
|
||||
|
||||
test('ensure overlay window level handler delegates to core', () => {
|
||||
@@ -39,15 +30,12 @@ test('enforce overlay layer order handler forwards resolved state', () => {
|
||||
const enforce = createEnforceOverlayLayerOrderHandler({
|
||||
enforceOverlayLayerOrderCore: (params) => {
|
||||
calls.push(params.visibleOverlayVisible ? 'visible-on' : 'visible-off');
|
||||
calls.push(params.invisibleOverlayVisible ? 'invisible-on' : 'invisible-off');
|
||||
params.ensureOverlayWindowLevel({});
|
||||
},
|
||||
getVisibleOverlayVisible: () => true,
|
||||
getInvisibleOverlayVisible: () => false,
|
||||
getMainWindow: () => ({}),
|
||||
getInvisibleWindow: () => ({}),
|
||||
ensureOverlayWindowLevel: () => calls.push('ensure-level'),
|
||||
});
|
||||
enforce();
|
||||
assert.deepEqual(calls, ['visible-on', 'invisible-off', 'ensure-level']);
|
||||
assert.deepEqual(calls, ['visible-on', 'ensure-level']);
|
||||
});
|
||||
|
||||
@@ -1,18 +1,10 @@
|
||||
import type { WindowGeometry } from '../../types';
|
||||
|
||||
export function createUpdateVisibleOverlayBoundsHandler(deps: {
|
||||
setOverlayWindowBounds: (layer: 'visible' | 'invisible', geometry: WindowGeometry) => void;
|
||||
setOverlayWindowBounds: (geometry: WindowGeometry) => void;
|
||||
}) {
|
||||
return (geometry: WindowGeometry): void => {
|
||||
deps.setOverlayWindowBounds('visible', geometry);
|
||||
};
|
||||
}
|
||||
|
||||
export function createUpdateInvisibleOverlayBoundsHandler(deps: {
|
||||
setOverlayWindowBounds: (layer: 'visible' | 'invisible', geometry: WindowGeometry) => void;
|
||||
}) {
|
||||
return (geometry: WindowGeometry): void => {
|
||||
deps.setOverlayWindowBounds('invisible', geometry);
|
||||
deps.setOverlayWindowBounds(geometry);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -27,23 +19,17 @@ export function createEnsureOverlayWindowLevelHandler(deps: {
|
||||
export function createEnforceOverlayLayerOrderHandler(deps: {
|
||||
enforceOverlayLayerOrderCore: (params: {
|
||||
visibleOverlayVisible: boolean;
|
||||
invisibleOverlayVisible: boolean;
|
||||
mainWindow: unknown;
|
||||
invisibleWindow: unknown;
|
||||
ensureOverlayWindowLevel: (window: unknown) => void;
|
||||
}) => void;
|
||||
getVisibleOverlayVisible: () => boolean;
|
||||
getInvisibleOverlayVisible: () => boolean;
|
||||
getMainWindow: () => unknown;
|
||||
getInvisibleWindow: () => unknown;
|
||||
ensureOverlayWindowLevel: (window: unknown) => void;
|
||||
}) {
|
||||
return (): void => {
|
||||
deps.enforceOverlayLayerOrderCore({
|
||||
visibleOverlayVisible: deps.getVisibleOverlayVisible(),
|
||||
invisibleOverlayVisible: deps.getInvisibleOverlayVisible(),
|
||||
mainWindow: deps.getMainWindow(),
|
||||
invisibleWindow: deps.getInvisibleWindow(),
|
||||
ensureOverlayWindowLevel: deps.ensureOverlayWindowLevel,
|
||||
});
|
||||
};
|
||||
|
||||
@@ -64,7 +64,6 @@ test('config derived runtime main deps builder maps callbacks', () => {
|
||||
const deps = createBuildConfigDerivedRuntimeMainDepsHandler({
|
||||
getResolvedConfig: () => ({ jimaku: {} } as never),
|
||||
getRuntimeOptionsManager: () => null,
|
||||
platform: 'darwin',
|
||||
defaultJimakuLanguagePreference: 'ja',
|
||||
defaultJimakuMaxEntryResults: 20,
|
||||
defaultJimakuApiBaseUrl: 'https://api.example.com',
|
||||
@@ -72,7 +71,6 @@ test('config derived runtime main deps builder maps callbacks', () => {
|
||||
|
||||
assert.deepEqual(deps.getResolvedConfig(), { jimaku: {} });
|
||||
assert.equal(deps.getRuntimeOptionsManager(), null);
|
||||
assert.equal(deps.platform, 'darwin');
|
||||
assert.equal(deps.defaultJimakuLanguagePreference, 'ja');
|
||||
assert.equal(deps.defaultJimakuMaxEntryResults, 20);
|
||||
assert.equal(deps.defaultJimakuApiBaseUrl, 'https://api.example.com');
|
||||
|
||||
@@ -35,7 +35,6 @@ export function createBuildConfigDerivedRuntimeMainDepsHandler(deps: ConfigDeriv
|
||||
return (): ConfigDerivedRuntimeDeps => ({
|
||||
getResolvedConfig: () => deps.getResolvedConfig(),
|
||||
getRuntimeOptionsManager: () => deps.getRuntimeOptionsManager(),
|
||||
platform: deps.platform,
|
||||
defaultJimakuLanguagePreference: deps.defaultJimakuLanguagePreference,
|
||||
defaultJimakuMaxEntryResults: deps.defaultJimakuMaxEntryResults,
|
||||
defaultJimakuApiBaseUrl: deps.defaultJimakuApiBaseUrl,
|
||||
|
||||
Reference in New Issue
Block a user