From d3396c4d3b48bcfa0b890a59550dd08608a62e43 Mon Sep 17 00:00:00 2001 From: vyctorbrzezowski Date: Tue, 25 Aug 2026 15:39:26 +0000 Subject: [PATCH] fix(ui): bound swarm task popover --- .../chat-swarm-progress.browser.test.ts | 62 +++++++++++++++++++ ui/src/styles/chat/layout.css | 3 + 2 files changed, 65 insertions(+) create mode 100644 ui/src/pages/chat/components/chat-swarm-progress.browser.test.ts diff --git a/ui/src/pages/chat/components/chat-swarm-progress.browser.test.ts b/ui/src/pages/chat/components/chat-swarm-progress.browser.test.ts new file mode 100644 index 000000000000..3d3c86d0ff80 --- /dev/null +++ b/ui/src/pages/chat/components/chat-swarm-progress.browser.test.ts @@ -0,0 +1,62 @@ +import { chromium, type Browser } from "playwright"; +import { afterAll, beforeAll, describe, expect, it } from "vitest"; +import { readStyleSheet } from "../../../../../test/helpers/ui-style-fixtures.js"; +import { + canRunPlaywrightChromium, + resolvePlaywrightChromiumExecutablePath, +} from "../../../test-helpers/control-ui-e2e.ts"; + +const chromiumExecutablePath = resolvePlaywrightChromiumExecutablePath(chromium.executablePath()); +const describeBrowserLayout = canRunPlaywrightChromium(chromiumExecutablePath) + ? describe + : describe.skip; + +let browser: Browser | null = null; + +describeBrowserLayout("chat swarm progress browser layout", () => { + beforeAll(async () => { + browser = await chromium.launch({ executablePath: chromiumExecutablePath, headless: true }); + }); + + afterAll(async () => { + await browser?.close(); + }); + + it("keeps long task popovers reachable inside the viewport", async () => { + if (!browser) { + throw new Error("expected browser"); + } + const page = await browser.newPage({ viewport: { width: 375, height: 568 } }); + const tasks = Array.from( + { length: 256 }, + (_, index) => `
Worker ${index + 1}
`, + ).join(""); + const styles = ["ui/src/styles/base.css", "ui/src/styles/chat/layout.css"] + .map((file) => readStyleSheet(file)) + .join("\n"); + await page.setContent(` + + `); + + const layout = await page.locator(".chat-swarm__tasks").evaluate((element) => { + const style = getComputedStyle(element); + return { + clientHeight: element.clientHeight, + overflowY: style.overflowY, + scrollHeight: element.scrollHeight, + top: element.getBoundingClientRect().top, + }; + }); + + expect(layout.top).toBeGreaterThanOrEqual(0); + expect(layout.overflowY).toBe("auto"); + expect(layout.scrollHeight).toBeGreaterThan(layout.clientHeight); + }); +}); diff --git a/ui/src/styles/chat/layout.css b/ui/src/styles/chat/layout.css index c569a4670832..f5861d838eca 100644 --- a/ui/src/styles/chat/layout.css +++ b/ui/src/styles/chat/layout.css @@ -2584,6 +2584,9 @@ button.chat-reply-preview--message:disabled { bottom: calc(100% + 8px); display: grid; gap: 2px; + max-height: calc(100dvh - 116px); + overflow-y: auto; + overscroll-behavior: contain; padding: 8px; border: 1px solid color-mix(in srgb, var(--border) 70%, transparent); border-radius: calc(14px * var(--openclaw-corner-radius-scale));