From b88698e7d9d911dba47e3e8d96dde989f6137f7d Mon Sep 17 00:00:00 2001 From: 0xPratik Date: Tue, 8 Sep 2026 13:38:16 +0545 Subject: [PATCH 1/5] Test New Agent drawer overflow containment --- apps/web/test/create-agent-panel.test.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/apps/web/test/create-agent-panel.test.tsx b/apps/web/test/create-agent-panel.test.tsx index fbee17094..5f0ad27a8 100644 --- a/apps/web/test/create-agent-panel.test.tsx +++ b/apps/web/test/create-agent-panel.test.tsx @@ -163,6 +163,15 @@ async function settle() { } describe("CreateAgentPanel happy path", () => { + test("scopes drawer overflow handling to the New Agent panel", async () => { + await mount(); + expect( + document + .querySelector('[data-slot="dialog-content"]') + ?.classList.contains("create-agent-panel"), + ).toBe(true); + }); + test("Advanced is collapsed by default", async () => { await mount(); const details = document.querySelector( From f8331daad5ebe1f2b8e92317a356b9909f6aece1 Mon Sep 17 00:00:00 2001 From: 0xPratik Date: Tue, 8 Sep 2026 13:38:36 +0545 Subject: [PATCH 2/5] Fix New Agent drawer clipping and scrollbar overlap --- apps/web/src/app.css | 23 +++++++++++++++++++++++ apps/web/src/pages/create-agent-panel.tsx | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/apps/web/src/app.css b/apps/web/src/app.css index 2fd9883ad..8bdf4b306 100644 --- a/apps/web/src/app.css +++ b/apps/web/src/app.css @@ -2990,6 +2990,29 @@ tr.insights-row-clickable:hover { /* The optional "what should this agent do?" field: visually quieter than the required Name field above it — a smaller label, no bold-required asterisk, since a name alone is the supported happy path. */ +.create-agent-panel { + min-width: 0; +} + +.create-agent-panel [data-slot="dialog-body"] { + min-width: 0; + overflow-x: hidden; + padding-inline: 0.5rem 0.75rem; + scrollbar-gutter: stable; +} + +.create-agent-panel [data-slot="dialog-body"] > *, +.create-agent-panel .create-agent-advanced-body > * { + min-width: 0; +} + +.create-agent-panel [data-slot="dialog-body"] input, +.create-agent-panel [data-slot="dialog-body"] select, +.create-agent-panel [data-slot="dialog-body"] textarea { + min-width: 0; + max-width: 100%; +} + .create-agent-quiet-field { display: flex; flex-direction: column; diff --git a/apps/web/src/pages/create-agent-panel.tsx b/apps/web/src/pages/create-agent-panel.tsx index b058eee92..2d3734974 100644 --- a/apps/web/src/pages/create-agent-panel.tsx +++ b/apps/web/src/pages/create-agent-panel.tsx @@ -398,7 +398,7 @@ export function CreateAgentPanel({ return ( - + New agent From 479ca40ef847c2b1d9f25b07469f313cec7f5af9 Mon Sep 17 00:00:00 2001 From: 0xPratik Date: Wed, 9 Sep 2026 13:15:16 +0545 Subject: [PATCH 3/5] Test New Agent drawer overflow in real browser --- scripts/e2e/browser/walkthrough.ts | 206 +++++++++++++++++++++++++++++ 1 file changed, 206 insertions(+) diff --git a/scripts/e2e/browser/walkthrough.ts b/scripts/e2e/browser/walkthrough.ts index 105ad3842..4c9ee5643 100644 --- a/scripts/e2e/browser/walkthrough.ts +++ b/scripts/e2e/browser/walkthrough.ts @@ -284,6 +284,155 @@ async function countMatching(page: Page, selector: string): Promise { ); } +async function createAgentDrawerLayout( + page: Page, + viewportName: string, +): Promise<{ + readonly viewportName: string; + readonly bodyClientWidth: number; + readonly bodyScrollWidth: number; + readonly panelClientWidth: number; + readonly panelScrollWidth: number; + readonly minimumLeftClearance: number; + readonly maximumRightOverflow: number; + readonly activeControl: string; +}> { + return page.evaluate((name: string) => { + const panel = document.querySelector( + '[data-slot="dialog-content"].create-agent-panel', + ); + const body = panel?.querySelector('[data-slot="dialog-body"]'); + if (panel === null || body === null) { + throw new Error("New Agent drawer body was not rendered"); + } + + const bodyRect = body.getBoundingClientRect(); + const bodyContentLeft = bodyRect.left + body.clientLeft; + const bodyContentRight = bodyContentLeft + body.clientWidth; + const controls = Array.from( + body.querySelectorAll("input, select, textarea, button"), + ); + if (controls.length === 0) { + throw new Error("New Agent drawer rendered no controls"); + } + + const minimumLeftClearance = Math.min( + ...controls.map( + (control) => control.getBoundingClientRect().left - bodyContentLeft, + ), + ); + const maximumRightOverflow = Math.max( + ...controls.map( + (control) => control.getBoundingClientRect().right - bodyContentRight, + ), + ); + + return { + viewportName: name, + bodyClientWidth: body.clientWidth, + bodyScrollWidth: body.scrollWidth, + panelClientWidth: panel.clientWidth, + panelScrollWidth: panel.scrollWidth, + minimumLeftClearance, + maximumRightOverflow, + activeControl: + document.activeElement instanceof HTMLElement + ? document.activeElement.id + : "", + }; + }, viewportName); +} + +async function openCreateAgentDrawer( + page: Page, + webBaseUrl: string, +): Promise { + await page.goto(`${webBaseUrl}/agents`, { waitUntil: "domcontentloaded" }); + await page.waitForSelector('button[aria-label="Create an agent"]', { + timeout: 15_000, + }); + await clickStable(page, 'button[aria-label="Create an agent"]'); + await page.waitForSelector( + '[data-slot="dialog-content"].create-agent-panel', + { timeout: 15_000 }, + ); + await page.waitForSelector("#create-agent-name"); + await page.type( + "#create-agent-name", + "Research Buddy with a deliberately long name", + ); + await page.type( + "#create-agent-purpose", + "A deliberately long description that wraps across multiple lines so the drawer has to contain the full form without clipping its focus ring or letting the scrollbar cover the field.", + ); + await page.focus("#create-agent-name"); + await clickStable(page, ".create-agent-advanced > summary"); + await page.waitForSelector("#create-agent-advanced-handle"); + await page + .waitForFunction( + () => { + const model = document.querySelector( + "#create-agent-advanced-model", + ); + return ( + (model !== null && model.options.length > 0) || + document.querySelector('[role="status"]') !== null + ); + }, + { timeout: 5_000 }, + ) + .catch(() => undefined); + const modelValues = await page.$$eval( + "#create-agent-advanced-model option", + (options) => + options + .map((option) => option.value) + .filter((value) => value.trim() !== ""), + ); + const modelSelectorAvailable = modelValues.length > 0; + if (modelSelectorAvailable) { + const modelValue = modelValues[0]; + if (modelValue === undefined) { + throw new Error("Model selector reported no usable options"); + } + await page.select("#create-agent-advanced-model", modelValue); + await page.focus("#create-agent-advanced-model"); + } else { + await page.focus("#create-agent-advanced-handle"); + } + await page.evaluate(() => { + const body = document.querySelector( + '[data-slot="dialog-body"]', + ); + if (body === null) throw new Error("New Agent drawer body was not found"); + body.scrollTop = body.scrollHeight; + }); + await page.waitForFunction( + () => + document.querySelector('[data-slot="dialog-body"]') + ?.scrollTop !== 0, + { timeout: 5_000 }, + ); + return modelSelectorAvailable; +} + +async function closeCreateAgentDrawer(page: Page): Promise { + await page.evaluate(() => { + const panel = document.querySelector( + '[data-slot="dialog-content"].create-agent-panel', + ); + const cancel = Array.from(panel?.querySelectorAll("button") ?? []).find( + (button) => button.textContent?.trim() === "Cancel", + ); + if (cancel === undefined) throw new Error("Cancel button was not found"); + cancel.click(); + }); + await page.waitForSelector( + '[data-slot="dialog-content"].create-agent-panel', + { hidden: true, timeout: 5_000 }, + ); +} + // --- the walkthrough ----------------------------------------------------- /** The picker card that mints a plain empty channel (`workbench-templates.ts`). */ @@ -643,6 +792,63 @@ async function run(): Promise { }, ); + await step( + () => page, + "04b-create-agent-drawer-layout", + async () => { + const previousViewport = page.viewport() ?? { + width: 1440, + height: 900, + }; + const cases = [ + { name: "desktop", width: 1280, height: 720 }, + { name: "short", width: 1280, height: 480 }, + ] as const; + const measurements: Awaited< + ReturnType + >[] = []; + let modelSelectorSeen = false; + + try { + for (const viewport of cases) { + await page.setViewport(viewport); + const modelSelectorAvailable = await openCreateAgentDrawer( + page, + webBaseUrl, + ); + modelSelectorSeen ||= modelSelectorAvailable; + const measurement = await createAgentDrawerLayout( + page, + viewport.name, + ); + measurements.push(measurement); + if ( + measurement.bodyScrollWidth !== measurement.bodyClientWidth || + measurement.panelScrollWidth !== measurement.panelClientWidth || + measurement.minimumLeftClearance < 4 || + measurement.maximumRightOverflow > 1 || + measurement.activeControl !== + (modelSelectorAvailable + ? "create-agent-advanced-model" + : "create-agent-advanced-handle") + ) { + throw new Error( + `New Agent drawer escaped its bounds at ${viewport.name}: ${JSON.stringify(measurement)}`, + ); + } + await closeCreateAgentDrawer(page); + } + } finally { + await page.setViewport(previousViewport); + } + + return { + status: "pass", + detail: `name, description, Advanced, ${modelSelectorSeen ? "model selector, " : "model catalog unavailable, "}focus, and scroll stayed contained: ${JSON.stringify(measurements)}`, + }; + }, + ); + await step( () => page, "04c-workbench-sidebar", From 4b036c05730e934c14e0cd6f6f3e96373bf56918 Mon Sep 17 00:00:00 2001 From: 0xPratik Date: Wed, 9 Sep 2026 13:16:04 +0545 Subject: [PATCH 4/5] Keep New Agent CSS documentation adjacent --- apps/web/src/app.css | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/web/src/app.css b/apps/web/src/app.css index 8bdf4b306..73ca57f03 100644 --- a/apps/web/src/app.css +++ b/apps/web/src/app.css @@ -2987,9 +2987,6 @@ tr.insights-row-clickable:hover { color: var(--muted-foreground); } -/* The optional "what should this agent do?" field: visually quieter than - the required Name field above it — a smaller label, no bold-required - asterisk, since a name alone is the supported happy path. */ .create-agent-panel { min-width: 0; } @@ -3013,6 +3010,9 @@ tr.insights-row-clickable:hover { max-width: 100%; } +/* The optional "what should this agent do?" field: visually quieter than + the required Name field above it — a smaller label, no bold-required + asterisk, since a name alone is the supported happy path. */ .create-agent-quiet-field { display: flex; flex-direction: column; From 82d90c77184a97a3680d928960427200daf8744a Mon Sep 17 00:00:00 2001 From: 0xPratik Date: Wed, 9 Sep 2026 13:23:57 +0545 Subject: [PATCH 5/5] Tighten drawer browser assertion narrowing --- scripts/e2e/browser/walkthrough.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/scripts/e2e/browser/walkthrough.ts b/scripts/e2e/browser/walkthrough.ts index 4c9ee5643..98c1dae57 100644 --- a/scripts/e2e/browser/walkthrough.ts +++ b/scripts/e2e/browser/walkthrough.ts @@ -301,8 +301,11 @@ async function createAgentDrawerLayout( const panel = document.querySelector( '[data-slot="dialog-content"].create-agent-panel', ); - const body = panel?.querySelector('[data-slot="dialog-body"]'); - if (panel === null || body === null) { + if (panel === null) { + throw new Error("New Agent drawer was not rendered"); + } + const body = panel.querySelector('[data-slot="dialog-body"]'); + if (body === null) { throw new Error("New Agent drawer body was not rendered"); }