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:
@@ -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`
|
||||
}
|
||||
@@ -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()}))`
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
|
||||
@@ -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"),
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user