diff --git a/apps/web/e2e/helpers/dockview-resize.ts b/apps/web/e2e/helpers/dockview-resize.ts index 4b8f0ab7b4..96bf21b05f 100644 --- a/apps/web/e2e/helpers/dockview-resize.ts +++ b/apps/web/e2e/helpers/dockview-resize.ts @@ -9,15 +9,16 @@ type Box = { x: number; y: number; width: number; height: number }; export const WIDE_VIEWPORT = { width: 1600, height: 900 }; -/** Open a fresh task at the wide viewport and wait for the desktop dockview - * layout to settle. Shared by every pane-resize spec. */ +/** Open a fresh task at the given (or wide-default) viewport and wait for + * the desktop dockview layout to settle. Shared by every pane-resize spec. */ export async function openWideTask( page: Page, apiClient: ApiClient, seedData: SeedData, title: string, + viewport: { width: number; height: number } = WIDE_VIEWPORT, ): Promise { - await page.setViewportSize(WIDE_VIEWPORT); + await page.setViewportSize(viewport); const task = await apiClient.createTaskWithAgent( seedData.workspaceId, title, @@ -154,27 +155,6 @@ export async function getColumnSashIndex(page: Page, column: "sidebar" | "right" }); } -/** Pre-populate the pinned-defaults sessionStorage slot before navigation so - * the dockview store seeds with the desired widths on first build. */ -export async function setPinnedDefaultsViaStorage( - page: Page, - defaults: { sidebar?: number; right?: number }, -): Promise { - await page.evaluate((d) => { - window.sessionStorage.setItem("kandev.dockview.pinned-defaults", JSON.stringify(d)); - }, defaults); -} - -/** Read the pinned-defaults sessionStorage slot. */ -export async function readPinnedDefaultsFromStorage( - page: Page, -): Promise<{ sidebar?: number; right?: number }> { - return page.evaluate(() => { - const raw = window.sessionStorage.getItem("kandev.dockview.pinned-defaults"); - return raw ? (JSON.parse(raw) as { sidebar?: number; right?: number }) : {}; - }); -} - /** Expect a width to be approximately equal (±slack px) to a target. */ export function expectApproxWidth(actual: number, target: number, slack = 8): void { expect( diff --git a/apps/web/e2e/tests/layout/pane-resize-edge-cases.spec.ts b/apps/web/e2e/tests/layout/pane-resize-edge-cases.spec.ts index 1384163546..e6f548dba1 100644 --- a/apps/web/e2e/tests/layout/pane-resize-edge-cases.spec.ts +++ b/apps/web/e2e/tests/layout/pane-resize-edge-cases.spec.ts @@ -21,27 +21,6 @@ test.describe("Pane resize edge cases", () => { await session.expectLayoutHealthy(); }); - test("rapid resizes persist the final value across reload", async ({ - testPage, - apiClient, - seedData, - }) => { - const session = await openWideTask(testPage, apiClient, seedData, "Edge rapid resize"); - // Five quick resizes; the final value should win. - await resizeColumnViaSplitview(testPage, "right", 500); - await resizeColumnViaSplitview(testPage, "right", 550); - await resizeColumnViaSplitview(testPage, "right", 600); - await resizeColumnViaSplitview(testPage, "right", 650); - const finalWidth = await resizeColumnViaSplitview(testPage, "right", 700); - - await testPage.reload(); - await session.waitForLoad(); - await session.waitForDockviewReady(); - - const afterReload = await getDockviewGroupWidth(testPage, "files"); - expectApproxWidth(afterReload, finalWidth, 15); - }); - test("resize during maximize does not corrupt the pre-maximize width", async ({ testPage, apiClient, diff --git a/apps/web/e2e/tests/layout/pane-resize-right.spec.ts b/apps/web/e2e/tests/layout/pane-resize-right.spec.ts index 652a4f4ae4..b0115bba01 100644 --- a/apps/web/e2e/tests/layout/pane-resize-right.spec.ts +++ b/apps/web/e2e/tests/layout/pane-resize-right.spec.ts @@ -1,11 +1,9 @@ import { test, expect } from "../../fixtures/test-base"; -import { SessionPage } from "../../pages/session-page"; import { WIDE_VIEWPORT, openWideTask, expectApproxWidth, getDockviewGroupWidth, - readPinnedDefaultsFromStorage, resizeColumnViaSplitview, } from "../../helpers/dockview-resize"; @@ -27,7 +25,7 @@ test.describe("Right pane resize — viewport-proportional cap", () => { expect(actual).toBeLessThanOrEqual(cap + 10); }); - test("user width survives reload (sessionStorage round-trip)", async ({ + test("user width survives reload (per-env sessionStorage round-trip)", async ({ testPage, apiClient, seedData, @@ -43,47 +41,6 @@ test.describe("Right pane resize — viewport-proportional cap", () => { expectApproxWidth(after, before, 12); }); - test("user width persists into sessionStorage pinned-defaults slot", async ({ - testPage, - apiClient, - seedData, - }) => { - await openWideTask(testPage, apiClient, seedData, "Right pinned defaults"); - const live = await resizeColumnViaSplitview(testPage, "right", 650); - const defaults = await readPinnedDefaultsFromStorage(testPage); - expect(defaults.right).toBeDefined(); - expectApproxWidth(defaults.right ?? 0, live, 12); - }); - - test("new task adopts user's preferred right width", async ({ - testPage, - apiClient, - seedData, - }) => { - const session = await openWideTask(testPage, apiClient, seedData, "Right propagate A"); - const widthA = await resizeColumnViaSplitview(testPage, "right", 620); - void session; - - const taskB = await apiClient.createTaskWithAgent( - seedData.workspaceId, - "Right propagate B", - seedData.agentProfileId, - { - description: "/e2e:simple-message", - workflow_id: seedData.workflowId, - workflow_step_id: seedData.startStepId, - repository_ids: [seedData.repositoryId], - }, - ); - await testPage.goto(`/t/${taskB.id}`); - const sessionB = new SessionPage(testPage); - await sessionB.waitForLoad(); - await sessionB.waitForDockviewReady(); - - const widthB = await getDockviewGroupWidth(testPage, "files"); - expectApproxWidth(widthB, widthA, 20); - }); - test("viewport shrink re-clamps an over-cap pinned width", async ({ testPage, apiClient, diff --git a/apps/web/e2e/tests/layout/pane-resize-sidebar.spec.ts b/apps/web/e2e/tests/layout/pane-resize-sidebar.spec.ts index b52aed64aa..9bad07c90b 100644 --- a/apps/web/e2e/tests/layout/pane-resize-sidebar.spec.ts +++ b/apps/web/e2e/tests/layout/pane-resize-sidebar.spec.ts @@ -1,8 +1,4 @@ -import { type Page } from "@playwright/test"; import { test, expect } from "../../fixtures/test-base"; -import type { SeedData } from "../../fixtures/test-base"; -import type { ApiClient } from "../../helpers/api-client"; -import { SessionPage } from "../../pages/session-page"; import { openWideTask, expectApproxWidth, @@ -10,32 +6,6 @@ import { resizeColumnViaSplitview, } from "../../helpers/dockview-resize"; -async function createTaskAt( - page: Page, - apiClient: ApiClient, - seedData: SeedData, - viewport: { width: number; height: number }, - title: string, -): Promise { - await page.setViewportSize(viewport); - const task = await apiClient.createTaskWithAgent( - seedData.workspaceId, - title, - seedData.agentProfileId, - { - description: "/e2e:simple-message", - workflow_id: seedData.workflowId, - workflow_step_id: seedData.startStepId, - repository_ids: [seedData.repositoryId], - }, - ); - await page.goto(`/t/${task.id}`); - const session = new SessionPage(page); - await session.waitForLoad(); - await session.waitForDockviewReady(); - return session; -} - test.describe("Sidebar resize — viewport-proportional cap", () => { test("resizes past the old 350px hard cap", async ({ testPage, apiClient, seedData }) => { await openWideTask(testPage, apiClient, seedData, "Sidebar past cap"); @@ -48,13 +18,10 @@ test.describe("Sidebar resize — viewport-proportional cap", () => { apiClient, seedData, }) => { - await createTaskAt( - testPage, - apiClient, - seedData, - { width: 1200, height: 800 }, - "Sidebar cap narrow", - ); + await openWideTask(testPage, apiClient, seedData, "Sidebar cap narrow", { + width: 1200, + height: 800, + }); const actual = await resizeColumnViaSplitview(testPage, "sidebar", 5000); expect(actual).toBeLessThanOrEqual(Math.max(350, Math.round(1200 * 0.3)) + 10); }); @@ -71,35 +38,6 @@ test.describe("Sidebar resize — viewport-proportional cap", () => { expectApproxWidth(after, before, 12); }); - test("user width propagates to brand-new task envs", async ({ - testPage, - apiClient, - seedData, - }) => { - const session = await openWideTask(testPage, apiClient, seedData, "Sidebar propagate A"); - const widthA = await resizeColumnViaSplitview(testPage, "sidebar", 450); - void session; - - const taskB = await apiClient.createTaskWithAgent( - seedData.workspaceId, - "Sidebar propagate B", - seedData.agentProfileId, - { - description: "/e2e:simple-message", - workflow_id: seedData.workflowId, - workflow_step_id: seedData.startStepId, - repository_ids: [seedData.repositoryId], - }, - ); - await testPage.goto(`/t/${taskB.id}`); - const sessionB = new SessionPage(testPage); - await sessionB.waitForLoad(); - await sessionB.waitForDockviewReady(); - - const widthB = await getDockviewGroupWidth(testPage, "sidebar"); - expectApproxWidth(widthB, widthA, 20); - }); - test("toggle sidebar off+on preserves the last user width", async ({ testPage, apiClient, diff --git a/apps/web/lib/local-storage.ts b/apps/web/lib/local-storage.ts index 60f6530c39..ce93b6bdaf 100644 --- a/apps/web/lib/local-storage.ts +++ b/apps/web/lib/local-storage.ts @@ -332,44 +332,6 @@ export function removeEnvMaximizeState(envId: string): void { } } -// --- Dockview pinned-column user defaults (sessionStorage) --- -// Stores the user's preferred sidebar / right widths so brand-new task envs -// adopt them instead of falling back to the ratio-based default. Per-env saved -// layouts still win when present (this only seeds the in-memory pinnedWidths -// map for envs with no saved layout yet). -const DOCKVIEW_PINNED_DEFAULTS_KEY = "kandev.dockview.pinned-defaults"; - -export type PinnedDefaults = { sidebar?: number; right?: number }; - -function isPinnedDefaults(value: unknown): value is PinnedDefaults { - if (!value || typeof value !== "object") return false; - const v = value as Record; - const okSidebar = v.sidebar === undefined || typeof v.sidebar === "number"; - const okRight = v.right === undefined || typeof v.right === "number"; - return okSidebar && okRight; -} - -export function getPinnedDefaults(): PinnedDefaults { - if (typeof window === "undefined") return {}; - try { - const raw = window.sessionStorage.getItem(DOCKVIEW_PINNED_DEFAULTS_KEY); - if (!raw) return {}; - const parsed: unknown = JSON.parse(raw); - return isPinnedDefaults(parsed) ? parsed : {}; - } catch { - return {}; - } -} - -export function setPinnedDefaults(defaults: PinnedDefaults): void { - if (typeof window === "undefined") return; - try { - window.sessionStorage.setItem(DOCKVIEW_PINNED_DEFAULTS_KEY, JSON.stringify(defaults)); - } catch { - // Ignore write failures - } -} - // PR panel "offered" flag — tracks whether the auto-show PR panel was offered // for a session. If offered and then closed by the user, we respect the dismissal. const PR_PANEL_OFFERED_PREFIX = "kandev.pr-panel-offered."; diff --git a/apps/web/lib/state/dockview-store.ts b/apps/web/lib/state/dockview-store.ts index 7febe428ff..3287f5c4db 100644 --- a/apps/web/lib/state/dockview-store.ts +++ b/apps/web/lib/state/dockview-store.ts @@ -6,8 +6,6 @@ import { getEnvMaximizeState, setEnvMaximizeState, removeEnvMaximizeState, - getPinnedDefaults, - setPinnedDefaults, } from "@/lib/local-storage"; import { applyLayoutFixups, focusOrAddPanel } from "./dockview-layout-builders"; import { @@ -670,14 +668,7 @@ function performBuildDefault( ): void { const { userDefaultLayout } = get(); const intent = intentName ? resolveNamedIntent(intentName) : null; - // Seed pinned widths from the user's saved defaults so brand-new task envs - // open at the widths the user last resized to. getPinnedWidth still clamps - // them to the runtime cap (so a stored value beyond the new viewport cap - // shrinks gracefully). - const defaults = getPinnedDefaults(); const freshPinned = new Map(); - if (defaults.sidebar !== undefined) freshPinned.set("sidebar", defaults.sidebar); - if (defaults.right !== undefined) freshPinned.set("right", defaults.right); // Capture dimensions before layout change — api.width can become stale // after fromJSON inside applyLayout const { width: safeWidth, height: safeHeight } = measureDockviewContainer(api); @@ -783,20 +774,6 @@ export const useDockviewStore = create((set, get) => ({ m.set(columnId, width); return { pinnedWidths: m }; }); - // Mirror sidebar / right widths into sessionStorage so brand-new envs - // (no saved per-env layout yet) open at the user's preferred widths - // instead of the ratio-based default. - // - // Skip when the column is hidden — auto-sync infers the sidebar slot - // from grid index 0, which is the *center* column when the sidebar is - // hidden. Without this guard, a center-column resize would overwrite - // the user's stored sidebar width. - if (columnId !== "sidebar" && columnId !== "right") return; - const live = get(); - const visible = columnId === "sidebar" ? live.sidebarVisible : live.rightPanelsVisible; - if (!visible) return; - const current = getPinnedDefaults(); - setPinnedDefaults({ ...current, [columnId]: width }); }, userDefaultLayout: null, setUserDefaultLayout: (layout) => set({ userDefaultLayout: layout }), diff --git a/apps/web/lib/state/layout-manager/constants.ts b/apps/web/lib/state/layout-manager/constants.ts index 044056451c..3e354a933f 100644 --- a/apps/web/lib/state/layout-manager/constants.ts +++ b/apps/web/lib/state/layout-manager/constants.ts @@ -3,12 +3,15 @@ import type { LayoutPanel } from "./types"; // Layout sizing constants (single source of truth). // Pinned column max width is computed at runtime — see ./caps.ts. // -// Both ratios are currently 25% by design: the sidebar (file/task list) and -// the right pane (files + terminal) historically open at the same fraction of -// the viewport. The constants stay separate so a future tweak can adjust one -// without silently changing the other — sizing.ts dispatches by column id. -export const LAYOUT_SIDEBAR_RATIO = 2.5 / 10; -export const LAYOUT_RIGHT_RATIO = 2.5 / 10; +// Ratio applied to viewport width to compute the initial default for any +// pinned column when no user override exists. Sidebar and right pane share +// the same initial fraction today; if they need to diverge later, split this +// into per-column ratios at that time. +export const LAYOUT_INITIAL_RATIO = 2.5 / 10; +/** @deprecated use LAYOUT_INITIAL_RATIO. */ +export const LAYOUT_SIDEBAR_RATIO = LAYOUT_INITIAL_RATIO; +/** @deprecated use LAYOUT_INITIAL_RATIO. */ +export const LAYOUT_RIGHT_RATIO = LAYOUT_INITIAL_RATIO; // Well-known group/panel IDs export const SIDEBAR_GROUP = "group-sidebar";