mirror of
https://github.com/anomalyco/opencode.git
synced 2026-11-13 19:46:16 +03:00
fix(app): give the collapsed sidebar toggle room next to the traffic lights (#56988)
This commit is contained in:
committed by
GitHub
Unverified
parent
3d9fec7f8c
commit
7920dfcb4b
@@ -435,6 +435,34 @@ for (const [screen, href] of [
|
||||
})
|
||||
}
|
||||
|
||||
// macOS 26 traffic lights end 74px from the window edge. The collapsed toggle starts 16px past them, as the horizontal
|
||||
// titlebar's first control does, and a fullscreen window has no traffic lights to clear.
|
||||
for (const [window, fullscreen, start] of [
|
||||
["windowed", false, 90],
|
||||
["fullscreen", true, 18],
|
||||
] as const) {
|
||||
test(`collapsed sidebar toggle clears the macOS traffic lights (${window})`, async ({ page }) => {
|
||||
await mockWorkspace(page, {
|
||||
name: "Tabs",
|
||||
sessions: [a],
|
||||
seed: { settings: { appearance: { tabLayout: "vertical", sidebarOpen: false } }, tabs: [a.id] },
|
||||
})
|
||||
await page.goto(
|
||||
`/e2e/utils/macos-window.html?${new URLSearchParams({ server: SERVER, route: sessionHref(a.id), fullscreen: String(fullscreen) })}`,
|
||||
)
|
||||
await expectSessionTitle(page, a.title)
|
||||
|
||||
const toggle = await page.locator("main").getByRole("button", { name: "Toggle sidebar", exact: true }).boundingBox()
|
||||
const project = await page.locator('[data-session-title] [data-slot="session-project-trigger"]').boundingBox()
|
||||
|
||||
if (!toggle || !project) throw new Error("collapsed sidebar header has no bounding boxes")
|
||||
expect(toggle.x).toBe(start)
|
||||
// The header's contents follow the toggle on its center line.
|
||||
expect(project.x).toBe(start + 32)
|
||||
expect(project.y + project.height / 2).toBe(toggle.y + toggle.height / 2)
|
||||
})
|
||||
}
|
||||
|
||||
test("vertical tab gaps hover and select the nearest row", async ({ page }) => {
|
||||
await mockWorkspace(page, {
|
||||
name: "Tabs",
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
import { MemoryRouter, createMemoryHistory } from "@solidjs/router"
|
||||
import { render } from "solid-js/web"
|
||||
import type { Bridge } from "@opencode/gui-extensions/sdk/bridge"
|
||||
import { AppBaseProviders, AppInterface } from "../../src/app"
|
||||
import { PlatformProvider, type Platform } from "../../src/runtime/platform/platform"
|
||||
|
||||
// The app in a macOS desktop window at `route`, so its layout leaves room for the native traffic lights. A fullscreen
|
||||
// window has none.
|
||||
export function mount(input: { server: string; route: string; fullscreen: boolean }) {
|
||||
const root = document.getElementById("root")
|
||||
|
||||
if (!root) throw new Error("Missing fixture root")
|
||||
const history = createMemoryHistory()
|
||||
history.set({ value: input.route })
|
||||
render(() => {
|
||||
const unused = async () => {
|
||||
throw new Error("Unexpected fixture action")
|
||||
}
|
||||
|
||||
// No main-process extensions run in this fixture.
|
||||
const bridge: Bridge = {
|
||||
packaged: false,
|
||||
call: unused,
|
||||
subscribe: async () => ({ available: false }),
|
||||
on: () => () => undefined,
|
||||
embed: () => undefined,
|
||||
capture: async () => undefined,
|
||||
runMenubarItem: () => undefined,
|
||||
configure: () => undefined,
|
||||
manager: {
|
||||
list: async () => [],
|
||||
enable: async () => undefined,
|
||||
disable: async () => undefined,
|
||||
reload: async () => undefined,
|
||||
install: async () => undefined,
|
||||
remove: async () => undefined,
|
||||
source: async () => "",
|
||||
asset: () => "",
|
||||
},
|
||||
}
|
||||
|
||||
const platform: Platform = {
|
||||
platform: "desktop",
|
||||
os: "macos",
|
||||
windowID: "macos-window-test",
|
||||
windowFullscreen: () => input.fullscreen,
|
||||
openExternal: () => undefined,
|
||||
openDirectoryPickerDialog: async () => null,
|
||||
notify: async () => undefined,
|
||||
restart: unused,
|
||||
extensions: bridge,
|
||||
}
|
||||
|
||||
return (
|
||||
<PlatformProvider value={platform}>
|
||||
<AppBaseProviders locale="en">
|
||||
<AppInterface
|
||||
servers={[{ type: "http", displayName: "Local Server", http: { url: input.server } }]}
|
||||
router={(props) => <MemoryRouter {...props} history={history} />}
|
||||
/>
|
||||
</AppBaseProviders>
|
||||
</PlatformProvider>
|
||||
)
|
||||
}, root)
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
</head>
|
||||
<body class="antialiased overscroll-none overflow-hidden">
|
||||
<div id="root" class="flex flex-col h-dvh"></div>
|
||||
<script type="module">
|
||||
import { mount } from "./macos-window.fixture.tsx"
|
||||
const query = new URLSearchParams(location.search)
|
||||
mount({
|
||||
server: query.get("server"),
|
||||
route: query.get("route") ?? "/",
|
||||
fullscreen: query.get("fullscreen") === "true",
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -9,8 +9,13 @@ import { useLanguage } from "@/runtime/i18n/language"
|
||||
import { usePlatform } from "@/runtime/platform/platform"
|
||||
import { useSettings } from "@/settings/model"
|
||||
|
||||
// Native controls: 14px left inset, two 20px button pitches, and a 14px button.
|
||||
export const macTrafficLightsBaseWidth = 68
|
||||
// The desktop window sets `trafficLightPosition: { x: 14, y: 14 }`. macOS 26 and later draw 14px buttons on a 23px
|
||||
// pitch, so the zoom button ends 74px from the window edge and the buttons center 21px from the top. macOS 15 and earlier
|
||||
// end at 68px and center at 22px.
|
||||
export const macTrafficLightsBaseWidth = 74
|
||||
|
||||
// Matches the horizontal titlebar, whose first control starts 16px past the traffic lights.
|
||||
const macTrafficLightsGap = 16
|
||||
|
||||
// The toggle's 28px plus a 4px gap before the header contents.
|
||||
const slotWidth = 32
|
||||
@@ -65,9 +70,11 @@ function useCollapsedSlot() {
|
||||
return {
|
||||
visible: () => desktop() && !windows() && settings.appearance.tabLayout() !== "horizontal",
|
||||
open: settings.appearance.sidebarOpen,
|
||||
// Keep the toggle clear of the traffic lights, which end 68px from the window edge.
|
||||
// Lays the toggle out as if the traffic lights were the header's first control.
|
||||
width: () =>
|
||||
mac() ? macTrafficLightsBaseWidth / (platform.webviewZoom?.() ?? 1) - contentStart + slotWidth : slotWidth,
|
||||
mac()
|
||||
? macTrafficLightsBaseWidth / (platform.webviewZoom?.() ?? 1) + macTrafficLightsGap - contentStart + slotWidth
|
||||
: slotWidth,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -43,6 +43,7 @@ export default defineConfig({
|
||||
"e2e/utils/settings-wsl.html",
|
||||
"e2e/utils/app-direction.html",
|
||||
"e2e/utils/windows-menu.html",
|
||||
"e2e/utils/macos-window.html",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user