fix(app): drop unverified iOS standalone titlebar offset

Keep the horizontal safe-area insets and the regression spec. Remove the 32px standalone offset: WebKit documents env(safe-area-inset-top) as the full status-bar height, and where iOS 26.x reports 0 the system already reserves the bar.
This commit is contained in:
LukeParkerDev
2026-09-20 18:53:49 +10:00
parent bef9a4d805
commit c3837bccdb
3 changed files with 11 additions and 30 deletions
@@ -4,31 +4,21 @@ import { mockOpenCodeServer } from "../utils/mock-server"
const cases = [
{
name: "mobile browser",
name: "portrait",
viewport: { width: 390, height: 844 },
insets: { top: 47, right: 0, bottom: 34, left: 0 },
standalone: false,
bottom: false,
},
{
name: "iOS standalone",
viewport: { width: 390, height: 844 },
insets: { top: 47, right: 0, bottom: 34, left: 0 },
standalone: true,
bottom: false,
},
{
name: "iOS standalone landscape",
name: "landscape",
viewport: { width: 844, height: 390 },
insets: { top: 0, right: 47, bottom: 21, left: 47 },
standalone: true,
bottom: false,
},
{
name: "iOS standalone with bottom tabs",
name: "portrait with bottom tabs",
viewport: { width: 390, height: 844 },
insets: { top: 47, right: 0, bottom: 34, left: 0 },
standalone: true,
bottom: true,
},
]
@@ -48,8 +38,7 @@ for (const input of cases) {
pageMessages: () => ({ items: [] }),
})
await page.addInitScript(
({ bottom, directory, server, sessions, standalone }) => {
Object.defineProperty(navigator, "standalone", { value: standalone })
({ bottom, directory, server, sessions }) => {
localStorage.setItem(
"settings.v3",
JSON.stringify({ general: { mobileTitlebarPosition: bottom ? "bottom" : "top" } }),
@@ -68,22 +57,20 @@ for (const input of cases) {
directory: fixture.directory,
server: fixture.serverKey,
sessions: fixture.sessions,
standalone: input.standalone,
},
)
await page.goto("/")
const titlebar = page.locator('[data-slot="titlebar-v2"]')
const top = input.insets.top + (input.standalone ? 32 : 0)
await expect(titlebar).toHaveCSS("padding-left", `${input.insets.left}px`)
await expect(titlebar).toHaveCSS("padding-right", `${input.insets.right}px`)
if (input.bottom) {
await expect(titlebar).toHaveCSS("padding-bottom", `${input.insets.bottom}px`)
await expect(page.getByRole("main")).toHaveCSS("padding-top", `${top}px`)
await expect(page.getByRole("main")).toHaveCSS("padding-top", `${input.insets.top}px`)
} else {
await expect(titlebar).toHaveCSS("padding-top", `${top}px`)
await expect(titlebar).toHaveCSS("padding-top", `${input.insets.top}px`)
}
const navigation =
+2 -7
View File
@@ -31,7 +31,6 @@ export default function Layout(props: ParentProps) {
})
const verticalTabs = () => preferences.appearance.tabLayout() === "vertical" && !mobile()
const bottomTitlebar = () => mobile() && preferences.general.mobileTitlebarPosition() === "bottom"
const iosStandalone = platform.platform === "web" && "standalone" in navigator && navigator.standalone === true
const update: TitlebarUpdate = {
get state() {
@@ -61,13 +60,9 @@ export default function Layout(props: ParentProps) {
<div
class="relative bg-v2-background-bg-deep flex-1 min-h-0 min-w-0 flex flex-col select-none [&_input]:select-text [&_textarea]:select-text [&_[contenteditable]]:select-text"
style={{
// iOS draws a native status-bar fade beyond the reported device inset in standalone mode.
"--safe-area-inset-top": iosStandalone
? "calc(env(safe-area-inset-top, 0px) + 32px)"
: "env(safe-area-inset-top, 0px)",
// Native Windows chrome supplies the gap; retain paint clearance for the panels' outer outlines.
"--shell-top-inset": bottomTitlebar()
? "max(0px, calc(8px - var(--safe-area-inset-top, env(safe-area-inset-top, 0px))))"
? "max(0px, calc(8px - env(safe-area-inset-top, 0px)))"
: platform.platform === "desktop" && platform.os === "windows"
? "1px"
: "8px",
@@ -106,7 +101,7 @@ export default function Layout(props: ParentProps) {
<main
class="flex-1 min-h-0 min-w-0 overflow-x-hidden flex flex-col items-start contain-content"
style={{
"padding-top": bottomTitlebar() ? "var(--safe-area-inset-top, env(safe-area-inset-top, 0px))" : "0px",
"padding-top": bottomTitlebar() ? "env(safe-area-inset-top, 0px)" : "0px",
"padding-bottom":
bottomTitlebar() || settings.active()
? "0px"
+3 -4
View File
@@ -158,9 +158,9 @@ export function Titlebar(props: {
platform.platform === "web"
? bottom()
? "calc(28px + max(8px, var(--safe-area-inset-bottom, env(safe-area-inset-bottom, 0px))))"
: "calc(28px + max(8px, var(--safe-area-inset-top, env(safe-area-inset-top, 0px))))"
: "calc(28px + max(8px, env(safe-area-inset-top, 0px)))"
: undefined,
"padding-top": bottom() ? "0px" : "var(--safe-area-inset-top, env(safe-area-inset-top, 0px))",
"padding-top": bottom() ? "0px" : "env(safe-area-inset-top, 0px)",
"padding-bottom": bottom() ? "var(--safe-area-inset-bottom, env(safe-area-inset-bottom, 0px))" : "0px",
"min-height": minHeight(),
// Keep native macOS traffic lights clear even when the desktop window is narrow.
@@ -446,8 +446,7 @@ export function Titlebar(props: {
<div
class="h-full flex-1 overflow-hidden flex flex-row items-center gap-1.5 px-2 md:pe-3"
classList={{
"pt-[max(0px,calc(8px-var(--safe-area-inset-top,env(safe-area-inset-top,0px))))]":
!bottom() && !windows(),
"pt-[max(0px,calc(8px-env(safe-area-inset-top,0px)))]": !bottom() && !windows(),
"pb-[max(0px,calc(8px-var(--safe-area-inset-bottom,env(safe-area-inset-bottom,0px))))]": bottom(),
"pl-4": macTrafficLights(),
// Center the 20px app icon over the sidebar's 16px icon column.