diff --git a/src/renderer/src/app-shell/TitlebarLeftControls.tsx b/src/renderer/src/app-shell/TitlebarLeftControls.tsx
index 352e3d121fba..1ef2affdaae8 100644
--- a/src/renderer/src/app-shell/TitlebarLeftControls.tsx
+++ b/src/renderer/src/app-shell/TitlebarLeftControls.tsx
@@ -15,6 +15,7 @@ import {
} from '@/store/slices/worktree-nav-history'
import { useShortcutLabel } from '../hooks/useShortcutLabel'
import { useAppStore } from '../store'
+import { macTrafficLightChromeForWindow } from '@/lib/desktop-window-chrome'
import { hasCustomTitleBar, isMac } from './app-window-chrome'
import type { AppChromeLayout } from './use-app-chrome-layout'
@@ -42,7 +43,10 @@ export function TitlebarLeftControls({ layout }: { layout: AppChromeLayout }): R
}`}
>
- {isMac && !layout.isFullScreen ? (
+ {macTrafficLightChromeForWindow({
+ isMac,
+ isFullScreen: layout.isFullScreen
+ }).showPad ? (
) : hasCustomTitleBar ? (
/* Why: Windows/Linux remove the native title bar, so render the logo plus a ยทยทยท button that pops the application menu (as Alt does). */
diff --git a/src/renderer/src/app-shell/app-window-chrome.ts b/src/renderer/src/app-shell/app-window-chrome.ts
index 8305650c4d03..23c4faa3e97c 100644
--- a/src/renderer/src/app-shell/app-window-chrome.ts
+++ b/src/renderer/src/app-shell/app-window-chrome.ts
@@ -1,5 +1,6 @@
import {
isPairedWebClientWindow,
+ macTrafficLightChromeForWindow,
shouldRenderDesktopWindowChrome
} from '@/lib/desktop-window-chrome'
@@ -19,4 +20,8 @@ export const WINDOW_CONTROLS_HEIGHT = hasCustomTitleBar ? '36px' : '0px'
// Why: macOS paints traffic lights on the window's top-left edge. Windows and Linux paint their
// controls on the right, so only macOS needs a surface to keep the left edge uncovered.
-export const MAC_TRAFFIC_LIGHTS_WIDTH = isMac ? '80px' : '0px'
+// A paired web client on a Mac has no traffic lights (#19299).
+export const MAC_TRAFFIC_LIGHTS_WIDTH = macTrafficLightChromeForWindow({
+ isMac,
+ isFullScreen: false
+}).width
diff --git a/src/renderer/src/lib/desktop-window-chrome.test.ts b/src/renderer/src/lib/desktop-window-chrome.test.ts
index a4bde1495ddd..afe25ade04ab 100644
--- a/src/renderer/src/lib/desktop-window-chrome.test.ts
+++ b/src/renderer/src/lib/desktop-window-chrome.test.ts
@@ -1,5 +1,14 @@
-import { describe, expect, it } from 'vitest'
-import { shouldRenderDesktopWindowChrome } from './desktop-window-chrome'
+import { afterEach, describe, expect, it, vi } from 'vitest'
+import {
+ macTrafficLightChromeForWindow,
+ macTrafficLightsWidth,
+ shouldRenderDesktopWindowChrome,
+ shouldShowMacTrafficLightPad
+} from './desktop-window-chrome'
+
+afterEach(() => {
+ vi.unstubAllGlobals()
+})
describe('shouldRenderDesktopWindowChrome', () => {
it('renders custom chrome for frameless desktop Linux and Windows windows', () => {
@@ -16,3 +25,46 @@ describe('shouldRenderDesktopWindowChrome', () => {
expect(shouldRenderDesktopWindowChrome({ platform: 'win32', isWebClient: true })).toBe(false)
})
})
+
+describe('mac traffic-light inset', () => {
+ it('reserves the inset on desktop macOS, including while fullscreen hides the pad', () => {
+ expect(macTrafficLightsWidth({ isMac: true, isWebClient: false })).toBe('80px')
+ expect(
+ shouldShowMacTrafficLightPad({ isMac: true, isWebClient: false, isFullScreen: false })
+ ).toBe(true)
+ expect(
+ shouldShowMacTrafficLightPad({ isMac: true, isWebClient: false, isFullScreen: true })
+ ).toBe(false)
+ })
+
+ it('does not reserve a traffic-light inset in the paired web client on a Mac', () => {
+ expect(macTrafficLightsWidth({ isMac: true, isWebClient: true })).toBe('0px')
+ expect(
+ shouldShowMacTrafficLightPad({ isMac: true, isWebClient: true, isFullScreen: false })
+ ).toBe(false)
+ expect(
+ shouldShowMacTrafficLightPad({ isMac: true, isWebClient: true, isFullScreen: true })
+ ).toBe(false)
+ })
+
+ it('reads the paired web flag for both the inset width and the pad', () => {
+ vi.stubGlobal('__ORCA_WEB_CLIENT__', true)
+ expect(macTrafficLightChromeForWindow({ isMac: true, isFullScreen: false })).toEqual({
+ width: '0px',
+ showPad: false
+ })
+
+ vi.stubGlobal('__ORCA_WEB_CLIENT__', false)
+ expect(macTrafficLightChromeForWindow({ isMac: true, isFullScreen: false })).toEqual({
+ width: '80px',
+ showPad: true
+ })
+ })
+
+ it('does not reserve a traffic-light inset on Windows or Linux', () => {
+ expect(macTrafficLightsWidth({ isMac: false, isWebClient: false })).toBe('0px')
+ expect(
+ shouldShowMacTrafficLightPad({ isMac: false, isWebClient: false, isFullScreen: false })
+ ).toBe(false)
+ })
+})
diff --git a/src/renderer/src/lib/desktop-window-chrome.ts b/src/renderer/src/lib/desktop-window-chrome.ts
index 59683f155185..affa660c1378 100644
--- a/src/renderer/src/lib/desktop-window-chrome.ts
+++ b/src/renderer/src/lib/desktop-window-chrome.ts
@@ -21,3 +21,39 @@ export function shouldRenderDesktopWindowChrome({
}: DesktopWindowChromeInput): boolean {
return !isWebClient && (platform === 'win32' || platform === 'linux')
}
+
+/** Desktop macOS keeps the left inset for native traffic lights. A paired browser has none. */
+export function macTrafficLightsWidth(input: {
+ isMac: boolean
+ isWebClient: boolean
+}): '80px' | '0px' {
+ return input.isMac && !input.isWebClient ? '80px' : '0px'
+}
+
+/** The pad element is hidden in fullscreen; the width token above stays, matching desktop today. */
+export function shouldShowMacTrafficLightPad(input: {
+ isMac: boolean
+ isWebClient: boolean
+ isFullScreen: boolean
+}): boolean {
+ return input.isMac && !input.isWebClient && !input.isFullScreen
+}
+
+/**
+ * One read of the paired-web flag feeds both the width token and the pad.
+ * Call sites that pass `isWebClient` themselves can drift apart.
+ */
+export function macTrafficLightChromeForWindow(input: { isMac: boolean; isFullScreen: boolean }): {
+ width: '80px' | '0px'
+ showPad: boolean
+} {
+ const isWebClient = isPairedWebClientWindow()
+ return {
+ width: macTrafficLightsWidth({ isMac: input.isMac, isWebClient }),
+ showPad: shouldShowMacTrafficLightPad({
+ isMac: input.isMac,
+ isWebClient,
+ isFullScreen: input.isFullScreen
+ })
+ }
+}