diff --git a/apps/ui/e2e/__screens__/narrow-pane.png b/apps/ui/e2e/__screens__/narrow-pane.png new file mode 100644 index 000000000..15046d519 Binary files /dev/null and b/apps/ui/e2e/__screens__/narrow-pane.png differ diff --git a/apps/ui/e2e/__screens__/split-view.png b/apps/ui/e2e/__screens__/split-view.png index fbea106ef..97ae9982d 100644 Binary files a/apps/ui/e2e/__screens__/split-view.png and b/apps/ui/e2e/__screens__/split-view.png differ diff --git a/apps/ui/e2e/visual-smoke.spec.ts b/apps/ui/e2e/visual-smoke.spec.ts index 820dbcf05..2c6de4937 100644 --- a/apps/ui/e2e/visual-smoke.spec.ts +++ b/apps/ui/e2e/visual-smoke.spec.ts @@ -261,4 +261,25 @@ test.describe("visual smoke — legacy-palette baseline", () => { scale: "css", }); }); + + test("narrow pane — agent view stacks its log panel", async ({ page }) => { + await page.goto("/#/channel/ch-svc-compass/topic/top-compass-acp"); + await page.locator(".conv-composer").waitFor({ state: "visible" }); + await page.locator('.cx-tab-strip [role="tab"]').first().click(); + await page.keyboard.press("w"); + await page.keyboard.press("v"); + await page + .locator("aside.left .tree-agent", { hasText: "compass-ui" }) + .first() + .click(); + const pane = page.locator(".cx-split-pane .view-panel", { + has: page.locator(".agent-view"), + }); + await pane.locator(".log-panel .obs-body").waitFor(); + await page.evaluate(() => document.fonts.ready); + await expect(pane).toHaveScreenshot("narrow-pane.png", { + animations: "disabled", + scale: "css", + }); + }); }); diff --git a/apps/ui/src/app.css b/apps/ui/src/app.css index 2e9d370d5..4ba47c743 100644 --- a/apps/ui/src/app.css +++ b/apps/ui/src/app.css @@ -458,8 +458,10 @@ white-space: nowrap; } -/* A view panel stands in for .main as its view's box, so views size as before. */ +/* A view panel stands in for .main as its view's box, so views size as before. + * It is the width container a view adapts to, since a split pane can be narrow. */ .view-panel { + container: view-panel / inline-size; flex: 1; min-height: 0; display: flex; @@ -3614,6 +3616,57 @@ display: block; } +/* A narrow pane stacks the log panel under the body: beside it, a 340px panel + * leaves the body no room. Open and minimized both keep their controls. */ +@container view-panel (width < 560px) { + .agent-view { + grid-template-columns: minmax(0, 1fr); + grid-template-rows: auto minmax(0, 1fr) fit-content(40%); + grid-template-areas: + "header" + "body" + "log"; + } + + /* The handle stays whole; model and cwd truncate so Share stays in view. */ + .av-header .av-name { + flex: none; + white-space: nowrap; + } + + .av-header .av-model, + .av-header .av-cwd { + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .log-panel, + .log-panel.minimized { + width: auto; + border-left: none; + border-top: 1px solid var(--cx-border); + } + + .log-panel.minimized .obs-head { + flex-direction: row; + } + + .log-panel.minimized .obs-spacer { + flex: 1; + } + + /* The toggle points down to minimize and up to expand. */ + .obs-min-glyph { + transform: rotate(-90deg); + } + + .obs-min-glyph.open { + transform: rotate(90deg); + } +} + .log-panel .obs-body { flex: 1; min-height: 0;