fix(app): stabilize diff reviews and workspace tabs

Reuse measured diff geometry when review controls open, keeping the interaction responsive on large changes. Preserve complete tab-track snapshots while exact content widths settle so new tabs and retained workspaces do not flash or truncate.
This commit is contained in:
Mohamed Boudra
2026-08-17 21:14:38 +02:00
parent c7b103a2cd
commit 8f7b3e8d75
10 changed files with 433 additions and 96 deletions
@@ -25,6 +25,39 @@ const MAX_MEDIAN_FRAME_MS = POC_MEDIAN_FRAME_MS * 1.1;
const MAX_P95_FRAME_MS = POC_P95_FRAME_MS * 1.1;
diffPerfDescribe("Diff canvas performance", () => {
test("opening a review stays responsive in a large diff", async ({ page }) => {
test.setTimeout(120_000);
const repo = await createLargeDiffRepository();
const client = await connectSeedClient();
let cdp: CDPSession | null = null;
try {
const created = await client.createWorkspace({
source: { kind: "directory", path: repo.path },
});
if (!created.workspace) {
throw new Error(created.error ?? "Failed to create diff performance workspace");
}
await configureUnwrappedUnifiedDiff(page);
await page.setViewportSize({ width: 1400, height: 900 });
await page.goto(buildHostWorkspaceRoute(getServerId(), created.workspace.id));
await waitForWorkspaceTabsVisible(page);
await openChangesPanel(page);
await expect(page.getByTestId("git-diff-canvas")).toBeVisible({ timeout: 30_000 });
cdp = await page.context().newCDPSession(page);
await cdp.send("Emulation.setCPUThrottlingRate", { rate: CPU_SLOWDOWN });
const elapsedMs = await openFirstReviewEditor(page);
expect(elapsedMs).toBeLessThanOrEqual(200 * CPU_SLOWDOWN);
} finally {
await cdp?.send("Emulation.setCPUThrottlingRate", { rate: 1 }).catch(() => undefined);
await cdp?.detach().catch(() => undefined);
await client.close().catch(() => undefined);
await repo.cleanup().catch(() => undefined);
}
});
test("one canvas stays filled through continuous fast scrolling", async ({ page }, testInfo) => {
test.setTimeout(180_000);
const repo = await createLargeDiffRepository();
@@ -241,6 +274,21 @@ async function configureUnwrappedUnifiedDiff(page: Page): Promise<void> {
);
}
async function openFirstReviewEditor(page: Page): Promise<number> {
const body = page.getByTestId("diff-file-0-body");
const canvas = page.getByTestId("git-diff-canvas");
const [bodyBounds, fontSize] = await Promise.all([
body.boundingBox(),
canvas.evaluate((element) => Number.parseFloat(getComputedStyle(element).fontSize)),
]);
if (!bodyBounds) throw new Error("Expanded diff body has no bounds");
const lineHeight = Math.round(fontSize * 1.5);
const startedAt = performance.now();
await page.mouse.click(bodyBounds.x + 120, bodyBounds.y + lineHeight * 1.5);
await expect(page.getByTestId("inline-review-editor")).toBeVisible();
return performance.now() - startedAt;
}
async function expectSingleCanvasSurface(page: Page): Promise<void> {
await expect(page.locator("canvas")).toHaveCount(1);
await expect(page.locator('[data-testid^="diff-code-row-"]')).toHaveCount(0);
@@ -12,6 +12,7 @@ import {
waitForTabWithTitle,
measureTileTransition,
sampleTabsDuringTransition,
expectTabTitleFits,
terminalSurfaceLocator,
} from "../support/helpers/launcher";
import { expectComposerVisible, composerLocator } from "../support/helpers/composer";
@@ -197,7 +198,7 @@ test.describe("Tab transitions (no flash)", () => {
await gotoWorkspace(page, workspace.workspaceId);
// Sample tabs at high frequency across the transition
const snapshots = await sampleTabsDuringTransition(page, () => clickNewChat(page), 2_000, 30);
const snapshots = await sampleTabsDuringTransition(page, () => clickNewChat(page));
// Every snapshot should have at least one tab — no blank/zero-tab frames
for (const snapshot of snapshots) {
@@ -207,11 +208,15 @@ test.describe("Tab transitions (no flash)", () => {
// Tab count should never spike excessively (no duplicate flash from add-then-remove).
// When running in-suite, previous tests may have created tabs on the shared workspace,
// so we allow +2 tolerance for accumulated state and React render batching.
const counts = snapshots.map((s) => s.length);
const counts = snapshots.map((snapshot) => snapshot.length);
const maxCount = Math.max(...counts);
const initialCount = counts[0] ?? 0;
expect(maxCount).toBeLessThanOrEqual(initialCount + 2);
expect(new Set(snapshots.map((snapshot) => JSON.stringify(snapshot))).size).toBeLessThanOrEqual(
2,
);
await expectTabTitleFits(page, "New Agent", { min: 96, max: 160 });
});
test("Terminal transition completes within visual budget", async ({ page }) => {
+51 -13
View File
@@ -158,21 +158,59 @@ export async function sampleTabsDuringTransition(
page: Page,
action: () => Promise<void>,
durationMs = 2_000,
intervalMs = 30,
): Promise<string[][]> {
const snapshots: string[][] = [];
const startSampling = async () => {
const start = Date.now();
while (Date.now() - start < durationMs) {
snapshots.push(await getTabTestIds(page));
await page.waitForTimeout(intervalMs);
): Promise<Array<Array<{ id: string; width: number }>>> {
await page.evaluate((duration) => {
const scope = globalThis as typeof globalThis & {
__paseoTabTrackFrames?: Array<Array<{ id: string; width: number }>>;
};
scope.__paseoTabTrackFrames = [];
const startedAt = performance.now();
function sample() {
const tabs = Array.from(
document.querySelectorAll<HTMLElement>(
'[data-testid^="workspace-tab-"]:not([data-testid^="workspace-tab-context-"])',
),
).filter((element) => element.getClientRects().length > 0);
scope.__paseoTabTrackFrames?.push(
tabs.map((element) => ({
id: element.getAttribute("data-testid") ?? "",
width: Math.round(element.getBoundingClientRect().width),
})),
);
if (performance.now() - startedAt < duration) {
requestAnimationFrame(sample);
}
}
};
const samplingPromise = startSampling();
requestAnimationFrame(sample);
}, durationMs);
await action();
await samplingPromise;
return snapshots;
await page.waitForTimeout(durationMs + 100);
return page.evaluate(() => {
const scope = globalThis as typeof globalThis & {
__paseoTabTrackFrames?: Array<Array<{ id: string; width: number }>>;
};
return scope.__paseoTabTrackFrames ?? [];
});
}
export async function expectTabTitleFits(
page: Page,
title: string,
widthRange: { min: number; max: number },
): Promise<void> {
const tab = page
.locator('[data-testid^="workspace-tab-"]:not([data-testid^="workspace-tab-context-"])')
.filter({ hasText: title })
.filter({ visible: true })
.last();
await expect(tab).toContainText(title);
const tabWidth = await tab.evaluate((element) => element.getBoundingClientRect().width);
expect(tabWidth).toBeGreaterThanOrEqual(widthRange.min);
expect(tabWidth).toBeLessThanOrEqual(widthRange.max);
const label = tab.getByText(title, { exact: true });
await expect(label).toBeVisible();
const labelFits = await label.evaluate((element) => element.scrollWidth <= element.clientWidth);
expect(labelFits).toBe(true);
}
export function terminalSurfaceLocator(page: Page) {