From 628b6a82d3afe796adaff08db0619008e5952940 Mon Sep 17 00:00:00 2001 From: Rohit C Prasad Date: Tue, 21 Jul 2026 23:16:40 -0700 Subject: [PATCH] gui: gate the macOS overlay layout to macOS Shell injects the OS; Windows/Linux keep the native title bar with no traffic-light insets. Thin scrollbars on Windows/Linux so panels stop losing width to classic scrollbars. --- surfaces/gui/e2e/composer-platform.spec.ts | 23 ++++++++++++++++++++++ surfaces/gui/src-tauri/src/lib.rs | 5 ++++- surfaces/gui/src/App.tsx | 7 +++++-- surfaces/gui/src/main.tsx | 3 +++ surfaces/gui/src/styles.css | 17 ++++++++++++++++ surfaces/gui/src/tauri.ts | 10 ++++++++++ 6 files changed, 62 insertions(+), 3 deletions(-) create mode 100644 surfaces/gui/e2e/composer-platform.spec.ts diff --git a/surfaces/gui/e2e/composer-platform.spec.ts b/surfaces/gui/e2e/composer-platform.spec.ts new file mode 100644 index 00000000..1f19d62d --- /dev/null +++ b/surfaces/gui/e2e/composer-platform.spec.ts @@ -0,0 +1,23 @@ +import { test, expect } from "./fixtures"; + +// The macOS overlay layout (traffic-light insets) must never apply on Windows — +// Windows keeps its native title bar (alignment bug, 2026-07-21). The shell injects +// __OCW_PLATFORM__; this simulates each platform and checks the overlay class. +test("windows platform gets no tauri-overlay layout", async ({ page }) => { + await page.addInitScript(() => { + (window as any).__TAURI__ = {}; // simulate the desktop shell + (window as any).__OCW_PLATFORM__ = "windows"; + }); + await page.goto("/"); + await expect(page.locator("html")).toHaveAttribute("data-platform", "windows"); + await expect(page.locator(".app.tauri-overlay")).toHaveCount(0); +}); + +test("macos platform keeps the overlay layout", async ({ page }) => { + await page.addInitScript(() => { + (window as any).__TAURI__ = {}; + (window as any).__OCW_PLATFORM__ = "macos"; + }); + await page.goto("/"); + await expect(page.locator(".app.tauri-overlay").first()).toBeVisible(); +}); diff --git a/surfaces/gui/src-tauri/src/lib.rs b/surfaces/gui/src-tauri/src/lib.rs index 0c7492e0..37cd6e07 100644 --- a/surfaces/gui/src-tauri/src/lib.rs +++ b/surfaces/gui/src-tauri/src/lib.rs @@ -577,7 +577,10 @@ pub fn run() { let http = format!("http://127.0.0.1:{port}"); let ws = format!("ws://127.0.0.1:{port}"); // Debug-format yields a quoted JS string literal. - let inject = format!("window.__COWORKER_HTTP__={http:?};window.__COWORKER_WS__={ws:?};"); + let inject = format!( + "window.__COWORKER_HTTP__={http:?};window.__COWORKER_WS__={ws:?};window.__OCW_PLATFORM__={:?};", + std::env::consts::OS + ); tauri::Builder::default() // MUST be the first plugin: when a second launch happens (e.g. the user relaunches diff --git a/surfaces/gui/src/App.tsx b/surfaces/gui/src/App.tsx index 93badb19..07728fea 100644 --- a/surfaces/gui/src/App.tsx +++ b/surfaces/gui/src/App.tsx @@ -33,7 +33,7 @@ import { baseName } from "./paths"; import { itemsFromMessages } from "./itemsFromMessages"; import { streamMode } from "./streamGate"; import { InboxItemCard } from "./components/InboxItemCard"; -import { isTauri, startWindowDrag } from "./tauri"; +import { isTauri, platformOS, startWindowDrag } from "./tauri"; import { Icon } from "./components/Icon"; import { Sidebar } from "./components/Sidebar"; import { Transcript } from "./components/Transcript"; @@ -1027,7 +1027,10 @@ export function App() { // tauri-overlay class + draws fake traffic lights at the real position) so the top-left can be // tuned in the preview without a DMG build. Never active in the real app (isTauri() short-circuits). const simOverlay = !desktop && new URLSearchParams(window.location.search).has("overlay"); - const overlay = desktop || simOverlay; + // Overlay layout is macOS-ONLY: Windows/Linux keep the native title bar, so the mac + // compensations (traffic-light insets, lowered top strips) must not apply there — + // they rendered as misalignments under Windows' native bar (caught 2026-07-21). + const overlay = (desktop && platformOS() === "macos") || simOverlay; const beginWindowDrag = (event: PointerEvent) => { if (!desktop || event.button !== 0) return; startWindowDrag(); diff --git a/surfaces/gui/src/main.tsx b/surfaces/gui/src/main.tsx index eeae5728..6cecf54d 100644 --- a/surfaces/gui/src/main.tsx +++ b/surfaces/gui/src/main.tsx @@ -2,10 +2,13 @@ import React from "react"; import ReactDOM from "react-dom/client"; import { App } from "./App"; import { initTheme } from "./theme"; +import { platformOS } from "./tauri"; import "./tailwind.css"; import "./styles.css"; initTheme(); +// Platform hook for CSS (html[data-platform="windows"] scrollbar styling etc.). +document.documentElement.dataset.platform = platformOS(); // A file dropped OUTSIDE a drop target (the composer) must never navigate the webview to the // file itself — the browser/WKWebView default. Drop targets stopPropagation-free preventDefault diff --git a/surfaces/gui/src/styles.css b/surfaces/gui/src/styles.css index 44ad1494..e01b3f83 100644 --- a/surfaces/gui/src/styles.css +++ b/surfaces/gui/src/styles.css @@ -1544,3 +1544,20 @@ html[data-theme="dark"] .hiw-sticky { background: #eab308; color: #3d2c05; } @keyframes toast-pulse { 50% { opacity: .35; } } .toast-drain { animation: toast-drain 5s linear forwards; } @keyframes toast-drain { to { width: 0; } } + +/* ============ Windows (WebView2) polish ============ */ +/* Classic Windows scrollbars consume ~17px of layout width that macOS's overlay + scrollbars don't — panels tuned on Mac ended up cramped/misaligned (caught + 2026-07-21). Thin, quiet scrollbars matching the app's neutral palette; scoped + to data-platform so macOS keeps its native overlay behavior. */ +html[data-platform="windows"] ::-webkit-scrollbar, +html[data-platform="linux"] ::-webkit-scrollbar { width: 10px; height: 10px; } +html[data-platform="windows"] ::-webkit-scrollbar-track, +html[data-platform="linux"] ::-webkit-scrollbar-track { background: transparent; } +html[data-platform="windows"] ::-webkit-scrollbar-thumb, +html[data-platform="linux"] ::-webkit-scrollbar-thumb { + background: var(--line-strong); border-radius: 5px; + border: 2px solid transparent; background-clip: content-box; +} +html[data-platform="windows"] ::-webkit-scrollbar-thumb:hover, +html[data-platform="linux"] ::-webkit-scrollbar-thumb:hover { background-color: var(--faint); } diff --git a/surfaces/gui/src/tauri.ts b/surfaces/gui/src/tauri.ts index ea46133a..95f3ccdb 100644 --- a/surfaces/gui/src/tauri.ts +++ b/surfaces/gui/src/tauri.ts @@ -6,6 +6,16 @@ export const isTauri = (): boolean => typeof (globalThis as any).__TAURI__ !== "undefined"; +// "macos" | "windows" | "linux" — injected by the shell (std::env::consts::OS) before the +// SPA loads; userAgent fallback covers browser dev. The macOS overlay-titlebar layout (and +// its traffic-light compensations) must NEVER apply on Windows, which keeps its native +// title bar (alignment bug, caught on Windows 2026-07-21). +export const platformOS = (): string => { + const injected = (globalThis as any).__OCW_PLATFORM__; + if (typeof injected === "string" && injected) return injected; + return /mac/i.test(navigator.userAgent) ? "macos" : /win/i.test(navigator.userAgent) ? "windows" : "linux"; +}; + export type DictationStatus = { recording: boolean; model_installed: boolean;