Ensure overlay modal grabs input

This commit is contained in:
2026-02-23 19:54:58 -08:00
parent fe8a71990a
commit 978cb8c401
24 changed files with 562 additions and 63 deletions

View File

@@ -20,11 +20,23 @@ 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 }),
setModalWindowBounds: (geometry) =>
calls.push(`modal-bounds:${geometry.x},${geometry.y},${geometry.width},${geometry.height}`),
})();
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 });
deps.setModalWindowBounds({ x: 10, y: 20, width: 30, height: 40 });
assert.deepEqual(calls, ['modal-bounds:10,20,30,40']);
});

View File

@@ -14,9 +14,15 @@ export function createBuildOverlayContentMeasurementStoreMainDepsHandler(
});
}
export function createBuildOverlayModalRuntimeMainDepsHandler(deps: OverlayWindowResolver) {
export function createBuildOverlayModalRuntimeMainDepsHandler(
deps: OverlayWindowResolver,
) {
return (): OverlayWindowResolver => ({
getMainWindow: () => deps.getMainWindow(),
getInvisibleWindow: () => deps.getInvisibleWindow(),
getModalWindow: () => deps.getModalWindow(),
createModalWindow: () => deps.createModalWindow(),
getModalGeometry: () => deps.getModalGeometry(),
setModalWindowBounds: (geometry) => deps.setModalWindowBounds(geometry),
});
}

View File

@@ -3,6 +3,7 @@ import test from 'node:test';
import {
createBuildCreateInvisibleWindowMainDepsHandler,
createBuildCreateMainWindowMainDepsHandler,
createBuildCreateModalWindowMainDepsHandler,
createBuildCreateOverlayWindowMainDepsHandler,
createBuildCreateSecondaryWindowMainDepsHandler,
} from './overlay-window-factory-main-deps';
@@ -47,5 +48,12 @@ test('overlay window factory main deps builders return mapped handlers', () => {
const secondaryDeps = buildSecondaryDeps();
secondaryDeps.setSecondaryWindow(null);
assert.deepEqual(calls, ['set-main', 'set-invisible', 'set-secondary']);
const buildModalDeps = createBuildCreateModalWindowMainDepsHandler({
createOverlayWindow: () => ({ id: 'modal' }),
setModalWindow: () => calls.push('set-modal'),
});
const modalDeps = buildModalDeps();
modalDeps.setModalWindow(null);
assert.deepEqual(calls, ['set-main', 'set-invisible', 'set-secondary', 'set-modal']);
});

View File

@@ -1,15 +1,15 @@
export function createBuildCreateOverlayWindowMainDepsHandler<TWindow>(deps: {
createOverlayWindowCore: (
kind: 'visible' | 'invisible' | 'secondary',
kind: 'visible' | 'invisible' | 'secondary' | 'modal',
options: {
isDev: boolean;
overlayDebugVisualizationEnabled: boolean;
ensureOverlayWindowLevel: (window: TWindow) => void;
onRuntimeOptionsChanged: () => void;
setOverlayDebugVisualizationEnabled: (enabled: boolean) => void;
isOverlayVisible: (windowKind: 'visible' | 'invisible' | 'secondary') => boolean;
isOverlayVisible: (windowKind: 'visible' | 'invisible' | 'secondary' | 'modal') => boolean;
tryHandleOverlayShortcutLocalFallback: (input: Electron.Input) => boolean;
onWindowClosed: (windowKind: 'visible' | 'invisible' | 'secondary') => void;
onWindowClosed: (windowKind: 'visible' | 'invisible' | 'secondary' | 'modal') => void;
},
) => TWindow;
isDev: boolean;
@@ -17,9 +17,9 @@ export function createBuildCreateOverlayWindowMainDepsHandler<TWindow>(deps: {
ensureOverlayWindowLevel: (window: TWindow) => void;
onRuntimeOptionsChanged: () => void;
setOverlayDebugVisualizationEnabled: (enabled: boolean) => void;
isOverlayVisible: (windowKind: 'visible' | 'invisible' | 'secondary') => boolean;
isOverlayVisible: (windowKind: 'visible' | 'invisible' | 'secondary' | 'modal') => boolean;
tryHandleOverlayShortcutLocalFallback: (input: Electron.Input) => boolean;
onWindowClosed: (windowKind: 'visible' | 'invisible' | 'secondary') => void;
onWindowClosed: (windowKind: 'visible' | 'invisible' | 'secondary' | 'modal') => void;
}) {
return () => ({
createOverlayWindowCore: deps.createOverlayWindowCore,
@@ -35,7 +35,7 @@ export function createBuildCreateOverlayWindowMainDepsHandler<TWindow>(deps: {
}
export function createBuildCreateMainWindowMainDepsHandler<TWindow>(deps: {
createOverlayWindow: (kind: 'visible' | 'invisible' | 'secondary') => TWindow;
createOverlayWindow: (kind: 'visible' | 'invisible' | 'secondary' | 'modal') => TWindow;
setMainWindow: (window: TWindow | null) => void;
}) {
return () => ({
@@ -45,7 +45,7 @@ export function createBuildCreateMainWindowMainDepsHandler<TWindow>(deps: {
}
export function createBuildCreateInvisibleWindowMainDepsHandler<TWindow>(deps: {
createOverlayWindow: (kind: 'visible' | 'invisible' | 'secondary') => TWindow;
createOverlayWindow: (kind: 'visible' | 'invisible' | 'secondary' | 'modal') => TWindow;
setInvisibleWindow: (window: TWindow | null) => void;
}) {
return () => ({
@@ -55,7 +55,7 @@ export function createBuildCreateInvisibleWindowMainDepsHandler<TWindow>(deps: {
}
export function createBuildCreateSecondaryWindowMainDepsHandler<TWindow>(deps: {
createOverlayWindow: (kind: 'visible' | 'invisible' | 'secondary') => TWindow;
createOverlayWindow: (kind: 'visible' | 'invisible' | 'secondary' | 'modal') => TWindow;
setSecondaryWindow: (window: TWindow | null) => void;
}) {
return () => ({
@@ -63,3 +63,13 @@ export function createBuildCreateSecondaryWindowMainDepsHandler<TWindow>(deps: {
setSecondaryWindow: deps.setSecondaryWindow,
});
}
export function createBuildCreateModalWindowMainDepsHandler<TWindow>(deps: {
createOverlayWindow: (kind: 'visible' | 'invisible' | 'secondary' | 'modal') => TWindow;
setModalWindow: (window: TWindow | null) => void;
}) {
return () => ({
createOverlayWindow: deps.createOverlayWindow,
setModalWindow: deps.setModalWindow,
});
}

View File

@@ -3,6 +3,7 @@ import assert from 'node:assert/strict';
import {
createCreateInvisibleWindowHandler,
createCreateMainWindowHandler,
createCreateModalWindowHandler,
createCreateOverlayWindowHandler,
createCreateSecondaryWindowHandler,
} from './overlay-window-factory';
@@ -80,3 +81,18 @@ test('create secondary window handler stores secondary window', () => {
assert.equal(createSecondaryWindow(), secondaryWindow);
assert.deepEqual(calls, ['create:secondary', 'set:secondary']);
});
test('create modal window handler stores modal window', () => {
const calls: string[] = [];
const modalWindow = { id: 'modal' };
const createModalWindow = createCreateModalWindowHandler({
createOverlayWindow: (kind) => {
calls.push(`create:${kind}`);
return modalWindow;
},
setModalWindow: (window) => calls.push(`set:${(window as { id: string }).id}`),
});
assert.equal(createModalWindow(), modalWindow);
assert.deepEqual(calls, ['create:modal', 'set:modal']);
});

View File

@@ -1,4 +1,4 @@
type OverlayWindowKind = 'visible' | 'invisible' | 'secondary';
type OverlayWindowKind = 'visible' | 'invisible' | 'secondary' | 'modal';
export function createCreateOverlayWindowHandler<TWindow>(deps: {
createOverlayWindowCore: (
@@ -69,3 +69,14 @@ export function createCreateSecondaryWindowHandler<TWindow>(deps: {
return window;
};
}
export function createCreateModalWindowHandler<TWindow>(deps: {
createOverlayWindow: (kind: OverlayWindowKind) => TWindow;
setModalWindow: (window: TWindow | null) => void;
}) {
return (): TWindow => {
const window = deps.createOverlayWindow('modal');
deps.setModalWindow(window);
return window;
};
}

View File

@@ -6,6 +6,7 @@ test('overlay window runtime handlers compose create/main/invisible handlers', (
let mainWindow: { kind: string } | null = null;
let invisibleWindow: { kind: string } | null = null;
let secondaryWindow: { kind: string } | null = null;
let modalWindow: { kind: string } | null = null;
let debugEnabled = false;
const calls: string[] = [];
@@ -32,6 +33,9 @@ test('overlay window runtime handlers compose create/main/invisible handlers', (
setSecondaryWindow: (window) => {
secondaryWindow = window;
},
setModalWindow: (window) => {
modalWindow = window;
},
});
assert.deepEqual(runtime.createOverlayWindow('visible'), { kind: 'visible' });
@@ -46,6 +50,8 @@ test('overlay window runtime handlers compose create/main/invisible handlers', (
assert.deepEqual(runtime.createSecondaryWindow(), { kind: 'secondary' });
assert.deepEqual(secondaryWindow, { kind: 'secondary' });
assert.deepEqual(runtime.createModalWindow(), { kind: 'modal' });
assert.deepEqual(modalWindow, { kind: 'modal' });
assert.equal(debugEnabled, false);
assert.deepEqual(calls, []);

View File

@@ -1,12 +1,14 @@
import {
createCreateInvisibleWindowHandler,
createCreateMainWindowHandler,
createCreateModalWindowHandler,
createCreateOverlayWindowHandler,
createCreateSecondaryWindowHandler,
} from './overlay-window-factory';
import {
createBuildCreateInvisibleWindowMainDepsHandler,
createBuildCreateMainWindowMainDepsHandler,
createBuildCreateModalWindowMainDepsHandler,
createBuildCreateOverlayWindowMainDepsHandler,
createBuildCreateSecondaryWindowMainDepsHandler,
} from './overlay-window-factory-main-deps';
@@ -20,6 +22,7 @@ export function createOverlayWindowRuntimeHandlers<TWindow>(deps: {
setMainWindow: (window: TWindow | null) => void;
setInvisibleWindow: (window: TWindow | null) => void;
setSecondaryWindow: (window: TWindow | null) => void;
setModalWindow: (window: TWindow | null) => void;
}) {
const createOverlayWindow = createCreateOverlayWindowHandler<TWindow>(
createBuildCreateOverlayWindowMainDepsHandler<TWindow>(deps.createOverlayWindowDeps)(),
@@ -42,11 +45,18 @@ export function createOverlayWindowRuntimeHandlers<TWindow>(deps: {
setSecondaryWindow: (window) => deps.setSecondaryWindow(window),
})(),
);
const createModalWindow = createCreateModalWindowHandler<TWindow>(
createBuildCreateModalWindowMainDepsHandler<TWindow>({
createOverlayWindow: (kind) => createOverlayWindow(kind),
setModalWindow: (window) => deps.setModalWindow(window),
})(),
);
return {
createOverlayWindow,
createMainWindow,
createInvisibleWindow,
createSecondaryWindow,
createModalWindow,
};
}