From 5f8b9db6794690a759be9388602ca1a75e1bf153 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 660b5f4e6b..278317243a 100644 --- a/packages/app/src/components/split-container.tsx +++ b/packages/app/src/components/split-container.tsx @@ -48,6 +48,7 @@ import type { WorkspaceTabDescriptor } from "@/screens/workspace/workspace-tabs- import { collectAllPanes, createDefaultLayout, + selectExplorerSidebarPaneId, useWorkspaceLayoutStore, type SplitNode, type SplitPane, @@ -183,8 +184,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 mainRoot = useMemo(