From 4dabb51d71e61372c7a27147e02ff643fb0ad080 Mon Sep 17 00:00:00 2001 From: Guan Tong Date: Sat, 1 Aug 2026 23:26:07 +0800 Subject: [PATCH] fix(widget): keep chat history rows scrollable --- src/agent_manager/api/static/widget.js | 6 ++-- .../api/static/widget/styles/styles.ts | 6 ++-- tests/e2e/widget.spec.ts | 34 +++++++++++++++++++ 3 files changed, 40 insertions(+), 6 deletions(-) diff --git a/src/agent_manager/api/static/widget.js b/src/agent_manager/api/static/widget.js index e73ef414..901e8e8c 100644 --- a/src/agent_manager/api/static/widget.js +++ b/src/agent_manager/api/static/widget.js @@ -53685,11 +53685,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; @@ -53698,7 +53698,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 bc5623e6..7070416e 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 46dfe6eb..46a3b645 100644 --- a/tests/e2e/widget.spec.ts +++ b/tests/e2e/widget.spec.ts @@ -570,6 +570,40 @@ test("thread drawer lists conversations, switches to one, and starts a new chat" await expect.poll(() => shadowText(page, ".messages")).toContain("How can I help you today?"); }); +test("thread drawer keeps conversation rows readable and scrollable", async ({ page }) => { + const threads = Array.from({ length: 30 }, (_, index) => ({ + conversation_id: `conv-${index}`, + title: `Conversation ${index}`, + last_message_at: "2026-06-01T00:00:00Z", + })); + await mockConversationApi(page, { threads }); + + await page.goto("/widget-demo.html"); + await shadowClick(page, ".launcher"); + await shadowClick(page, '.header-btn[aria-label="Conversations"]'); + + const handle = await widget(page); + const metrics = await handle.evaluate((element) => { + const root = element.shadowRoot; + const list = root?.querySelector(".thread-list"); + const items = Array.from(root?.querySelectorAll(".thread-item") ?? []); + if (!list || items.length === 0) throw new Error("Thread drawer did not render"); + + return { + itemCount: items.length, + itemHeights: items.map((item) => item.getBoundingClientRect().height), + itemFlexShrink: getComputedStyle(items[0]).flexShrink, + listClientHeight: list.clientHeight, + listScrollHeight: list.scrollHeight, + }; + }); + + expect(metrics.itemCount).toBe(30); + expect(metrics.itemFlexShrink).toBe("0"); + expect(Math.min(...metrics.itemHeights)).toBeGreaterThan(30); + expect(metrics.listScrollHeight).toBeGreaterThan(metrics.listClientHeight); +}); + test("thinking dots persist when switching away from an in-flight thread and back", async ({ page, }) => {