fix(desktop): remove titlebar inset in fullscreen

The macOS traffic-light padding was keyed only by platform, so it remained after Electron hid the controls in fullscreen.
This commit is contained in:
LukeParkerDev
2026-07-25 12:52:08 +10:00
parent 5e2a6257b2
commit 525e51dcbb
9 changed files with 50 additions and 2 deletions
@@ -0,0 +1,13 @@
import { describe, expect, test } from "bun:test"
import { macTitlebarLeftPadding } from "./titlebar-padding"
describe("macOS titlebar padding", () => {
test("reserves traffic light space while windowed", () => {
expect(macTitlebarLeftPadding(1, false)).toBe("84px")
expect(macTitlebarLeftPadding(2, false)).toBe("42px")
})
test("does not reserve traffic light space in fullscreen", () => {
expect(macTitlebarLeftPadding(1, true)).toBe("0px")
})
})
@@ -0,0 +1,6 @@
const macTrafficLightsWidth = 84
export function macTitlebarLeftPadding(zoom: number, fullscreen: boolean) {
if (fullscreen) return "0px"
return `${macTrafficLightsWidth / zoom}px`
}
+3 -2
View File
@@ -29,6 +29,7 @@ import type { PromptSession } from "@/context/prompt"
import "./titlebar.css"
import { newTabTooltipKeybind } from "./command-tooltip-keybind"
import { normalizeSessionInfo } from "@/utils/session"
import { macTitlebarLeftPadding } from "./titlebar-padding"
type TauriDesktopWindow = {
startDragging?: () => Promise<void>
@@ -237,8 +238,8 @@ export function Titlebar(props: { update?: TitlebarUpdate; debugTools?: { visibl
}}
style={{
"min-height": minHeight(),
// Keep native macOS traffic lights clear even when the desktop window is narrow.
"padding-left": mac() ? `${84 / zoom()}px` : 0,
// Keep visible native macOS traffic lights clear even when the desktop window is narrow.
"padding-left": mac() ? macTitlebarLeftPadding(zoom(), platform.windowFullscreen?.() ?? false) : 0,
width: electronWindows() ? `env(titlebar-area-width, calc(100vw - ${windowsControlsWidth()}))` : undefined,
"max-width": electronWindows()
? `env(titlebar-area-width, calc(100vw - ${windowsControlsWidth()}))`
+3
View File
@@ -97,6 +97,9 @@ type PlatformBase = {
/** Webview zoom level (desktop only) */
webviewZoom?: Accessor<number>
/** Whether the native desktop window is fullscreen */
windowFullscreen?: Accessor<boolean>
/** Get whether native pinch/Ctrl-scroll zoom gestures are enabled (desktop only) */
getPinchZoomEnabled?(): Promise<boolean> | boolean
+5
View File
@@ -227,6 +227,11 @@ export function registerIpcHandlers(deps: Deps) {
return win?.isFocused() ?? false
})
ipcMain.handle("get-window-fullscreen", (event: IpcMainInvokeEvent) => {
const win = BrowserWindow.fromWebContents(event.sender)
return win?.isFullScreen() ?? false
})
ipcMain.handle("set-window-focus", (event: IpcMainInvokeEvent) => {
const win = BrowserWindow.fromWebContents(event.sender)
win?.focus()
+6
View File
@@ -219,6 +219,7 @@ export function createMainWindow(id: string = randomUUID()) {
state.manage(win)
registerWindow(win, id)
wireFullscreen(win)
loadWindow(win, "index.html")
wireZoom(win)
@@ -472,6 +473,11 @@ function wireZoom(win: BrowserWindow) {
})
}
function wireFullscreen(win: BrowserWindow) {
win.on("enter-full-screen", () => win.webContents.send("window-fullscreen-changed", true))
win.on("leave-full-screen", () => win.webContents.send("window-fullscreen-changed", false))
}
function clampZoom(value: number) {
return Math.min(Math.max(value, minZoomLevel), maxZoomLevel)
}
+6
View File
@@ -100,6 +100,12 @@ const api: ElectronAPI = {
readClipboardImage: () => ipcRenderer.invoke("read-clipboard-image"),
showNotification: (title, body) => ipcRenderer.send("show-notification", title, body),
getWindowFocused: () => ipcRenderer.invoke("get-window-focused"),
getWindowFullscreen: () => ipcRenderer.invoke("get-window-fullscreen"),
onWindowFullscreenChanged: (cb) => {
const handler = (_: unknown, fullscreen: boolean) => cb(fullscreen)
ipcRenderer.on("window-fullscreen-changed", handler)
return () => ipcRenderer.removeListener("window-fullscreen-changed", handler)
},
setWindowFocus: () => ipcRenderer.invoke("set-window-focus"),
showWindow: () => ipcRenderer.invoke("show-window"),
relaunch: () => ipcRenderer.send("relaunch"),
+2
View File
@@ -91,6 +91,8 @@ export type ElectronAPI = {
readClipboardImage: () => Promise<{ buffer: ArrayBuffer; width: number; height: number } | null>
showNotification: (title: string, body?: string) => void
getWindowFocused: () => Promise<boolean>
getWindowFullscreen: () => Promise<boolean>
onWindowFullscreenChanged: (cb: (fullscreen: boolean) => void) => () => void
setWindowFocus: () => Promise<void>
showWindow: () => Promise<void>
relaunch: () => void
+6
View File
@@ -30,6 +30,10 @@ import "./styles.css"
import { Splash } from "@opencode-ai/ui/logo"
import { useTheme } from "@opencode-ai/ui/theme/context"
const [windowFullscreen, setWindowFullscreen] = createSignal(false)
window.api.onWindowFullscreenChanged(setWindowFullscreen)
void window.api.getWindowFullscreen().then(setWindowFullscreen)
const root = document.getElementById("root")
if (import.meta.env.DEV && !(root instanceof HTMLElement)) {
throw new Error(t("error.dev.rootNotFound"))
@@ -294,6 +298,8 @@ const createPlatform = (windowState: DesktopWindowState): Platform => {
webviewZoom,
windowFullscreen,
getPinchZoomEnabled: () => window.api.getPinchZoomEnabled(),
setPinchZoomEnabled,