Keep mobile workspace switching responsive with long chats (#3610)

* perf(app): bound native workspace mount work

Native workspace switches retained inactive React trees and mounted large chat histories in aggressive list batches, making Hermes and Fabric work scale with prior usage.

Retain only the active native workspace, reveal a shared turn-aligned history window locally before daemon pagination, preserve desktop retention semantics, and pace native list mounting.

* test(app): cover bounded history journeys

Exercise local history reveal and hidden outline jumps through the production browser viewport while preserving held-page pagination and scroll anchoring.

* fix(app): reveal fetched outline targets

A bounded merged jump could load its target before the mounted history window, leaving the pending jump unable to scroll. Reveal fetched hidden targets through the existing history-window capability, then scroll once they mount.

* test(app): follow anchored history reveals

History-start helpers could stop at a transient edge before local window expansion restored the content anchor. Retry the real wheel or keyboard action after geometry settles until the timeline remains at history start.

* test(app): wait for history anchor release
This commit is contained in:
Mohamed Boudra
2026-08-20 20:52:12 +02:00
committed by GitHub
parent 5d7afd59ae
commit 2a0a0ff656
17 changed files with 646 additions and 59 deletions
@@ -91,14 +91,16 @@ test.describe("Agent timeline pagination", () => {
await expectTimelinePromptVisible(page, agent.newestPrompt);
await expectTimelinePromptCentered(page, agent.newestPrompt);
await expectTimelinePromptNotMounted(page, agent.oldestPrompt);
await expectTimelinePromptNotMounted(page, agent.initialTailOldestPrompt);
await userScrollsTimelineToHistoryStart(page);
await expectTimelinePromptVisible(page, agent.initialTailOldestPrompt);
await history.expectRequestedPages(1);
history.releasePage(1);
await history.expectSettledWithRequestedPages(1);
await expectTimelinePromptCentered(page, agent.firstOlderPagePrompt);
await userScrollsTimelineToHistoryStart(page);
await expectTimelinePromptCentered(page, agent.firstOlderPagePrompt);
await history.expectRequestedPages(2);
} finally {
await agent.cleanup();
@@ -119,6 +119,7 @@ test.describe("desktop chat outline", () => {
test("previews and jumps to the focused prompt from the keyboard", async ({ page }) => {
await focusChatOutlinePrompt(page, 1);
await expectChatOutlinePreview(page, agent.prompts[0]);
await expectTimelinePromptNotMounted(page, agent.oldestPrompt);
await pressEnterOnFocusedPrompt(page);
await expectTimelinePromptLandedBelowTop(page, agent.oldestPrompt);
@@ -532,30 +532,35 @@ export async function scrollTimelineToOldestLoadedEdge(page: Page): Promise<void
export async function userScrollsTimelineToHistoryStart(page: Page): Promise<void> {
const scroll = page.locator('[data-testid="agent-chat-scroll"]:visible').first();
await scroll.hover();
for (let step = 0; step < 60; step += 1) {
if ((await readTimelineViewport(page)).scrollTop <= HISTORY_START_THRESHOLD_PX) {
break;
}
await page.mouse.wheel(0, -1_000);
await page.evaluate(
() =>
new Promise<void>((resolve) => {
requestAnimationFrame(() => resolve());
}),
);
}
await expect
.poll(async () => (await readTimelineViewport(page)).scrollTop)
.toBeLessThanOrEqual(HISTORY_START_THRESHOLD_PX);
await returnTimelineToSettledHistoryStart(page, () => page.mouse.wheel(0, -1_000));
}
export async function userNavigatesTimelineToHistoryStartWithKeyboard(page: Page): Promise<void> {
const scroll = page.locator('[data-testid="agent-chat-scroll"]:visible').first();
await scroll.focus();
await page.keyboard.press("Home");
await expect
.poll(async () => (await readTimelineViewport(page)).scrollTop)
.toBeLessThanOrEqual(HISTORY_START_THRESHOLD_PX);
await returnTimelineToSettledHistoryStart(page, () => page.keyboard.press("Home"));
}
async function returnTimelineToSettledHistoryStart(
page: Page,
moveUp: () => Promise<unknown>,
): Promise<void> {
let previous = await readTimelineViewport(page);
while (true) {
await moveUp();
await waitForTimelineGeometryToSettle(page);
let viewport = await readTimelineViewport(page);
if (viewport.scrollTop <= HISTORY_START_THRESHOLD_PX) {
return;
}
await expect(page.getByTestId("load-older-history-spinner")).toBeHidden();
await waitForTimelineGeometryToSettle(page);
viewport = await readTimelineViewport(page);
expect(
viewport.scrollTop < previous.scrollTop || viewport.scrollHeight > previous.scrollHeight,
).toBe(true);
previous = viewport;
}
}
export async function scrollTimelineToNewestLoadedEdge(page: Page): Promise<void> {