From 0cc7d94b9319956a9283f32bd324fb2b19b89dc6 Mon Sep 17 00:00:00 2001 From: Amir Fathi Date: Sun, 26 Jul 2026 12:23:14 +0000 Subject: [PATCH] fix(gui): keep a drag region reachable while onboarding is on screen MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit On macOS the native title bar is hidden (src-tauri/src/lib.rs gates TitleBarStyle::Overlay + hidden_title(true) behind cfg(target_os = "macos")), so moving the window depends entirely on a data-tauri-drag-region element receiving the pointer event. Onboarding's full-viewport backdrop (Onboarding.tsx, fixed inset-0 z-50) sits above both of the app's only drag surfaces, Sidebar's brand row and App.tsx's topbar, and carries no drag region of its own, so the window cannot be moved for as long as onboarding is on screen. Resizing is unaffected because it is handled by the native OS edge, outside the web content. The fix mirrors the app's own precedent for this exact situation: the boot splash (App.tsx, rendered before Sidebar/topbar mount) already renders a transparent `.titlebar-drag` strip at z-index 180, above any backdrop, for the same "no chrome mounted yet" gap. Onboarding now renders the identical strip while the macOS overlay layout is active. Verified in a clean container (Node 20, Playwright 1.61.1 chromium) against HEAD: added surfaces/gui/e2e/onboarding-drag.spec.ts, which simulates the macOS shell (__TAURI__ + __OCW_PLATFORM__=macos) and asserts a data-tauri-drag-region element is the actual elementFromPoint hit at its own coordinates while onboarding is showing. Fails on HEAD (the hit is the backdrop), passes with this change. Full suites: `npm test` (68/68) and `npm run e2e` (154/155, the one failure is nav-collapse.spec.ts's ⌘B-toggle test, which fails identically on unmodified HEAD and is unrelated to this change). Not verified: actual native window dragging in a compiled macOS build, which this Linux VPS cannot produce. The strip is visually transparent (matching the existing boot-splash strip), so a before/after screenshot of the running app would be pixel-identical; the behavioral proof above is what this PR relies on instead. Fixes #95 --- surfaces/gui/e2e/onboarding-drag.spec.ts | 40 ++++++++++++++++++++++ surfaces/gui/src/components/Onboarding.tsx | 16 +++++++++ 2 files changed, 56 insertions(+) create mode 100644 surfaces/gui/e2e/onboarding-drag.spec.ts diff --git a/surfaces/gui/e2e/onboarding-drag.spec.ts b/surfaces/gui/e2e/onboarding-drag.spec.ts new file mode 100644 index 00000000..52b15690 --- /dev/null +++ b/surfaces/gui/e2e/onboarding-drag.spec.ts @@ -0,0 +1,40 @@ +// Issue #95: on macOS the window has no native title bar while onboarding is on screen +// (App.tsx's `overlay`, gated to `platformOS() === "macos"` — hidden_title(true) in +// src-tauri/src/lib.rs), so the ONLY way to move the window is a `data-tauri-drag-region` +// element. Onboarding's full-viewport backdrop (z-50) sits over both of the app's drag +// surfaces (Sidebar's brand row, App.tsx's topbar), and carries no drag region of its own, +// so the window is stuck until onboarding finishes. +import { expect } from "@playwright/test"; +import { test } from "./fixtures"; + +async function openOnboarding(page) { + await page.goto("/"); + await page.getByTestId("account-row").click(); + await page.getByTestId("account-menu").getByRole("button", { name: "Settings" }).click(); + await page.getByRole("button", { name: "Run setup again" }).click(); + await expect(page.getByTestId("ob-step-model")).toBeVisible(); +} + +test("macOS: a drag region is reachable while onboarding covers the screen", async ({ page }) => { + await page.addInitScript(() => { + (window as any).__TAURI__ = {}; + (window as any).__OCW_PLATFORM__ = "macos"; + }); + await openOnboarding(page); + + // The overlay title bar is macOS-only; confirm it actually engaged for this run. + await expect(page.locator(".app.tauri-overlay").first()).toBeVisible(); + + const dragRegions = page.locator("[data-tauri-drag-region]"); + await expect(dragRegions.first()).toBeVisible(); + + // The mechanism Tauri relies on: the element the OS would hit-test at the drag + // region's own coordinates must BE that element, not something stacked on top of it. + const reachable = await dragRegions.first().evaluate((el) => { + const r = el.getBoundingClientRect(); + const x = r.left + Math.min(10, r.width / 2); + const y = r.top + r.height / 2; + return document.elementFromPoint(x, y) === el; + }); + expect(reachable).toBe(true); +}); diff --git a/surfaces/gui/src/components/Onboarding.tsx b/surfaces/gui/src/components/Onboarding.tsx index 5cfe88a2..458dfcb7 100644 --- a/surfaces/gui/src/components/Onboarding.tsx +++ b/surfaces/gui/src/components/Onboarding.tsx @@ -10,7 +10,9 @@ import { } from "../api"; import { ConnectorBadge } from "../connectors/ConnectorIcon"; import { ProviderCards, ProviderForm, useProviderSetup } from "../providers/ProviderSetup"; +import { isTauri, platformOS } from "../tauri"; import { Spinner } from "./AutomationQuickstart"; +import { Icon } from "./Icon"; // First-run onboarding (UX-DECISIONS §24 → §29 → §39): model → your tools → go. // §39 (owner design, 2026-07-18): step 1 is a PROVIDER GALLERY — 13 real brand @@ -109,8 +111,22 @@ export function Onboarding({ onDone }: { onDone: (next?: "work" | "gallery" | "a ); + // macOS overlay title bar (hidden native title bar, App.tsx's `overlay`): the app's only + // drag surfaces (Sidebar's brand row, App.tsx's topbar) sit UNDER this full-viewport + // backdrop and can't be reached, same gap the boot splash already covers (App.tsx + // ~line 1130) before the sidebar even mounts. Mirror that same strip here, above the + // backdrop's z-50 (z-180), so the window stays movable while onboarding is on screen. + const overlay = isTauri() && platformOS() === "macos"; + return (
+ {overlay && ( +
+ + OpenWorkerBETA + +
+ )} {/* FIXED height across all three steps (owner call 2026-07-12, reaffirmed §39: the modal must never resize — the gallery⇄form swap happens inside this box). */}