From 383e579eec5076d9cc4aa33eb54211520b2f82fd Mon Sep 17 00:00:00 2001
From: Linus Rath <139418639+rathlinus@users.noreply.github.com>
Date: Tue, 3 Nov 2026 16:52:42 +0100
Subject: [PATCH] feat: run the app to the top of the window, with the window's
controls in its own toolbars
The desktop app and an installed PWA had a row across the whole window
with the title and the window controls. That row is gone. The rail,
sidebar, list and reader run to the top of the window in their own
colours, and the controls sit in the top row of whatever is rightmost.
- `lib/window-controls-overlay.ts` finds and marks those rows before
paint, and they reserve the controls' width through
`--titlebar-right-inset`
- the rows and the rail are the drag area, everything clickable is
`no-drag`, and an invisible band lets a page with no row still move
- settings and the admin shell get a row only under the overlay, Pro's
tab strip and Mountain View's bar match the controls' height
- Alt floats the menu over the top left, in `window-menu-bar.tsx`
- skeletons carry the same marks, so nothing shifts when the app loads
---
app/(main)/[locale]/layout.tsx | 2 +
app/(main)/[locale]/login/page.tsx | 4 +-
app/(main)/admin/layout.tsx | 2 +
app/(main)/layout.tsx | 4 +-
app/desktop.css | 3 +-
app/globals.css | 137 ++++++-
app/manifest.ts | 2 +-
components/admin/admin-shell.tsx | 4 +
components/boot-skeleton/admin-skeleton.tsx | 3 +
.../boot-skeleton/calendar-skeleton.tsx | 2 +-
components/boot-skeleton/files-skeleton.tsx | 2 +-
components/boot-skeleton/login-skeleton.tsx | 2 +-
components/boot-skeleton/mail-skeleton.tsx | 2 +-
components/boot-skeleton/pro-skeleton.tsx | 2 +
.../boot-skeleton/settings-skeleton.tsx | 7 +-
components/calendar/calendar-toolbar.tsx | 2 +-
components/calendar/event-modal.tsx | 2 +-
components/calendar/task-modal.tsx | 2 +-
components/contacts/contact-detail.tsx | 6 +-
components/contacts/contact-form.tsx | 2 +-
components/contacts/contact-group-detail.tsx | 2 +-
components/contacts/contact-group-form.tsx | 2 +-
components/email/email-composer.tsx | 1 +
components/email/email-viewer.tsx | 4 +-
components/files/preview-shell.tsx | 1 +
components/layout/inline-app-view.tsx | 2 +-
components/layout/navigation-rail.tsx | 1 +
components/layout/window-controls-host.tsx | 29 ++
components/layout/window-menu-bar.tsx | 100 ++++++
components/layout/window-title-bar.tsx | 138 -------
components/plugins/app-side-panel.tsx | 1 +
components/plugins/plugin-side-panels.tsx | 2 +
components/pro/pro-tab-bar.tsx | 1 +
components/settings/settings-app.tsx | 4 +-
components/ui/dialog.tsx | 4 +-
integration/tests/23-do-pwa-windows.spec.ts | 38 +-
lib/__tests__/window-controls-host.test.tsx | 340 ++++++++++++++++++
lib/__tests__/window-title-bar.test.tsx | 221 ------------
lib/desktop/boot.ts | 3 +-
lib/window-controls-overlay.ts | 126 ++++++-
locales/ar/common.json | 3 +-
locales/ca/common.json | 3 +-
locales/cs/common.json | 3 +-
locales/da/common.json | 3 +-
locales/de/common.json | 3 +-
locales/en/common.json | 3 +-
locales/es/common.json | 3 +-
locales/et/common.json | 3 +-
locales/fa/common.json | 3 +-
locales/fr/common.json | 3 +-
locales/he/common.json | 3 +-
locales/hu/common.json | 3 +-
locales/it/common.json | 3 +-
locales/ja/common.json | 3 +-
locales/ko/common.json | 3 +-
locales/lv/common.json | 3 +-
locales/mn/common.json | 3 +-
locales/nb/common.json | 3 +-
locales/nl/common.json | 3 +-
locales/pl/common.json | 3 +-
locales/pt/common.json | 3 +-
locales/ro/common.json | 3 +-
locales/ru/common.json | 3 +-
locales/sk/common.json | 3 +-
locales/th/common.json | 3 +-
locales/tr/common.json | 3 +-
locales/uk/common.json | 3 +-
locales/vi/common.json | 3 +-
locales/zh-TW/common.json | 3 +-
locales/zh/common.json | 3 +-
70 files changed, 843 insertions(+), 459 deletions(-)
create mode 100644 components/layout/window-controls-host.tsx
create mode 100644 components/layout/window-menu-bar.tsx
delete mode 100644 components/layout/window-title-bar.tsx
create mode 100644 lib/__tests__/window-controls-host.test.tsx
delete mode 100644 lib/__tests__/window-title-bar.test.tsx
diff --git a/app/(main)/[locale]/layout.tsx b/app/(main)/[locale]/layout.tsx
index 762f30ab9..3c03bd9be 100644
--- a/app/(main)/[locale]/layout.tsx
+++ b/app/(main)/[locale]/layout.tsx
@@ -38,6 +38,7 @@ import { MainWindowOnly } from "@/components/popout/main-window-only";
import { PopoutHost } from "@/components/popout/popout-host";
import { WindowSyncHost } from "@/components/popout/window-sync-host";
import { DesktopHost } from "@/components/desktop/desktop-host";
+import { WindowMenuBar } from "@/components/layout/window-menu-bar";
import { IS_LITE } from "@/lib/lite";
import { setRequestLocale } from "next-intl/server";
import { locales } from "@/i18n/routing";
@@ -90,6 +91,7 @@ export default async function LocaleLayout({
{!IS_LITE && }
{!IS_LITE && }
+ {!IS_LITE && }
{!IS_LITE && }
diff --git a/app/(main)/[locale]/login/page.tsx b/app/(main)/[locale]/login/page.tsx
index bf9b69c44..52d6f4be0 100644
--- a/app/(main)/[locale]/login/page.tsx
+++ b/app/(main)/[locale]/login/page.tsx
@@ -1208,7 +1208,7 @@ function LoginPageContent() {
return (
{/* Theme toggle, hidden while the theme policy locks the mode (#1280) */}
-
+
{/* Theme toggle - top right, dropdown style; hidden while the theme policy locks the mode (#1280) */}
-
+