mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-14 13:16:16 -08:00
feat(shell): zoom shortcuts on Windows and Linux (#1109)
Cmd/Ctrl and +/−/0 (numpad included) now zoom the app on every platform. Windows/Linux run without a menu (`setMenu(null)`), so the shortcuts are a renderer key binding in `WorkspaceKeyboardShortcutsService` calling a new synchronous, preload-local bridge method `adjustZoomLevel`, which steps the frame-bound temporary level through `webFrame.setZoomLevel` — never a main-process `webContents.setZoomLevel`, whose per-URL entry the app's `file://` path routing resets. Step and limits live in `libs/shared/interfaces` (`stepZoomLevel`: 0.5 per press like Electron's zoomIn/zoomOut roles, clamped to levels −4…6). On macOS the renderer sees the key before the application menu, and `preventDefault()` keeps the menu role from stepping a second time (Electron only performs the menu key equivalent in its unhandled-keyboard-event hook). Persistence is unchanged: the main process still reads the live level back on close, quit and reload. The zoom E2E now drives the real shortcuts (in, out, numpad, reset). Help dialog entries added and translated for all locales; contract updated in docs/architecture/workspace-shell.md. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
ab239b5043
commit
67327bfd29
38 files changed
+705
-31
No files matched your search
@@ -25,12 +25,23 @@ async function renderedZoomFactor(app: LaunchedElectronApp): Promise<number> {
|
||||
return contentWidth / innerWidth;
|
||||
}
|
||||
|
||||
/** What the macOS menu roles (Cmd +/−) do: `webContents.zoomLevel += 1`. */
|
||||
async function zoomInFromMenu(app: LaunchedElectronApp): Promise<void> {
|
||||
await app.electronApp.evaluate(({ BrowserWindow }) => {
|
||||
const [win] = BrowserWindow.getAllWindows();
|
||||
win.webContents.setZoomLevel(win.webContents.getZoomLevel() + 1);
|
||||
});
|
||||
/**
|
||||
* The app zoom shortcuts as the renderer receives them: Cmd on macOS, Ctrl
|
||||
* elsewhere. Dispatched through CDP they reach the renderer's keydown
|
||||
* binding directly (`WorkspaceKeyboardShortcutsService`), which steps the
|
||||
* frame-bound level through the preload bridge; the macOS application menu
|
||||
* is not in this path.
|
||||
*/
|
||||
const ZOOM_MODIFIER = process.platform === 'darwin' ? 'Meta' : 'Control';
|
||||
|
||||
async function pressZoomShortcut(
|
||||
app: LaunchedElectronApp,
|
||||
key: 'Equal' | 'Minus' | 'Digit0' | 'NumpadAdd' | 'NumpadSubtract',
|
||||
times = 1
|
||||
): Promise<void> {
|
||||
for (let index = 0; index < times; index += 1) {
|
||||
await app.mainWindow.keyboard.press(`${ZOOM_MODIFIER}+${key}`);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -58,11 +69,13 @@ async function resizeWindowBy(
|
||||
}, delta);
|
||||
}
|
||||
|
||||
// Zoom level 1 is a 1.2 factor. The packaged renderer runs under file:// with
|
||||
// path routing, where Chromium keys zoom by full URL: without frame-bound
|
||||
// (temporary) zoom the level "holds" only until the next resize after a
|
||||
// section change, and a restart brings back the default (issue #1109).
|
||||
// Zoom level 1 is a 1.2 factor; each shortcut press steps the level by 0.5
|
||||
// (Electron's zoomIn/zoomOut role step). The packaged renderer runs under
|
||||
// file:// with path routing, where Chromium keys zoom by full URL: without
|
||||
// frame-bound (temporary) zoom the level "holds" only until the next resize
|
||||
// after a section change, and a restart brings back the default (issue #1109).
|
||||
const ZOOMED_FACTOR = 1.2;
|
||||
const HALF_STEP_FACTOR = Math.sqrt(ZOOMED_FACTOR);
|
||||
|
||||
test('@electron @window keeps the zoom level across sections, resizes and a restart', async ({
|
||||
dataDir,
|
||||
@@ -72,7 +85,23 @@ test('@electron @window keeps the zoom level across sections, resizes and a rest
|
||||
try {
|
||||
expect(await renderedZoomFactor(app)).toBeCloseTo(1, 1);
|
||||
|
||||
await zoomInFromMenu(app);
|
||||
// In three times, out once (main keys and numpad): level 1.
|
||||
await pressZoomShortcut(app, 'Equal', 2);
|
||||
await pressZoomShortcut(app, 'NumpadAdd');
|
||||
await expect
|
||||
.poll(() => renderedZoomFactor(app))
|
||||
.toBeCloseTo(ZOOMED_FACTOR * HALF_STEP_FACTOR, 1);
|
||||
await pressZoomShortcut(app, 'Minus');
|
||||
await expect
|
||||
.poll(() => renderedZoomFactor(app))
|
||||
.toBeCloseTo(ZOOMED_FACTOR, 1);
|
||||
|
||||
// Reset returns to level 0, then zoom back in (three in, one out on
|
||||
// the numpad) for the persistence checks below.
|
||||
await pressZoomShortcut(app, 'Digit0');
|
||||
await expect.poll(() => renderedZoomFactor(app)).toBeCloseTo(1, 1);
|
||||
await pressZoomShortcut(app, 'Equal', 3);
|
||||
await pressZoomShortcut(app, 'NumpadSubtract');
|
||||
await expect
|
||||
.poll(() => renderedZoomFactor(app))
|
||||
.toBeCloseTo(ZOOMED_FACTOR, 1);
|
||||
|
||||
Reference in new issue
Block a user