diff --git a/src/agent_manager/api/static/widget.js b/src/agent_manager/api/static/widget.js index 7651b89e..e80ae718 100644 --- a/src/agent_manager/api/static/widget.js +++ b/src/agent_manager/api/static/widget.js @@ -54505,11 +54505,11 @@ function styles(config) { .thread-drawer.open { transform: none; opacity: 1; pointer-events: auto; } .thread-drawer-head { display: flex; align-items: center; justify-content: space-between; padding: 10px 12px 10px 18px; font-weight: 600; font-size: 14px; color: #18181b; - border-bottom: 1px solid #f0f0f1; } + border-bottom: 1px solid #f0f0f1; flex: 0 0 auto; } .thread-new { display: flex; align-items: center; gap: 8px; margin: 12px 14px 4px; padding: 10px 14px; border: 1px solid #e4e4e7; border-radius: 12px; background: #fff; color: #18181b; font-size: 14px; font-weight: 500; cursor: pointer; font-family: inherit; - transition: background .12s; } + transition: background .12s; flex: 0 0 auto; } .thread-new:hover { background: #f4f4f5; } .thread-new svg { width: 16px; height: 16px; flex: 0 0 auto; } .thread-list { flex: 1; min-height: 0; overflow-y: auto; padding: 6px 10px 14px; @@ -54518,7 +54518,7 @@ function styles(config) { .thread-item { text-align: left; border: 0; background: transparent; cursor: pointer; padding: 10px 12px; border-radius: 10px; font-size: 13.5px; color: #3f3f46; font-family: inherit; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - transition: background .12s; } + transition: background .12s; flex: 0 0 auto; } .thread-item:hover { background: #f4f4f5; } .thread-item.active { background: #f4f4f5; color: #18181b; font-weight: 600; } .thread-empty { color: #a1a1aa; font-size: 13px; text-align: center; padding: 24px 12px; margin: 0; } diff --git a/src/agent_manager/api/static/widget/styles/styles.ts b/src/agent_manager/api/static/widget/styles/styles.ts index d9b22a18..c413a33c 100644 --- a/src/agent_manager/api/static/widget/styles/styles.ts +++ b/src/agent_manager/api/static/widget/styles/styles.ts @@ -66,11 +66,11 @@ export function styles(config: AgentChatConfig): string { .thread-drawer.open { transform: none; opacity: 1; pointer-events: auto; } .thread-drawer-head { display: flex; align-items: center; justify-content: space-between; padding: 10px 12px 10px 18px; font-weight: 600; font-size: 14px; color: #18181b; - border-bottom: 1px solid #f0f0f1; } + border-bottom: 1px solid #f0f0f1; flex: 0 0 auto; } .thread-new { display: flex; align-items: center; gap: 8px; margin: 12px 14px 4px; padding: 10px 14px; border: 1px solid #e4e4e7; border-radius: 12px; background: #fff; color: #18181b; font-size: 14px; font-weight: 500; cursor: pointer; font-family: inherit; - transition: background .12s; } + transition: background .12s; flex: 0 0 auto; } .thread-new:hover { background: #f4f4f5; } .thread-new svg { width: 16px; height: 16px; flex: 0 0 auto; } .thread-list { flex: 1; min-height: 0; overflow-y: auto; padding: 6px 10px 14px; @@ -79,7 +79,7 @@ export function styles(config: AgentChatConfig): string { .thread-item { text-align: left; border: 0; background: transparent; cursor: pointer; padding: 10px 12px; border-radius: 10px; font-size: 13.5px; color: #3f3f46; font-family: inherit; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; - transition: background .12s; } + transition: background .12s; flex: 0 0 auto; } .thread-item:hover { background: #f4f4f5; } .thread-item.active { background: #f4f4f5; color: #18181b; font-weight: 600; } .thread-empty { color: #a1a1aa; font-size: 13px; text-align: center; padding: 24px 12px; margin: 0; } diff --git a/tests/e2e/widget.spec.ts b/tests/e2e/widget.spec.ts index 35a6da56..d41923cb 100644 --- a/tests/e2e/widget.spec.ts +++ b/tests/e2e/widget.spec.ts @@ -1501,6 +1501,43 @@ test("thread drawer paginates and appends next pages on scroll", async ({ page } await expect.poll(() => shadowText(page, ".thread-drawer")).toContain("Thread Three"); }); +test("thread drawer keeps natural row height with many conversations and scrolls", async ({ page }) => { + const threads = Array.from({ length: 25 }, (_, i) => ({ + conversation_id: `conv-${i}`, + title: `Conversation number ${i + 1}`, + last_message_at: "2026-06-28T00:00:00Z", + })); + await mockConversationApi(page, { threads }); + await page.route("**/conversations/*/messages", async (route: Route) => { + if (route.request().method() !== "GET") return route.fallback(); + await route.fulfill({ status: 200, contentType: "application/json", body: "[]" }); + }); + + await page.goto("/widget-demo.html"); + await shadowClick(page, ".launcher"); + await shadowClick(page, '[aria-label="Conversations"]'); + await expect.poll(() => shadowText(page, ".thread-drawer")).toContain("Conversation number 25"); + + // Every row must render at the same natural height — the count must not + // compress them. (The bug: flex-shrink squashed rows to ~25px once the list + // overflowed, clipping titles and hiding the scrollbar.) + const heights = await (await widget(page)).evaluate((el) => { + const rows = Array.from(el.shadowRoot?.querySelectorAll(".thread-item") ?? []); + return rows.map((row) => row.getBoundingClientRect().height); + }); + const first = heights[0]; + expect(first).toBeGreaterThan(20); + for (const height of heights) expect(Math.abs(height - first)).toBeLessThan(0.5); + + // The list itself must overflow so the scrollbar is usable. + const scrollMetrics = await (await widget(page)).evaluate((el) => { + const list = el.shadowRoot?.querySelector(".thread-list"); + if (!list) throw new Error("thread-list not found"); + return { scrollHeight: list.scrollHeight, clientHeight: list.clientHeight }; + }); + expect(scrollMetrics.scrollHeight).toBeGreaterThan(scrollMetrics.clientHeight); +}); + test("visitor pass cached, cookie login hand-off merges history and first thread list request observes merged threads", async ({ page }) => { const calls: string[] = []; await pinVisitorPass(page);