mirror of
https://github.com/getpaseo/paseo.git
synced 2026-11-04 07:33:18 +00:00
Keep context details open while chat streams (#8742)
* fix(app): keep hover cards open while unrelated content scrolls Hover cards closed on any scroll outside their content. A streaming chat scrolls on every new line, so the context window details closed while an agent was responding. Close only when the scrolled element contains the trigger, which is the case that moves the card's anchor. * test(app): confirm the turn is still streaming once the details open * test(app): cover context clicks and trigger scrolling Exercise the reported click path without replacing usage transport, and preserve dismissal when a sidebar scroll moves its trigger. Co-authored-by: 3ae3ae <ogyshjsm@naver.com> --------- Co-authored-by: 3ae3ae <ogyshjsm@naver.com> Co-authored-by: paseo-bot[bot] <266920839+paseo-bot[bot]@users.noreply.github.com>
This commit is contained in:
@@ -429,6 +429,32 @@ test("context details support keyboard entry and dismiss when the viewport moves
|
||||
await expect(details).toHaveCount(0);
|
||||
});
|
||||
|
||||
test("context details stay open while the chat streams", async ({ page }) => {
|
||||
test.setTimeout(120_000);
|
||||
await page.setViewportSize(DESKTOP);
|
||||
const session = await seedMockAgentWorkspace({
|
||||
repoPrefix: "context-window-streaming-",
|
||||
title: "Context details while streaming",
|
||||
model: "ten-second-stream",
|
||||
});
|
||||
try {
|
||||
await openAgentRoute(page, session);
|
||||
await expectComposerVisible(page);
|
||||
await submitMessageWithButton(page, "Stream while the context details are open.");
|
||||
const stop = page.getByRole("button", { name: /stop|cancel/i }).first();
|
||||
await expect(stop).toBeVisible();
|
||||
const details = await pressContextWindowMeter(page, /^Context window/);
|
||||
await expect(stop).toBeVisible();
|
||||
await qaScreenshot(page, "context-details-while-streaming");
|
||||
|
||||
await expect(stop).toHaveCount(0, { timeout: 30_000 });
|
||||
await expect(details).toBeVisible();
|
||||
await qaScreenshot(page, "context-details-after-streaming");
|
||||
} finally {
|
||||
await session.cleanup();
|
||||
}
|
||||
});
|
||||
|
||||
test("pointer hovering context details preserves composer focus", async ({ page, agent }) => {
|
||||
const usage = await scriptAgentUsage(page);
|
||||
await page.setViewportSize(DESKTOP);
|
||||
|
||||
@@ -13,6 +13,7 @@ import {
|
||||
movePointerAwayFromWorkspaceHoverCard,
|
||||
openMobileAgentSidebar,
|
||||
pinWorkspaceFromSidebar,
|
||||
scrollWorkspaceList,
|
||||
} from "../support/helpers/sidebar";
|
||||
import { seedWorkspace } from "../support/helpers/seed-client";
|
||||
import { expectWorkspaceHeader } from "../support/helpers/workspace-ui";
|
||||
@@ -582,6 +583,12 @@ test.describe("Half-screen desktop layout", () => {
|
||||
await page.getByTestId("menu-button").click();
|
||||
await expect(page.getByTestId("sidebar-global-new-workspace")).toBeVisible();
|
||||
await expect(sidebarScroll).toHaveJSProperty("scrollTop", scrollTop);
|
||||
|
||||
await movePointerAwayFromWorkspaceHoverCard(page);
|
||||
await focusWorkspaceRowWithKeyboard(page, lastWorkspaceId);
|
||||
await expectWorkspaceHoverCardOpen(page);
|
||||
await scrollWorkspaceList(page);
|
||||
await expectWorkspaceHoverCardClosed(page);
|
||||
} finally {
|
||||
await workspace.cleanup();
|
||||
}
|
||||
|
||||
@@ -54,12 +54,15 @@ export async function leaveContextWindowMeter(page: Page): Promise<void> {
|
||||
await expect(contextWindowDetails(page)).toHaveCount(0);
|
||||
}
|
||||
|
||||
/** Compact screens open the meter's details in a sheet. */
|
||||
export async function pressContextWindowMeter(page: Page, name = METER_NAME): Promise<Locator> {
|
||||
/** Clicking the meter opens its details in the active layout. */
|
||||
export async function pressContextWindowMeter(
|
||||
page: Page,
|
||||
name: string | RegExp = METER_NAME,
|
||||
): Promise<Locator> {
|
||||
await page.getByRole("button", { name, exact: true }).click({ timeout: 30_000 });
|
||||
const sheet = contextWindowDetails(page);
|
||||
await expect(sheet).toBeVisible();
|
||||
return sheet;
|
||||
const details = contextWindowDetails(page);
|
||||
await expect(details).toBeVisible();
|
||||
return details;
|
||||
}
|
||||
|
||||
export async function closeContextWindowSheet(page: Page): Promise<void> {
|
||||
|
||||
@@ -71,6 +71,15 @@ export async function focusWorkspaceRowWithKeyboard(
|
||||
await expect(row).toBeFocused();
|
||||
}
|
||||
|
||||
export async function scrollWorkspaceList(page: Page): Promise<void> {
|
||||
const list = page.getByTestId("sidebar-project-workspace-list-scroll");
|
||||
const before = await list.evaluate((element) => element.scrollTop);
|
||||
await list.evaluate((element) =>
|
||||
element.scrollBy({ top: element.scrollTop > 0 ? -40 : 40, behavior: "instant" }),
|
||||
);
|
||||
await expect.poll(() => list.evaluate((element) => element.scrollTop)).not.toBe(before);
|
||||
}
|
||||
|
||||
export async function expectWorkspaceHoverCardOpen(page: Page): Promise<void> {
|
||||
await expect(page.getByTestId("workspace-hover-card")).toBeVisible();
|
||||
}
|
||||
|
||||
@@ -208,9 +208,9 @@ function WebHoverCard({
|
||||
)
|
||||
keyPressed(event);
|
||||
};
|
||||
// Only a scroll that moves the trigger detaches the card. A streaming chat beside it does not.
|
||||
const scrolled = (event: Event) => {
|
||||
if (!(contentRef.current as unknown as HTMLElement | null)?.contains(event.target as Node))
|
||||
close();
|
||||
if ((event.target as Node).contains(trigger)) close();
|
||||
};
|
||||
document.addEventListener("focusin", focusEntered);
|
||||
document.addEventListener("focusout", focusLeft);
|
||||
|
||||
Reference in New Issue
Block a user