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
This commit is contained in:
paseo-bot[bot]
2026-10-13 17:26:13 +02:00
parent 5805b776c2
commit 6eee6a6b3a
2 changed files with 106 additions and 2 deletions
@@ -0,0 +1,88 @@
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(page: Page, divider: Locator, deltaX: number): Promise<void> {
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: Page, index: number): Promise<void> {
const divider = splitDividers(page).nth(index);
const before = await dividerX(divider);
await dragDivider(page, 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: 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, 1);
await splitRight(page, 2);
await splitRight(page, 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, 1);
await expectDividerFollowsDrag(page, 0);
await splitRight(page, 2);
await splitRight(page, 3);
for (const index of [0, 1, 2]) {
await expectDividerFollowsDrag(page, index);
}
} finally {
await workspace.cleanup();
}
});
});
@@ -872,6 +872,19 @@ function SplitGroupChild({
);
}
/**
* Sizes from a divider drag are kept per group id, apart from the layout tree. A group keeps its
* id when panes are added or removed, so the dragged sizes only apply while they still have one
* entry per child; otherwise the dividers past the end of the list could not move.
*/
function resolveGroupSizes(
layoutSizes: number[],
draggedSizes: number[] | undefined,
childCount: number,
): number[] {
return draggedSizes?.length === childCount ? draggedSizes : layoutSizes;
}
/**
* Flex grow per child, renormalized so the visible ones always sum to 1.
*
@@ -968,8 +981,11 @@ function SplitNodeView({
groupId ? state.splitSizesByWorkspace[workspaceKey]?.[groupId] : undefined,
);
const groupChildren = node.kind === "group" ? node.group.children : EMPTY_SPLIT_NODES;
const groupSizes =
storedGroupSizes ?? (node.kind === "group" ? node.group.sizes : EMPTY_SPLIT_SIZES);
const groupSizes = resolveGroupSizes(
node.kind === "group" ? node.group.sizes : EMPTY_SPLIT_SIZES,
storedGroupSizes,
groupChildren.length,
);
const visibleFlex = useMemo(
() => resolveVisibleGroupFlex(groupChildren, groupSizes, maximizedPaneId),
[groupChildren, groupSizes, maximizedPaneId],