From 4713925eb712f893b0ca5abf64a566d0c0c6446c Mon Sep 17 00:00:00 2001 From: Nathan Shan Date: Sat, 18 Jul 2026 02:03:44 +0800 Subject: [PATCH 1/3] fix(web): inset Windows desktop scrollbars from resize edge - Add Electron platform classes so Windows renderers can opt into edge compensation. - Reserve a narrow right inset on Windows desktop to keep scrollbars out of the native resize target. - Cover Windows platform class selection with unit tests. --- apps/web/src/index.css | 6 +++++ .../web/src/lib/windowControlsOverlay.test.ts | 25 ++++++++++++++++++ apps/web/src/lib/windowControlsOverlay.ts | 26 +++++++++++++++++++ apps/web/src/main.tsx | 6 ++++- 4 files changed, 62 insertions(+), 1 deletion(-) create mode 100644 apps/web/src/lib/windowControlsOverlay.test.ts diff --git a/apps/web/src/index.css b/apps/web/src/index.css index 5a0d1f394c0..4aa7a64fcfe 100644 --- a/apps/web/src/index.css +++ b/apps/web/src/index.css @@ -6,6 +6,7 @@ :root { --app-scrollbar-width: 6px; + --desktop-window-right-resize-inset: 0px; --workspace-topbar-height: 52px; --workspace-controls-top: 0px; --workspace-controls-left: calc(env(safe-area-inset-left) + 0.75rem); @@ -375,6 +376,11 @@ body { overflow-y: hidden; overscroll-behavior-y: none; padding-top: max(env(safe-area-inset-top), 0px); + padding-right: var(--desktop-window-right-resize-inset); +} + +.electron-windows { + --desktop-window-right-resize-inset: 6px; } /* App-chrome grain. Baked into each surface's own background (behind diff --git a/apps/web/src/lib/windowControlsOverlay.test.ts b/apps/web/src/lib/windowControlsOverlay.test.ts new file mode 100644 index 00000000000..269071808f8 --- /dev/null +++ b/apps/web/src/lib/windowControlsOverlay.test.ts @@ -0,0 +1,25 @@ +import { describe, expect, it } from "vite-plus/test"; + +import { + ELECTRON_CLASS_NAME, + ELECTRON_WINDOWS_CLASS_NAME, + getElectronPlatformClassNames, +} from "./windowControlsOverlay"; + +describe("getElectronPlatformClassNames", () => { + it("marks Windows Electron renderers for native resize gutter compensation", () => { + expect(getElectronPlatformClassNames("Win32")).toEqual([ + ELECTRON_CLASS_NAME, + ELECTRON_WINDOWS_CLASS_NAME, + ]); + expect(getElectronPlatformClassNames("windows_nt")).toEqual([ + ELECTRON_CLASS_NAME, + ELECTRON_WINDOWS_CLASS_NAME, + ]); + }); + + it("does not apply Windows scrollbar compensation on other desktop platforms", () => { + expect(getElectronPlatformClassNames("MacIntel")).toEqual([ELECTRON_CLASS_NAME]); + expect(getElectronPlatformClassNames("Linux x86_64")).toEqual([ELECTRON_CLASS_NAME]); + }); +}); diff --git a/apps/web/src/lib/windowControlsOverlay.ts b/apps/web/src/lib/windowControlsOverlay.ts index 8f9ae786b0e..11d6073571d 100644 --- a/apps/web/src/lib/windowControlsOverlay.ts +++ b/apps/web/src/lib/windowControlsOverlay.ts @@ -1,4 +1,8 @@ +import { isWindowsPlatform } from "./utils"; + const WCO_CLASS_NAME = "wco"; +export const ELECTRON_CLASS_NAME = "electron"; +export const ELECTRON_WINDOWS_CLASS_NAME = "electron-windows"; interface WindowControlsOverlayLike { readonly visible: boolean; @@ -38,3 +42,25 @@ export function syncDocumentWindowControlsOverlayClass(): () => void { overlay.removeEventListener("geometrychange", update); }; } + +export function getElectronPlatformClassNames( + platform: string, +): + | readonly [typeof ELECTRON_CLASS_NAME] + | readonly [typeof ELECTRON_CLASS_NAME, typeof ELECTRON_WINDOWS_CLASS_NAME] { + return isWindowsPlatform(platform) + ? [ELECTRON_CLASS_NAME, ELECTRON_WINDOWS_CLASS_NAME] + : [ELECTRON_CLASS_NAME]; +} + +export function syncDocumentElectronPlatformClasses(platform: string): () => void { + if (typeof document === "undefined") { + return () => {}; + } + + const classNames = getElectronPlatformClassNames(platform); + document.documentElement.classList.add(...classNames); + return () => { + document.documentElement.classList.remove(...classNames); + }; +} diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx index 453649bfdc5..c03cc65f654 100644 --- a/apps/web/src/main.tsx +++ b/apps/web/src/main.tsx @@ -15,7 +15,10 @@ import { isElectron } from "./env"; import { ManagedRelayAuthProvider } from "./cloud/managedAuth"; import { hasCloudPublicConfig } from "./cloud/publicConfig"; import { getRouter } from "./router"; -import { syncDocumentWindowControlsOverlayClass } from "./lib/windowControlsOverlay"; +import { + syncDocumentElectronPlatformClasses, + syncDocumentWindowControlsOverlayClass, +} from "./lib/windowControlsOverlay"; import { AppRoot } from "./AppRoot"; // Electron loads the app from a file-backed shell, so hash history avoids path resolution issues. @@ -24,6 +27,7 @@ const history = isElectron ? createHashHistory() : createBrowserHistory(); const router = getRouter(history); if (isElectron) { + syncDocumentElectronPlatformClasses(navigator.platform); syncDocumentWindowControlsOverlayClass(); } From 43d6034770b78f10d96cd8bbd0e5d3e6bcb4743c Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Fri, 17 Jul 2026 21:48:56 +0200 Subject: [PATCH 2/3] Delete apps/web/src/lib/windowControlsOverlay.test.ts --- .../web/src/lib/windowControlsOverlay.test.ts | 25 ------------------- 1 file changed, 25 deletions(-) delete mode 100644 apps/web/src/lib/windowControlsOverlay.test.ts diff --git a/apps/web/src/lib/windowControlsOverlay.test.ts b/apps/web/src/lib/windowControlsOverlay.test.ts deleted file mode 100644 index 269071808f8..00000000000 --- a/apps/web/src/lib/windowControlsOverlay.test.ts +++ /dev/null @@ -1,25 +0,0 @@ -import { describe, expect, it } from "vite-plus/test"; - -import { - ELECTRON_CLASS_NAME, - ELECTRON_WINDOWS_CLASS_NAME, - getElectronPlatformClassNames, -} from "./windowControlsOverlay"; - -describe("getElectronPlatformClassNames", () => { - it("marks Windows Electron renderers for native resize gutter compensation", () => { - expect(getElectronPlatformClassNames("Win32")).toEqual([ - ELECTRON_CLASS_NAME, - ELECTRON_WINDOWS_CLASS_NAME, - ]); - expect(getElectronPlatformClassNames("windows_nt")).toEqual([ - ELECTRON_CLASS_NAME, - ELECTRON_WINDOWS_CLASS_NAME, - ]); - }); - - it("does not apply Windows scrollbar compensation on other desktop platforms", () => { - expect(getElectronPlatformClassNames("MacIntel")).toEqual([ELECTRON_CLASS_NAME]); - expect(getElectronPlatformClassNames("Linux x86_64")).toEqual([ELECTRON_CLASS_NAME]); - }); -}); From a8702cb59cf62f6cf1106853beac967c60bfc9c1 Mon Sep 17 00:00:00 2001 From: Julius Marminge Date: Fri, 17 Jul 2026 21:49:24 +0200 Subject: [PATCH 3/3] Update windowControlsOverlay.ts --- apps/web/src/lib/windowControlsOverlay.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/web/src/lib/windowControlsOverlay.ts b/apps/web/src/lib/windowControlsOverlay.ts index 11d6073571d..42f9f13c7cd 100644 --- a/apps/web/src/lib/windowControlsOverlay.ts +++ b/apps/web/src/lib/windowControlsOverlay.ts @@ -1,8 +1,8 @@ import { isWindowsPlatform } from "./utils"; const WCO_CLASS_NAME = "wco"; -export const ELECTRON_CLASS_NAME = "electron"; -export const ELECTRON_WINDOWS_CLASS_NAME = "electron-windows"; +const ELECTRON_CLASS_NAME = "electron"; +const ELECTRON_WINDOWS_CLASS_NAME = "electron-windows"; interface WindowControlsOverlayLike { readonly visible: boolean;