diff --git a/apps/desktop/electron/main.cjs b/apps/desktop/electron/main.cjs index a20c85387b8..64a8701c4a2 100644 --- a/apps/desktop/electron/main.cjs +++ b/apps/desktop/electron/main.cjs @@ -21,6 +21,7 @@ const crypto = require('node:crypto') const fs = require('node:fs') const http = require('node:http') const https = require('node:https') +const os = require('node:os') const path = require('node:path') const { pathToFileURL } = require('node:url') const { execFileSync, spawn } = require('node:child_process') @@ -45,7 +46,10 @@ const { fetchMarketplaceThemes, searchMarketplaceThemes } = require('./vscode-ma const { buildDesktopBackendEnv, normalizeHermesHomeRoot } = require('./backend-env.cjs') const { readWindowsUserEnvVar } = require('./windows-user-env.cjs') const { readWslWindowsClipboardImage } = require('./wsl-clipboard-image.cjs') -const { nativeOverlayWidth: computeNativeOverlayWidth } = require('./titlebar-overlay-width.cjs') +const { + nativeOverlayWidth: computeNativeOverlayWidth, + macTitleBarOverlayHeight +} = require('./titlebar-overlay-width.cjs') const { readDirForIpc } = require('./fs-read-dir.cjs') const { readLiveUpdateMarker } = require('./update-marker.cjs') const { @@ -160,6 +164,9 @@ const IS_PACKAGED = app.isPackaged const IS_MAC = process.platform === 'darwin' const IS_WINDOWS = process.platform === 'win32' const IS_WSL = isWslEnvironment() +// Truthful macOS kernel major (Tahoe = 25). Product version lies (16 vs 26) per +// build SDK, so gate Tahoe workarounds on Darwin instead. +const DARWIN_MAJOR = IS_MAC ? Number.parseInt(os.release(), 10) || 0 : 0 const APP_ROOT = app.getAppPath() function hiddenWindowsChildOptions(options = {}) { @@ -532,7 +539,10 @@ const TITLEBAR_OVERLAY_COLOR = 'rgba(1, 0, 0, 0)' function getTitleBarOverlayOptions() { if (IS_MAC) { - return { height: TITLEBAR_HEIGHT } + // Tahoe (Darwin 25+) misplaces the traffic lights when the overlay has a + // nonzero height (electron#49183); 0 there keeps them at the configured + // inset. See macTitleBarOverlayHeight. + return { height: macTitleBarOverlayHeight({ darwinMajor: DARWIN_MAJOR, titlebarHeight: TITLEBAR_HEIGHT }) } } // WSLg paints WCO via the RDP host's own min/max/close, so requesting diff --git a/apps/desktop/electron/titlebar-overlay-width.cjs b/apps/desktop/electron/titlebar-overlay-width.cjs index 7c1e4ca09f0..9d7a4933497 100644 --- a/apps/desktop/electron/titlebar-overlay-width.cjs +++ b/apps/desktop/electron/titlebar-overlay-width.cjs @@ -21,4 +21,23 @@ function nativeOverlayWidth({ isWindows = false, isWsl = false, isMac = false } return OVERLAY_FALLBACK_WIDTH } -module.exports = { OVERLAY_FALLBACK_WIDTH, nativeOverlayWidth } +// macOS Tahoe ships as Darwin 25 (Sequoia is 24); the Darwin number is truthful, +// unlike the product version which macOS reports as 16 or 26 depending on the +// build SDK. +const MACOS_TAHOE_DARWIN_MAJOR = 25 + +/** + * Height (px) to pass to `titleBarOverlay` on macOS. Tahoe (Darwin 25+) + * miscalculates the native traffic-light position when the overlay carries a + * nonzero height (electron#49183), shoving the lights into the left titlebar + * tools. Return 0 there so `setWindowButtonPosition` lands them at the configured + * inset; the renderer paints its own drag strips, so nothing is lost. Pre-Tahoe + * keeps the full titlebar height, byte-identical. + * + * @param {{ darwinMajor?: number, titlebarHeight?: number }} opts + */ +function macTitleBarOverlayHeight({ darwinMajor = 0, titlebarHeight = 0 } = {}) { + return darwinMajor >= MACOS_TAHOE_DARWIN_MAJOR ? 0 : titlebarHeight +} + +module.exports = { MACOS_TAHOE_DARWIN_MAJOR, OVERLAY_FALLBACK_WIDTH, macTitleBarOverlayHeight, nativeOverlayWidth } diff --git a/apps/desktop/electron/titlebar-overlay-width.test.cjs b/apps/desktop/electron/titlebar-overlay-width.test.cjs index b4d58c44b50..ccec1015b4f 100644 --- a/apps/desktop/electron/titlebar-overlay-width.test.cjs +++ b/apps/desktop/electron/titlebar-overlay-width.test.cjs @@ -1,7 +1,12 @@ const assert = require('node:assert/strict') const test = require('node:test') -const { OVERLAY_FALLBACK_WIDTH, nativeOverlayWidth } = require('./titlebar-overlay-width.cjs') +const { + MACOS_TAHOE_DARWIN_MAJOR, + OVERLAY_FALLBACK_WIDTH, + macTitleBarOverlayHeight, + nativeOverlayWidth +} = require('./titlebar-overlay-width.cjs') // This static reservation is only the pre-layout FALLBACK. Once laid out the // renderer reads the exact width from navigator.windowControlsOverlay @@ -34,3 +39,16 @@ test('macOS uses traffic lights, not a WCO overlay, so it reserves nothing', () test('the fallback width is a sane positive pixel value', () => { assert.ok(Number.isInteger(OVERLAY_FALLBACK_WIDTH) && OVERLAY_FALLBACK_WIDTH > 0) }) + +test('pre-Tahoe keeps the full titlebar overlay height', () => { + assert.equal(macTitleBarOverlayHeight({ darwinMajor: MACOS_TAHOE_DARWIN_MAJOR - 1, titlebarHeight: 34 }), 34) +}) + +test('Tahoe (Darwin 25+) drops the overlay height to 0 to avoid electron#49183', () => { + assert.equal(macTitleBarOverlayHeight({ darwinMajor: MACOS_TAHOE_DARWIN_MAJOR, titlebarHeight: 34 }), 0) + assert.equal(macTitleBarOverlayHeight({ darwinMajor: MACOS_TAHOE_DARWIN_MAJOR + 1, titlebarHeight: 34 }), 0) +}) + +test('macTitleBarOverlayHeight tolerates missing args (unknown platform → 0)', () => { + assert.equal(macTitleBarOverlayHeight(), 0) +})