From db75059922ff0341eafc9df1396b6ed7370af3de Mon Sep 17 00:00:00 2001 From: "paseo-bot[bot]" <266920839+paseo-bot[bot]@users.noreply.github.com> Date: Sun, 4 Oct 2026 01:32:12 +0200 Subject: [PATCH] fix(app): keep split dividers under the mouse before Explorer is first opened The main row only left the Explorer pane out once the Explorer had been shown, because the split container read the registered pane id directly. Before that, the hidden Explorer stayed in the row with its share of the sizes, and every divider drag moved about 1.28 times the mouse distance. Resolve the pane the same way the workspace screen does, so the default Explorer pane is left out from the start. Closes #5998 --- .../browser/split-pane-drag-tracking.spec.ts | 60 +++++++++++++++++++ .../app/src/components/split-container.tsx | 5 +- 2 files changed, 63 insertions(+), 2 deletions(-) create mode 100644 packages/app/e2e/browser/split-pane-drag-tracking.spec.ts diff --git a/packages/app/e2e/browser/split-pane-drag-tracking.spec.ts b/packages/app/e2e/browser/split-pane-drag-tracking.spec.ts new file mode 100644 index 0000000000..02ad911765 --- /dev/null +++ b/packages/app/e2e/browser/split-pane-drag-tracking.spec.ts @@ -0,0 +1,60 @@ +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; +const TOLERANCE_PX = 2; + +function splitDividers(page: Page): Locator { + return page.getByTestId("workspace-split-resize-handle").filter({ visible: true }); +} + +async function dividerX(divider: Locator): Promise { + 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 { + 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, y, { steps: 8 }); + await page.mouse.up(); +} + +test.describe("Split pane drag tracking", () => { + test("a divider moves as far as the mouse in the main row", async ({ page }) => { + await page.setViewportSize({ width: 1600, height: 900 }); + const workspace = await seedWorkspace({ repoPrefix: "split-drag-tracking-" }); + try { + await gotoWorkspace(page, workspace.workspaceId); + await waitForWorkspaceTabsVisible(page); + await clickNewChat(page); + for (const expectedDividers of [1, 2, 3]) { + await runWorkspaceActionFromCommandCenter(page, "Split pane right"); + await expect(splitDividers(page)).toHaveCount(expectedDividers); + } + + for (const index of [0, 1, 2]) { + const divider = splitDividers(page).nth(index); + const before = await dividerX(divider); + await dragDivider(divider, DRAG_DISTANCE); + await expect + .poll(async () => Math.abs((await dividerX(divider)) - before - DRAG_DISTANCE), { + message: `divider ${index + 1} should stay under the mouse`, + }) + .toBeLessThanOrEqual(TOLERANCE_PX); + } + } finally { + await workspace.cleanup(); + } + }); +}); diff --git a/packages/app/src/components/split-container.tsx b/packages/app/src/components/split-container.tsx index 9705f3be15..b06d22438f 100644 --- a/packages/app/src/components/split-container.tsx +++ b/packages/app/src/components/split-container.tsx @@ -82,6 +82,7 @@ import type { WorkspaceTabDescriptor } from "@/screens/workspace/workspace-tabs- import { createDefaultLayout, findPaneById, + selectExplorerSidebarPaneId, useWorkspaceLayoutStore, type SplitNode, type SplitPane, @@ -351,8 +352,8 @@ export function SplitContainer({ } | null>(null); const maximizedPaneId = maximizedPane?.workspaceKey === workspaceKey ? maximizedPane.paneId : null; - const explorerSidebarPaneId = useWorkspaceLayoutStore( - (state) => state.explorerSidebarPaneIdByWorkspace[workspaceKey] ?? null, + const explorerSidebarPaneId = useWorkspaceLayoutStore((state) => + selectExplorerSidebarPaneId(state, workspaceKey), ); const sensors = useSensors(