Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -53,3 +53,6 @@ Thumbs.db
# electron-forge / vite build output
.vite/
dist-electron/

# playwright artifacts
frontend/test-results/
6 changes: 6 additions & 0 deletions DESIGN.md
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
25 changes: 25 additions & 0 deletions frontend/e2e/history-nav.spec.ts
Original file line number Diff line number Diff line change
@@ -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/);
});
26 changes: 26 additions & 0 deletions frontend/e2e/inspector-toggle.spec.ts
Original file line number Diff line number Diff line change
@@ -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();
});
6 changes: 5 additions & 1 deletion frontend/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
11 changes: 11 additions & 0 deletions frontend/src/renderer/components/DashboardSubhead.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<div className="flex items-baseline gap-3 px-[18px] pt-[22px]">
<h1 className="text-[21px] font-bold tracking-[-0.025em] text-foreground">{title}</h1>
{typeof count === "number" && <span className="font-mono text-[13px] text-passive">{count}</span>}
<span className="text-[12.5px] text-passive">{subtitle}</span>
</div>
);
}
139 changes: 0 additions & 139 deletions frontend/src/renderer/components/DashboardTopbar.tsx

This file was deleted.

3 changes: 1 addition & 2 deletions frontend/src/renderer/components/ProjectSettingsForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { useState } from "react";
import type { components } from "../../api/schema";
import { apiClient, apiErrorMessage } from "../lib/api-client";
import { workspaceQueryKey } from "../hooks/useWorkspaceQuery";
import { DashboardSubhead, DashboardTopbar } from "./DashboardTopbar";
import { DashboardSubhead } from "./DashboardSubhead";
import { Button } from "./ui/button";
import { Card, CardContent, CardHeader, CardTitle } from "./ui/card";
import { Label } from "./ui/label";
Expand Down Expand Up @@ -51,7 +51,6 @@ export function ProjectSettingsForm({ projectId }: { projectId: string }) {

return (
<div className="flex h-full min-h-0 flex-col bg-background text-foreground">
<DashboardTopbar activeTab="coding" projectId={projectId} projectLabel={query.data.name} />
<DashboardSubhead title="Settings" subtitle={query.data.path} />
<div className="min-h-0 flex-1 overflow-y-auto p-[18px]">
<SettingsBody
Expand Down
3 changes: 1 addition & 2 deletions frontend/src/renderer/components/PullRequestsPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { useState } from "react";
import { apiClient, apiErrorMessage } from "../lib/api-client";
import { useWorkspaceQuery, workspaceQueryKey } from "../hooks/useWorkspaceQuery";
import type { WorkspaceSession } from "../types/workspace";
import { DashboardSubhead, DashboardTopbar } from "./DashboardTopbar";
import { DashboardSubhead } from "./DashboardSubhead";
import { Badge } from "./ui/badge";
import { Button } from "./ui/button";
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "./ui/table";
Expand Down Expand Up @@ -46,7 +46,6 @@ export function PullRequestsPage() {

return (
<div className="flex h-full min-h-0 flex-col bg-background text-foreground">
<DashboardTopbar />
<DashboardSubhead
title="Pull requests"
subtitle="Open PRs across every agent session, ready to resolve and merge."
Expand Down
Loading