fix(ui): stop sidebar session sections from painting over each other in short windows (#104447)

This commit is contained in:
Peter Steinberger
2026-07-11 04:57:15 -07:00
committed by GitHub
parent 6c415c44c9
commit 9523a52871
2 changed files with 87 additions and 3 deletions
+74
View File
@@ -1084,6 +1084,80 @@ describeControlUiE2e("Control UI session management mocked Gateway E2E", () => {
}
});
it("scrolls long session lists in short windows instead of squeezing sections", async () => {
const baseTime = Date.parse("2026-07-01T16:00:00.000Z");
const rows = [
...Array.from({ length: 8 }, (_, index) =>
sessionRow(`agent:main:work-${index}`, `Work session ${index}`, baseTime - index * 60_000, {
worktree: { branch: `openclaw/wt-${index}`, repoRoot: "/Users/dev/Projects/clawdbot" },
}),
),
...Array.from({ length: 30 }, (_, index) =>
sessionRow(`agent:main:chat-${index}`, `Chat ${index}`, baseTime - (index + 10) * 60_000),
),
];
const context = await browser.newContext({
locale: "en-US",
serviceWorkers: "block",
viewport: { height: 620, width: 1280 },
});
const page = await context.newPage();
await installMockGateway(page, {
methodResponses: {
"sessions.list": sessionsListResponse(rows),
},
sessionKey: "agent:main:main",
});
try {
await page.goto(`${server.baseUrl}chat`);
await expect
.poll(() => page.locator(".sidebar-recent-session").count(), { timeout: 15_000 })
.toBeGreaterThanOrEqual(rows.length);
await captureUiProof(page, "short-window-session-sections.png");
// Sections must stack below each other, not paint over the rows above.
const overlaps = await page.evaluate(() => {
const rects = [
...document.querySelectorAll(".sidebar-recent-session, .sidebar-recent-sessions__head"),
]
.map((element) => {
const rect = element.getBoundingClientRect();
return { top: rect.top, bottom: rect.bottom };
})
.filter((rect) => rect.bottom > rect.top)
.toSorted((a, b) => a.top - b.top);
let bad = 0;
for (let index = 1; index < rects.length; index += 1) {
if (rects[index].top < rects[index - 1].bottom - 2) {
bad += 1;
}
}
return bad;
});
expect(overlaps).toBe(0);
// The squeeze regression compressed sections into the viewport with no
// overflow; a healthy list is taller than its container and scrolls.
const scroll = await page.evaluate(() => {
const list = document.querySelector(".sidebar-recent-sessions");
if (!list) {
return null;
}
list.scrollTop = list.scrollHeight;
return {
clientHeight: list.clientHeight,
scrollHeight: list.scrollHeight,
scrollTop: list.scrollTop,
};
});
expect(scroll).not.toBeNull();
expect(scroll?.scrollHeight ?? 0).toBeGreaterThan(scroll?.clientHeight ?? 0);
expect(scroll?.scrollTop ?? 0).toBeGreaterThan(0);
} finally {
await context.close();
}
});
it("keeps sidebar session controls reachable on touch pointers", async () => {
const context = await browser.newContext({
hasTouch: true,
+13 -3
View File
@@ -695,6 +695,9 @@ html.openclaw-native-nav .shell-nav-expand:focus-visible {
}
.sidebar {
/* Sticky headers inside the sidebar reuse --sidebar-bg so scrolled rows
never shine through with a mismatched tone. */
--sidebar-bg: color-mix(in srgb, var(--bg) 96%, var(--bg-elevated) 4%);
position: relative;
display: flex;
flex-direction: column;
@@ -702,11 +705,11 @@ html.openclaw-native-nav .shell-nav-expand:focus-visible {
min-height: 0;
min-width: 0;
overflow: hidden;
background: color-mix(in srgb, var(--bg) 96%, var(--bg-elevated) 4%);
background: var(--sidebar-bg);
}
:root[data-theme-mode="light"] .sidebar {
background: color-mix(in srgb, var(--panel) 98%, white 2%);
--sidebar-bg: color-mix(in srgb, var(--panel) 98%, white 2%);
}
.sidebar-shell {
@@ -990,6 +993,13 @@ html.openclaw-native-nav .shell-nav-expand:focus-visible {
cursor: default;
}
/* The scroller's children must not flex-shrink: shrink squeezes each section
to its min-height while the fixed-height rows inside overflow and paint
over the following section. Fixed sizing makes the list scroll instead. */
.sidebar-recent-sessions > * {
flex: 0 0 auto;
}
.sidebar-recent-sessions__group {
display: flex;
flex-direction: column;
@@ -1039,7 +1049,7 @@ html.openclaw-native-nav .shell-nav-expand:focus-visible {
position: sticky;
top: 0;
z-index: 2;
background: var(--bg);
background: var(--sidebar-bg);
}
.sidebar-recent-sessions__head .sidebar-recent-sessions__label-text {