From c1b2210411da79350a3f9f5a65a4a64971e4acad Mon Sep 17 00:00:00 2001 From: shivam <91240327+shivamhwp@users.noreply.github.com> Date: Tue, 15 Sep 2026 21:37:03 +0530 Subject: [PATCH] fix(desktop): keep the sidebar brand and window buttons aligned (#11906) Co-authored-by: T3 Code Test --- apps/desktop/src/preload.ts | 15 +++++++- apps/desktop/src/window/DesktopWindow.test.ts | 36 +++++++++++++++++++ apps/desktop/src/window/DesktopWindow.ts | 24 ++++++++++++- apps/web/src/components/AppSidebarLayout.tsx | 2 +- .../src/components/sidebar/SidebarChrome.tsx | 7 ++-- 5 files changed, 78 insertions(+), 6 deletions(-) diff --git a/apps/desktop/src/preload.ts b/apps/desktop/src/preload.ts index 453879d37afe4a42673564f2259ebec591ed5726..d4339c21e5fcb7a2f8bfc81250c65451d43b9010 100644 --- a/apps/desktop/src/preload.ts +++ b/apps/desktop/src/preload.ts @@ -6,7 +6,7 @@ import type { DesktopSnapShotEvent, } from "@t3tools/contracts"; import { exposeClerkBridge } from "@clerk/electron/preload"; -import { contextBridge, ipcRenderer } from "electron"; +import { contextBridge, ipcRenderer, webFrame } from "electron"; import * as IpcChannels from "./ipc/channels.ts"; @@ -32,6 +32,19 @@ exposeClerkBridge({ passkeys: true }); // oxlint-disable-next-line t3code/no-global-process-runtime -- Electron exposes the client platform in its sandboxed preload process. const clientPlatform = process.platform; +if (clientPlatform === "darwin") { + // Native window buttons do not scale with Chromium zoom. Keep their reserved + // space in native points, including when a zoomed page is reloaded. + const syncWindowControlInset = () => { + document.documentElement.style.setProperty( + "--desktop-window-controls-inset", + `${90 / webFrame.getZoomFactor()}px`, + ); + }; + window.addEventListener("DOMContentLoaded", syncWindowControlInset, { once: true }); + window.addEventListener("resize", syncWindowControlInset); +} + function unwrapEnsureSshEnvironmentResult(result: unknown) { if ( typeof result === "object" && diff --git a/apps/desktop/src/window/DesktopWindow.test.ts b/apps/desktop/src/window/DesktopWindow.test.ts index 338a02b26a1fbe30a794b353769579e562ba99a8..78834a55690e08cc84dca2e6c8691a40673f02ad 100644 --- a/apps/desktop/src/window/DesktopWindow.test.ts +++ b/apps/desktop/src/window/DesktopWindow.test.ts @@ -77,6 +77,7 @@ function makeFakeBrowserWindow() { isDestroyed: vi.fn(() => false), getURL: vi.fn(() => "t3code-dev://app/"), getZoomLevel: vi.fn(() => zoomLevel), + getZoomFactor: vi.fn(() => 1.2 ** zoomLevel), setZoomLevel: vi.fn((level: number) => { zoomLevel = level; }), @@ -118,6 +119,7 @@ function makeFakeBrowserWindow() { setOpacity: vi.fn(), setTitle: vi.fn(), setTitleBarOverlay: vi.fn(), + setWindowButtonPosition: vi.fn(), show: vi.fn(), webContents, }; @@ -136,6 +138,7 @@ function makeFakeBrowserWindow() { reload: webContents.reload, send: webContents.send, setZoomLevel: webContents.setZoomLevel, + setWindowButtonPosition: window.setWindowButtonPosition, setBackgroundThrottling: webContents.setBackgroundThrottling, setAutoHideCursor: window.setAutoHideCursor, setFullScreen: window.setFullScreen, @@ -742,6 +745,39 @@ describe("DesktopWindow", () => { }), ); + it.effect("keeps macOS window buttons centered when zooming and leaving fullscreen", () => + Effect.gen(function* () { + const fakeWindow = makeFakeBrowserWindow(); + const createCount = yield* Ref.make(0); + const mainWindow = yield* Ref.make>(Option.none()); + const layer = makeTestLayer({ window: fakeWindow.window, createCount, mainWindow }); + + yield* Effect.gen(function* () { + const desktopWindow = yield* DesktopWindow.DesktopWindow; + yield* desktopWindow.handleBackendReady(new URL("http://127.0.0.1:3773")); + + for (const direction of ["in", "in", "out", "reset", "out"] as const) { + yield* desktopWindow.zoomMain(direction); + const position = fakeWindow.setWindowButtonPosition.mock.lastCall?.[0]; + assert.isDefined(position); + // The 14-point native buttons should share the zoomed 52px header's center. + const headerCenter = 26 * fakeWindow.window.webContents.getZoomFactor(); + assert.isAtMost(Math.abs(position.y + 7 - headerCenter), 0.5); + assert.equal(position.x, 16); + } + + fakeWindow.isFullScreen.mockReturnValue(true); + fakeWindow.setWindowButtonPosition.mockClear(); + yield* desktopWindow.zoomMain("reset"); + assert.equal(fakeWindow.setWindowButtonPosition.mock.calls.length, 0); + + fakeWindow.isFullScreen.mockReturnValue(false); + fakeWindow.windowListeners.get("leave-full-screen")?.(); + assert.deepEqual(fakeWindow.setWindowButtonPosition.mock.lastCall, [{ x: 16, y: 19 }]); + }).pipe(Effect.provide(layer)); + }), + ); + it.effect("uses the persisted main window bounds when opening the window", () => Effect.gen(function* () { const fakeWindow = makeFakeBrowserWindow(); diff --git a/apps/desktop/src/window/DesktopWindow.ts b/apps/desktop/src/window/DesktopWindow.ts index e19a759621266259d8fe73213fc639c1b950911b..b3964cb929c7c273acdc7a88ad176ffbb98288f8 100644 --- a/apps/desktop/src/window/DesktopWindow.ts +++ b/apps/desktop/src/window/DesktopWindow.ts @@ -31,6 +31,22 @@ import * as ElectronApp from "../electron/ElectronApp.ts"; import { makeQuitShortcutHandler } from "./QuitHold.ts"; const TITLEBAR_HEIGHT = 40; +// Matches --workspace-topbar-height in apps/web/src/index.css. Native macOS +// buttons are 14 points tall and do not scale with the renderer's zoom. +const MACOS_WORKSPACE_TOPBAR_HEIGHT = 52; +const MACOS_WINDOW_BUTTON_RADIUS = 7; + +function syncMacosWindowButtons(window: Electron.BrowserWindow): void { + if (window.isDestroyed() || window.isFullScreen()) return; + window.setWindowButtonPosition({ + x: 16, + y: Math.round( + (MACOS_WORKSPACE_TOPBAR_HEIGHT * window.webContents.getZoomFactor()) / 2 - + MACOS_WINDOW_BUTTON_RADIUS, + ), + }); +} + const TITLEBAR_COLOR = "#01000000"; // #00000000 does not work correctly on Linux const TITLEBAR_LIGHT_SYMBOL_COLOR = "#1f2937"; const TITLEBAR_DARK_SYMBOL_COLOR = "#f8fafc"; @@ -241,7 +257,10 @@ function getWindowTitleBarOptions( if (platform === "darwin") { return { titleBarStyle: "hiddenInset", - trafficLightPosition: { x: 16, y: 18 }, + trafficLightPosition: { + x: 16, + y: MACOS_WORKSPACE_TOPBAR_HEIGHT / 2 - MACOS_WINDOW_BUTTON_RADIUS, + }, }; } @@ -660,6 +679,7 @@ export const make = Effect.gen(function* () { window.webContents.send(WINDOW_FULLSCREEN_STATE_CHANNEL, true); }); window.on("leave-full-screen", () => { + syncMacosWindowButtons(window); window.webContents.send(WINDOW_FULLSCREEN_STATE_CHANNEL, false); }); } @@ -720,6 +740,7 @@ export const make = Effect.gen(function* () { clearDevelopmentLoadRetry(); developmentLoadRetryIndex = 0; window.setTitle(environment.displayName); + if (environment.platform === "darwin") syncMacosWindowButtons(window); }); window.webContents.on( "did-fail-load", @@ -990,6 +1011,7 @@ export const make = Effect.gen(function* () { webContents.setZoomLevel( direction === "reset" ? 0 : webContents.getZoomLevel() + (direction === "in" ? 0.5 : -0.5), ); + if (environment.platform === "darwin") syncMacosWindowButtons(window.value); // Chromium pushes the new level down to embedded guests, which would zoom // the previewed page along with the app UI. The preview browser keeps its // own zoom, so put each guest back where the preview left it. diff --git a/apps/web/src/components/AppSidebarLayout.tsx b/apps/web/src/components/AppSidebarLayout.tsx index 6769586f7fa86fe74d153d207f904b8266ad7243..c00565f45902521e02322764adf93abc32790433 100644 --- a/apps/web/src/components/AppSidebarLayout.tsx +++ b/apps/web/src/components/AppSidebarLayout.tsx @@ -46,7 +46,7 @@ import { } from "./ui/sidebar"; import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip"; -const MACOS_TRAFFIC_LIGHTS_LEFT_INSET = "90px"; +const MACOS_TRAFFIC_LIGHTS_LEFT_INSET = "var(--desktop-window-controls-inset, 90px)"; function subscribeToViewportWidth(onChange: () => void): () => void { window.addEventListener("resize", onChange); diff --git a/apps/web/src/components/sidebar/SidebarChrome.tsx b/apps/web/src/components/sidebar/SidebarChrome.tsx index afbbf7671dfc69a898a004b8c3faa8cddb316fb8..5a332b86057f174667e85c6f850bc9aff6015887 100644 --- a/apps/web/src/components/sidebar/SidebarChrome.tsx +++ b/apps/web/src/components/sidebar/SidebarChrome.tsx @@ -91,11 +91,12 @@ function SidebarBrand({ onBackdrop }: { onBackdrop: boolean }) { )} to="/" > - - + {/* Center the visible capitals, without the font's ascender/descender space. */} + + -- 2.54.0