Gate the titlebar inset on a windowChrome capability instead of osType (#570)

This commit is contained in:
Gregory Schier
2026-08-16 22:21:38 -07:00
committed by GitHub
parent bea58b16b4
commit e99f6d2bc7
5 changed files with 24 additions and 5 deletions
+1
View File
@@ -58,6 +58,7 @@ const ALL_CAPABILITIES: PlatformCapabilities = {
localFiles: true, localFiles: true,
timeline: true, timeline: true,
multiWindow: true, multiWindow: true,
windowChrome: true,
plugins: true, plugins: true,
encryption: true, encryption: true,
updater: true, updater: true,
+7
View File
@@ -255,6 +255,13 @@ export interface PlatformCapabilities {
timeline: boolean; timeline: boolean;
/** More than one window or tab on the same data. */ /** More than one window or tab on the same data. */
multiWindow: boolean; multiWindow: boolean;
/**
* The page is the window's titlebar: it draws the drag region and window
* controls, and leaves room for macOS traffic lights. False when something
* else owns the frame around the page (a browser tab), so none of that
* chrome should be reserved or drawn.
*/
windowChrome: boolean;
/** The plugin runtime. */ /** The plugin runtime. */
plugins: boolean; plugins: boolean;
/** Workspace encryption backed by a key the host keeps. */ /** Workspace encryption backed by a key the host keeps. */
+4 -1
View File
@@ -129,13 +129,16 @@ Reported honestly, so callers gate on the question rather than on the host:
| True | False | | True | False |
| --- | --- | | --- | --- |
| `cookieJar` (the jar stores and edits here; only filling it needs the sender) | `grpc`, `websocket`, `git`, `sync`, `tlsOptions`, `localFiles`, `timeline`, `multiWindow`, `plugins`, `encryption`, `updater`, `clipboardRead`, `systemFonts`, `license` | | `cookieJar` (the jar stores and edits here; only filling it needs the sender) | `grpc`, `websocket`, `git`, `sync`, `tlsOptions`, `localFiles`, `timeline`, `multiWindow`, `windowChrome`, `plugins`, `encryption`, `updater`, `clipboardRead`, `systemFonts`, `license` |
`multiWindow: false` means the host cannot open a *second window* on demand — `multiWindow: false` means the host cannot open a *second window* on demand —
what `cmd_new_child_window` does for Settings and workspace switching. It is not what `cmd_new_child_window` does for Settings and workspace switching. It is not
a claim that nothing else is looking: other tabs may well be open on the same a claim that nothing else is looking: other tabs may well be open on the same
worker, and it pushes every write to all of them regardless. worker, and it pushes every write to all of them regardless.
`windowChrome: false` means the browser owns the frame around the page, so the
header draws no window controls and reserves no room for macOS traffic lights.
## Multiple tabs ## Multiple tabs
Each tab mints a label at load (`tab_xxxxxxxx`) and sends it with every command; Each tab mints a label at load (`tab_xxxxxxxx`) and sends it with every command;
+3
View File
@@ -49,6 +49,9 @@ function capabilitiesFor(): PlatformCapabilities {
// else is looking: other tabs may well be open on the same worker, and it // else is looking: other tabs may well be open on the same worker, and it
// pushes every write to all of them regardless of this flag. // pushes every write to all of them regardless of this flag.
multiWindow: false, multiWindow: false,
// The browser draws the frame around the page. There are no traffic lights
// to leave room for and no window controls to draw.
windowChrome: false,
plugins: false, plugins: false,
encryption: false, encryption: false,
updater: false, updater: false,
+9 -4
View File
@@ -1,3 +1,4 @@
import { useCapability } from "@yaakapp-internal/platform";
import classNames from "classnames"; import classNames from "classnames";
import type { CSSProperties, HTMLAttributes, ReactNode } from "react"; import type { CSSProperties, HTMLAttributes, ReactNode } from "react";
import { useMemo } from "react"; import { useMemo } from "react";
@@ -31,6 +32,10 @@ export function HeaderSize({
interfaceScale, interfaceScale,
}: HeaderSizeProps) { }: HeaderSizeProps) {
const isFullscreen = useIsFullscreen(); const isFullscreen = useIsFullscreen();
// The header only doubles as the titlebar when the host hands the page the
// window frame (Tauri) and the user hasn't opted for the native one. In a
// browser tab the frame is the browser's: no controls, no traffic lights.
const drawsWindowChrome = useCapability("windowChrome") && !useNativeTitlebar;
const finalStyle = useMemo<CSSProperties>(() => { const finalStyle = useMemo<CSSProperties>(() => {
const s = { ...style }; const s = { ...style };
@@ -38,8 +43,8 @@ export function HeaderSize({
if (size === "md") s.minHeight = HEADER_SIZE_MD; if (size === "md") s.minHeight = HEADER_SIZE_MD;
if (size === "lg") s.minHeight = HEADER_SIZE_LG; if (size === "lg") s.minHeight = HEADER_SIZE_LG;
if (useNativeTitlebar) { if (!drawsWindowChrome) {
// No style updates when using native titlebar // No style updates when something else draws the titlebar
} else if (osType === "macos") { } else if (osType === "macos") {
if (!isFullscreen) { if (!isFullscreen) {
// Add large padding for window controls // Add large padding for window controls
@@ -57,7 +62,7 @@ export function HeaderSize({
interfaceScale, interfaceScale,
size, size,
style, style,
useNativeTitlebar, drawsWindowChrome,
osType, osType,
]); ]);
@@ -82,7 +87,7 @@ export function HeaderSize({
> >
{children} {children}
</div> </div>
{!hideControls && !useNativeTitlebar && ( {!hideControls && drawsWindowChrome && (
<WindowControls <WindowControls
onlyX={onlyXWindowControl} onlyX={onlyXWindowControl}
osType={osType} osType={osType}