diff --git a/apps/web/src/resizable-pane.test.ts b/apps/web/src/resizable-pane.test.ts index 28835a5f..f00e98a2 100644 --- a/apps/web/src/resizable-pane.test.ts +++ b/apps/web/src/resizable-pane.test.ts @@ -16,9 +16,11 @@ describe("bounded pane resizing", () => { expect(clampPane(401, 250, 400)).toBe(400); }); - it("moves each pane edge in its screen direction", () => { - expect(resizeDelta("left", 16)).toBe(16); - expect(resizeDelta("right", 16)).toBe(-16); + it("keeps each pane edge under the pointer", () => { + expect(resizeDelta("left", -16)).toBe(16); + expect(resizeDelta("left", 16)).toBe(-16); + expect(resizeDelta("right", 16)).toBe(16); + expect(resizeDelta("right", -16)).toBe(-16); }); it("restores a valid saved width and rejects invalid persisted values", () => { @@ -58,8 +60,10 @@ describe("bounded pane resizing", () => { it("maps keyboard commands to bounded pane movements", () => { let bounds = { min: 304, max: 400 }; - expect(keyboardPaneDelta("left", "ArrowRight", false, 320, bounds)).toBe(16); - expect(keyboardPaneDelta("right", "ArrowRight", true, 384, bounds)).toBe(-64); + expect(keyboardPaneDelta("left", "ArrowLeft", false, 320, bounds)).toBe(16); + expect(keyboardPaneDelta("left", "ArrowRight", true, 384, bounds)).toBe(-64); + expect(keyboardPaneDelta("right", "ArrowRight", false, 320, bounds)).toBe(16); + expect(keyboardPaneDelta("right", "ArrowLeft", true, 384, bounds)).toBe(-64); expect(keyboardPaneDelta("left", "Home", false, 360, bounds)).toBe(-56); expect(keyboardPaneDelta("right", "End", false, 360, bounds)).toBe(40); expect(keyboardPaneDelta("left", "Tab", false, 360, bounds)).toBeUndefined(); diff --git a/apps/web/src/resizable-pane.tsx b/apps/web/src/resizable-pane.tsx index 16af9985..76b12020 100644 --- a/apps/web/src/resizable-pane.tsx +++ b/apps/web/src/resizable-pane.tsx @@ -15,9 +15,12 @@ export function clampPane(value: number, min: number, max: number): number { return Math.min(max, Math.max(min, value)); } -/** Translates an on-screen drag into the width change for the pane's edge. */ +/** + * Translates an on-screen drag into a width change for a pane resized from its `side` edge. + * Moving a left edge rightward narrows the pane; moving a right edge rightward widens it. + */ export function resizeDelta(side: PaneSide, delta: number): number { - return side === "left" ? delta : -delta; + return side === "left" ? -delta : delta; } export function restorePaneWidth(stored: string | null, { initial, min, max }: PaneBounds): number { diff --git a/e2e/shell.e2e.ts b/e2e/shell.e2e.ts index b544dc79..9011fff6 100644 --- a/e2e/shell.e2e.ts +++ b/e2e/shell.e2e.ts @@ -77,11 +77,15 @@ test("the chat rail has its own resize control", async ({ join, page }) => { let beforeValue = Number(await handle.getAttribute("aria-valuenow")); let before = (await box(rail)).width; - await handle.press("ArrowRight"); - await handle.press("ArrowRight"); + await handle.press("ArrowLeft"); + await handle.press("ArrowLeft"); expect((await box(rail)).width).toBeGreaterThan(before); expect(Number(await handle.getAttribute("aria-valuenow"))).toBeGreaterThan(beforeValue); + let widened = (await box(rail)).width; + await handle.press("ArrowRight"); + expect((await box(rail)).width).toBeLessThan(widened); + await handle.press("End"); let maximum = await handle.getAttribute("aria-valuemax"); if (maximum === null) throw new Error("Chat resize handle must expose aria-valuemax"); @@ -91,6 +95,26 @@ test("the chat rail has its own resize control", async ({ join, page }) => { ); }); +test("the chat rail edge follows the pointer", async ({ join, page }) => { + await join("ana"); + + let rail = page.getByRole("complementary", { name: "Chat" }); + let handle = page.getByRole("separator", { name: "Resize chat" }); + let start = await box(handle); + let y = start.y + start.height / 2; + let before = (await box(rail)).width; + + await handle.hover(); + await page.mouse.down(); + await page.mouse.move(start.x - 40, y, { steps: 4 }); + let widened = (await box(rail)).width; + expect(widened).toBeGreaterThan(before); + + await page.mouse.move(start.x + 20, y, { steps: 4 }); + await page.mouse.up(); + expect((await box(rail)).width).toBeLessThan(widened); +}); + test("the compact workspace keeps the document unobstructed", async ({ join, page }) => { await page.setViewportSize({ width: 640, height: 800 }); await join("ana"); diff --git a/e2e/sidecar.e2e.ts b/e2e/sidecar.e2e.ts index 141e0518..06fd1116 100644 --- a/e2e/sidecar.e2e.ts +++ b/e2e/sidecar.e2e.ts @@ -669,8 +669,14 @@ test("decision cards save independently with progressive custom answers", async await added.check(); await scope.getByRole("checkbox", { name: /^Anchors/ }).check(); await saveScope.click(); - await expect(questionnaire(page).filter({ hasText: "Which of these belong in the first cut?" })) - .toContainText("Only collaborative anchors"); + let allResolved = page.getByRole("button", { name: "2 resolved" }); + await expect(allResolved).toBeVisible(); + await allResolved.click(); + let resolvedScope = questionnaire(page).filter({ + hasText: "Which of these belong in the first cut?", + }); + await expect(resolvedScope).toContainText("Only collaborative anchors"); + await expect(resolvedScope).toContainText("Answered by @ana"); }); test("Save and Next wait for a chosen answer", async ({ join, seed }) => {