Compare commits

...
Author SHA1 Message Date
Brendonovich 82752e2b9f fix(app): keep cramped tab close button trailing 2026-09-17 02:25:03 +00:00
3 changed files with 65 additions and 25 deletions
@@ -106,6 +106,64 @@ test("keyboard navigation follows the visible tab order", async ({ page }) => {
await expect(page).toHaveURL(new RegExp(`${hrefC.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`))
})
test("cramped active tab keeps normal content and a trailing close button", async ({ page }) => {
await page.setViewportSize({ width: 900, height: 720 })
await mockServer(page)
await page.addInitScript(
({ server, sessionA, directory }) => {
localStorage.setItem(
"opencode.window.browser.dat:tabs",
JSON.stringify([
{ type: "session", server, sessionId: sessionA },
...Array.from({ length: 12 }, (_, index) => ({
type: "draft",
server,
directory,
draftID: `draft_cramped_${index}`,
})),
]),
)
},
{ server, sessionA: sessionA.id, directory: sessionA.directory },
)
const href = `/server/${base64Encode(server)}/session/${sessionA.id}`
await page.goto(href)
const slot = page.locator(`[data-titlebar-tab-slot]:has(a[href="${href}"])`)
const tab = slot.locator("[data-titlebar-tab]")
const link = slot.locator("[data-titlebar-tab-link]")
const avatar = slot.locator('[data-slot="project-avatar-slot"]')
const title = slot.locator("[data-titlebar-tab-title]")
const close = slot.getByRole("button", { name: "Close tab", exact: true })
await expect(slot).toHaveAttribute("data-active", "true")
await expect(link).toHaveCSS("mask-image", "none")
await expect(avatar).toBeVisible()
await expect(title).toBeVisible()
await expect(close).toBeVisible()
await expect.poll(async () => (await tab.boundingBox())?.width).toBe(54)
await expect
.poll(async () => {
const tabBox = await tab.boundingBox()
const avatarBox = await avatar.boundingBox()
if (!tabBox || !avatarBox) return null
return Math.round(avatarBox.x - tabBox.x)
})
.toBe(6)
await expect
.poll(async () => {
const tabBox = await tab.boundingBox()
const avatarBox = await avatar.boundingBox()
const closeBox = await close.boundingBox()
if (!tabBox || !avatarBox || !closeBox) return null
return {
iconGap: Math.round(closeBox.x - avatarBox.x - avatarBox.width),
trailingGap: Math.round(tabBox.x + tabBox.width - closeBox.x - closeBox.width),
}
})
.toEqual({ iconGap: 6, trailingGap: 6 })
})
test("mobile drawer exposes close controls and navigates between tabs", async ({ page }) => {
await page.setViewportSize({ width: 360, height: 720 })
await mockServer(page)
+5 -23
View File
@@ -219,34 +219,16 @@
}
@container (max-width: 64px) {
[data-titlebar-tab]:not([data-orientation="vertical"]) [data-titlebar-tab-link] {
justify-content: center;
gap: 0;
padding-inline: 0;
[data-titlebar-tab]:not([data-orientation="vertical"])[data-active="true"] [data-slot="tab-link"] {
-webkit-mask-image: none;
mask-image: none;
}
@media (hover: none) {
[data-titlebar-tab]:not([data-orientation="vertical"]):not([data-editing="true"]) [data-slot="tab-link"],
[data-titlebar-tab][data-title-overflow="true"]:not([data-orientation="vertical"]):not([data-editing="true"]):dir(
rtl
)
[data-slot="tab-link"] {
-webkit-mask-image: none;
mask-image: none;
}
}
[data-titlebar-tab]:not([data-orientation="vertical"]) [data-titlebar-tab-title] {
display: none;
[data-titlebar-tab]:not([data-orientation="vertical"])[data-active="true"] [data-slot="tab-close"] {
inset-inline-end: 6px;
}
[data-titlebar-tab]:not([data-orientation="vertical"]):not([data-active="true"]) [data-slot="tab-close"] {
display: none;
}
[data-titlebar-tab]:not([data-orientation="vertical"]) [data-slot="tab-close"] {
right: auto;
left: 50%;
transform: translateX(-50%);
}
}
@@ -52,7 +52,7 @@ function SessionTabSlot(props: {
data-orientation={props.orientation}
class="relative flex"
classList={{
"w-56 min-w-7 max-w-56 flex-shrink": props.orientation === "horizontal",
"w-56 min-w-[54px] max-w-56 flex-shrink": props.orientation === "horizontal",
"w-full shrink-0": props.orientation === "vertical",
}}
>
@@ -211,7 +211,7 @@ function DraftTabSlot(props: {
data-orientation={props.orientation}
class="relative flex"
classList={{
"w-56 min-w-7 max-w-56 flex-shrink": props.orientation === "horizontal",
"w-56 min-w-[54px] max-w-56 flex-shrink": props.orientation === "horizontal",
"w-full shrink-0": props.orientation === "vertical",
}}
>