Compare commits

...
Author SHA1 Message Date
paseo-bot[bot] 49a02d8eb9 test(app): name the split resize helper arguments 2026-10-13 17:26:14 +02:00
paseo-bot[bot] ee6b095d0e test(app): take the page from the divider locator in the split resize spec 2026-10-13 17:26:14 +02:00
paseo-bot[bot] fb065afa2f refactor(app): choose split group sizes at the call site 2026-10-13 17:26:14 +02:00
paseo-bot[bot] 6eee6a6b3a fix(app): let every split divider drag after panes are added to a resized row
Divider drags store sizes per group id, apart from the layout tree. A group
keeps its id when panes are added, so after resizing a row and splitting it
again the stored list had fewer entries than panes, and dividers past its end
could not move. Use the dragged sizes only while they have one entry per pane.

Closes #5893
2026-10-13 17:26:13 +02:00
2 changed files with 106 additions and 1 deletions
@@ -0,0 +1,101 @@
import { expect, type Locator, type Page } from "@playwright/test";
import { test } from "../support/fixtures";
import { runWorkspaceActionFromCommandCenter } from "../support/helpers/command-center-workspace-actions";
import { clickNewChat, gotoWorkspace } from "../support/helpers/launcher";
import { seedWorkspace } from "../support/helpers/seed-client";
import { waitForWorkspaceTabsVisible } from "../support/helpers/workspace-tabs";
const DRAG_DISTANCE = -40;
function splitDividers(page: Page): Locator {
return page.getByTestId("workspace-split-resize-handle").filter({ visible: true });
}
async function dividerX(divider: Locator): Promise<number> {
const box = await divider.boundingBox();
if (!box) throw new Error("Divider has no bounding box");
return box.x;
}
async function dragDivider(divider: Locator, deltaX: number): Promise<void> {
const page = divider.page();
const box = await divider.boundingBox();
if (!box) throw new Error("Divider has no bounding box");
const x = box.x + box.width / 2;
const y = box.y + box.height / 2;
await page.mouse.move(x, y);
await page.mouse.down();
await page.mouse.move(x + deltaX / 2, y, { steps: 4 });
await page.mouse.move(x + deltaX, y, { steps: 4 });
await page.mouse.up();
}
async function expectDividerFollowsDrag({
page,
index,
}: {
page: Page;
index: number;
}): Promise<void> {
const divider = splitDividers(page).nth(index);
const before = await dividerX(divider);
await dragDivider(divider, DRAG_DISTANCE);
await expect
.poll(async () => (await dividerX(divider)) - before, {
message: `divider ${index + 1} should follow the drag`,
})
.toBeLessThan(DRAG_DISTANCE * 0.75);
}
async function splitRight({
page,
expectedDividers,
}: {
page: Page;
expectedDividers: number;
}): Promise<void> {
await runWorkspaceActionFromCommandCenter(page, "Split pane right");
await expect(splitDividers(page)).toHaveCount(expectedDividers);
}
test.describe("Split pane resize", () => {
test("every divider drags in a four-pane row", async ({ page }) => {
await page.setViewportSize({ width: 1600, height: 900 });
const workspace = await seedWorkspace({ repoPrefix: "split-resize-fresh-" });
try {
await gotoWorkspace(page, workspace.workspaceId);
await waitForWorkspaceTabsVisible(page);
await clickNewChat(page);
await splitRight({ page, expectedDividers: 1 });
await splitRight({ page, expectedDividers: 2 });
await splitRight({ page, expectedDividers: 3 });
for (const index of [0, 1, 2]) {
await expectDividerFollowsDrag({ page, index });
}
} finally {
await workspace.cleanup();
}
});
test("every divider drags after panes are added to a resized row", async ({ page }) => {
await page.setViewportSize({ width: 1600, height: 900 });
const workspace = await seedWorkspace({ repoPrefix: "split-resize-grown-" });
try {
await gotoWorkspace(page, workspace.workspaceId);
await waitForWorkspaceTabsVisible(page);
await clickNewChat(page);
await splitRight({ page, expectedDividers: 1 });
await expectDividerFollowsDrag({ page, index: 0 });
await splitRight({ page, expectedDividers: 2 });
await splitRight({ page, expectedDividers: 3 });
for (const index of [0, 1, 2]) {
await expectDividerFollowsDrag({ page, index });
}
} finally {
await workspace.cleanup();
}
});
});
@@ -968,8 +968,12 @@ function SplitNodeView({
groupId ? state.splitSizesByWorkspace[workspaceKey]?.[groupId] : undefined,
);
const groupChildren = node.kind === "group" ? node.group.children : EMPTY_SPLIT_NODES;
// Dragged sizes live per group id, apart from the layout tree, and a group keeps its id when
// panes are added or removed. They only apply while they still have one entry per child;
// otherwise the dividers past the end of the list could not move.
const layoutGroupSizes = node.kind === "group" ? node.group.sizes : EMPTY_SPLIT_SIZES;
const groupSizes =
storedGroupSizes ?? (node.kind === "group" ? node.group.sizes : EMPTY_SPLIT_SIZES);
storedGroupSizes?.length === groupChildren.length ? storedGroupSizes : layoutGroupSizes;
const visibleFlex = useMemo(
() => resolveVisibleGroupFlex(groupChildren, groupSizes, maximizedPaneId),
[groupChildren, groupSizes, maximizedPaneId],