diff --git a/.gitignore b/.gitignore index d0b25c0..0a8225e 100644 --- a/.gitignore +++ b/.gitignore @@ -53,3 +53,6 @@ Thumbs.db # electron-forge / vite build output .vite/ dist-electron/ + +# playwright artifacts +frontend/test-results/ diff --git a/DESIGN.md b/DESIGN.md index 8987585..4778112 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -41,6 +41,12 @@ resizable`, react-resizable-panels v4 `collapsible` panel + imperative API, content keeps a stable min-width (yyork-style, no mid-animation reflow). Toggled by a `PanelRight` icon button in the session topbar and ⌘⇧B; open state + split width persist. The AO reference keeps the rail always visible. +- **Approved divergence (2026-06-12):** the shell topbar spans the full window + width and the sidebar is pinned below it (`top-14`), so the sidebar's right + border stops at the header instead of cutting through the macOS traffic-light + strip (user-requested). The AO reference keeps a full-height sidebar with the + header beside it. On macOS the header always pads past the lights + TitlebarNav + cluster (`.is-under-titlebar-nav`, 180px). ## Product Context diff --git a/frontend/e2e/history-nav.spec.ts b/frontend/e2e/history-nav.spec.ts new file mode 100644 index 0000000..536d7d4 --- /dev/null +++ b/frontend/e2e/history-nav.spec.ts @@ -0,0 +1,25 @@ +import { expect, test } from "@playwright/test"; + +// Repro for the titlebar history arrows: navigate home → project → back, +// then the forward arrow must be enabled and actually traverse forward. +test("titlebar back/forward arrows traverse history", async ({ page }) => { + await page.goto("/"); + await expect(page.getByText("Projects")).toBeVisible(); + + // Navigate: home → session view (in-app push). + await page.getByRole("button", { name: "Open refactor-mux" }).click(); + await expect(page).toHaveURL(/sessions\/refactor-mux/); + + const back = page.getByRole("button", { name: "Go back" }); + const forward = page.getByRole("button", { name: "Go forward" }); + + await expect(forward).toBeDisabled(); + await expect(back).toBeEnabled(); + + await back.click(); + await expect(page).not.toHaveURL(/sessions\/refactor-mux/); + + await expect(forward).toBeEnabled(); + await forward.click(); + await expect(page).toHaveURL(/sessions\/refactor-mux/); +}); diff --git a/frontend/e2e/inspector-toggle.spec.ts b/frontend/e2e/inspector-toggle.spec.ts new file mode 100644 index 0000000..ff6460b --- /dev/null +++ b/frontend/e2e/inspector-toggle.spec.ts @@ -0,0 +1,26 @@ +import { expect, test } from "@playwright/test"; + +// Regression for the dead inspector toggle: rrp v4 derives panel sizes from +// the observed DOM layout, so the flex-grow transition animating an +// imperative expand()/collapse() fired onResize with transient sizes. +// SessionView mirrored every onResize into the ui-store, so a mid-collapse +// frame read as "dragged back open" and re-expanded the panel — the topbar +// button did nothing visible — and a mount-time 0-size event flipped fresh +// profiles to collapsed. Only real separator drags may write back; this needs +// the real rrp + CSS pipeline, which the mocked unit tests can't exercise. +test("topbar button collapses and reopens the inspector rail", async ({ page }) => { + await page.goto("/"); + await page.getByRole("button", { name: "Open refactor-mux" }).click(); + await expect(page).toHaveURL(/sessions\/refactor-mux/); + + // Fresh profile: the rail must mount open, not get toggled shut by + // mount-time layout events. + const inspector = page.locator("#inspector"); + await expect(inspector).toBeVisible(); + + await page.getByRole("button", { name: "Close inspector panel" }).click(); + await expect(inspector).toBeHidden(); + + await page.getByRole("button", { name: "Open inspector panel" }).click(); + await expect(inspector).toBeVisible(); +}); diff --git a/frontend/src/main.ts b/frontend/src/main.ts index 2d0d541..f0fe3de 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -88,7 +88,11 @@ function createWindow(): void { title: "Agent Orchestrator", backgroundColor: "#0f1014", titleBarStyle: "hiddenInset", - trafficLightPosition: { x: 14, y: 14 }, + // Lights visually centered at y=28 — the 56px topbar/.titlebar-nav center + // line — so lights + nav cluster + header content share one row. macOS + // draws the 12pt disc 2pt below the given y (measured: center = y + 8), + // hence 20, not 22. + trafficLightPosition: { x: 14, y: 20 }, webPreferences: { preload: preloadPath(), contextIsolation: true, diff --git a/frontend/src/renderer/components/DashboardSubhead.tsx b/frontend/src/renderer/components/DashboardSubhead.tsx new file mode 100644 index 0000000..d732922 --- /dev/null +++ b/frontend/src/renderer/components/DashboardSubhead.tsx @@ -0,0 +1,11 @@ +// The board subhead (mc-board .dashboard-main__subhead): a 21px bold title with +// a muted one-line subtitle, optionally a trailing count. +export function DashboardSubhead({ title, subtitle, count }: { title: string; subtitle: string; count?: number }) { + return ( +
Could not load reviews.
- ) : runs.length === 0 ? ( -- No review runs yet. Pick a worker and Run review. -
- ) : ( -no findings
- ) : ( -