From 51b8928df5a2c350edb5a29adf21c6e86aa2fe92 Mon Sep 17 00:00:00 2001 From: 4gray Date: Sun, 4 Oct 2026 09:47:23 +0200 Subject: [PATCH] fix(workspace): keep the macOS rail clear of the lights when zoomed out App zoom (webFrame.setZoomLevel) scales CSS pixels but not the native traffic lights: at the minimum zoom the fixed 56px inset put the first rail link at about 27 window pixels, over the buttons (y 20 to 34). The rail now publishes the page zoom factor (outerWidth / innerWidth, refreshed on resize) and keeps at least 48 window pixels: max(56px, 48px / zoom). The macOS E2E now requires a native button position instead of falling back to y=0, measures the gap in window pixels, and repeats it at the minimum zoom (fails at 27 without this change). Co-Authored-By: Claude Opus 5.5 --- .../src/window-controls.e2e.ts | 33 +++++++++++++-- docs/architecture/workspace-shell.md | 6 ++- .../workspace-shell-rail.component.html | 6 ++- .../workspace-shell-rail.component.scss | 4 +- .../workspace-shell-rail.component.spec.ts | 42 +++++++++++++++++++ .../workspace-shell-rail.component.ts | 35 +++++++++++++++- 6 files changed, 119 insertions(+), 7 deletions(-) diff --git a/apps/electron-backend-e2e/src/window-controls.e2e.ts b/apps/electron-backend-e2e/src/window-controls.e2e.ts index fba81345f..da90fec62 100644 --- a/apps/electron-backend-e2e/src/window-controls.e2e.ts +++ b/apps/electron-backend-e2e/src/window-controls.e2e.ts @@ -217,10 +217,37 @@ test.describe('macOS traffic lights', () => { const lights = await app.electronApp.evaluate(({ BrowserWindow }) => BrowserWindow.getAllWindows()[0]?.getWindowButtonPosition() ); - // The buttons are about 14pt tall; keep a visible gap below them. - expect(linkBox?.y ?? 0).toBeGreaterThanOrEqual( - (lights?.y ?? 0) + 14 + 16 + expect(lights, 'native window button position').toBeTruthy(); + const lightsY = lights?.y ?? Number.NaN; + // The buttons are about 14pt tall; keep a visible gap below them, + // measured in window pixels (CSS pixels times the zoom factor). + const linkTopInWindowPixels = async (): Promise => { + const [box, zoom] = await Promise.all([ + firstLink.boundingBox(), + page.evaluate(() => window.outerWidth / window.innerWidth), + ]); + return (box?.y ?? Number.NaN) * zoom; + }; + expect(await linkTopInWindowPixels()).toBeGreaterThanOrEqual( + lightsY + 14 + 16 ); + + // App zoom scales CSS pixels but not the native buttons: at the + // smallest zoom the inset must still clear them. + for (let step = 0; step < 8; step++) { + await page.evaluate(() => + window.electron.adjustZoomLevel('out') + ); + } + await expect + .poll(() => + page.evaluate(() => window.outerWidth / window.innerWidth) + ) + .toBeLessThan(0.6); + await expect + .poll(linkTopInWindowPixels) + .toBeGreaterThanOrEqual(lightsY + 14 + 8); + await page.evaluate(() => window.electron.adjustZoomLevel('reset')); } finally { await closeElectronApp(app); } diff --git a/docs/architecture/workspace-shell.md b/docs/architecture/workspace-shell.md index 6eafed5a5..31eaaec18 100644 --- a/docs/architecture/workspace-shell.md +++ b/docs/architecture/workspace-shell.md @@ -330,7 +330,11 @@ The Electron window hides the native title bar on all desktop platforms sit in the 56 px header band above the rail, so the macOS rail (`.app-rail.is-macos`) starts its first link at 56 px: level with the content area and the dashboard hero, with its hover surface clear of the - lights. `window-controls.e2e.ts` checks both on macOS. + lights. App zoom scales CSS pixels but not the lights, so the rail + publishes the page zoom factor (`outerWidth / innerWidth`, refreshed on + `resize`) as `--rail-zoom-factor` and keeps at least 48 window pixels when + zoomed out. `window-controls.e2e.ts` checks the alignment and the gap at + default and minimum zoom on macOS. 2. Windows and Linux use renderer-drawn window controls (`app-window-controls`, `libs/ui/components/src/lib/window-controls/`). `frame` is intentionally left untouched so native resize borders and diff --git a/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-rail/workspace-shell-rail.component.html b/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-rail/workspace-shell-rail.component.html index 04dadb780..13e259f50 100644 --- a/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-rail/workspace-shell-rail.component.html +++ b/libs/workspace/shell/feature/src/lib/workspace-shell/components/workspace-shell-rail/workspace-shell-rail.component.html @@ -1,6 +1,10 @@ -