-
Agent Orchestrator
-
- No session selected. Pick a worker to attach its terminal.
+
+ {showExitedState && (
+
+ )}
+
+
+ {showEmptyState && (
+
+
+
Agent Orchestrator
+
+ No session selected. Pick a worker to attach its terminal.
+
-
- )}
- {banner && (
-
- {banner}
-
- )}
+ )}
+ {banner && (
+
+ {banner}
+
+ )}
+
+
+ );
+}
+
+type TerminalEndedStripProps = {
+ canRestore: boolean;
+ error?: string;
+ isRestoring: boolean;
+ onRestore: () => void;
+ variant: "reviewer" | "session";
+};
+
+function TerminalEndedStrip({ canRestore, error, isRestoring, onRestore, variant }: TerminalEndedStripProps) {
+ const message = canRestore
+ ? "Restore the session to attach a live terminal and continue writing."
+ : variant === "reviewer"
+ ? "This reviewer terminal has ended. Re-run review from the summary panel, or switch back to the agent terminal."
+ : "This terminal process ended, but the session is not marked terminated yet.";
+
+ return (
+
+
+
+
+ Terminal ended
+
+
{message}
+
+ {error &&
{error}
}
+ {canRestore && (
+
+ )}
+
);
}
diff --git a/frontend/src/renderer/lib/mock-data.ts b/frontend/src/renderer/lib/mock-data.ts
index d838046b0..df97916c9 100644
--- a/frontend/src/renderer/lib/mock-data.ts
+++ b/frontend/src/renderer/lib/mock-data.ts
@@ -1,5 +1,8 @@
import type { WorkspaceSummary } from "../types/workspace";
+const now = new Date().toISOString();
+const hoursAgo = (hours: number) => new Date(Date.now() - hours * 60 * 60 * 1000).toISOString();
+
export const mockWorkspaces: WorkspaceSummary[] = [
{
id: "api-gateway",
@@ -11,12 +14,12 @@ export const mockWorkspaces: WorkspaceSummary[] = [
terminalHandleId: "refactor-mux/terminal_0",
workspaceId: "api-gateway",
workspaceName: "api-gateway",
- title: "refactor-mux",
+ title: "Split terminal mux responsibilities",
provider: "claude-code",
branch: "feat/refactor-mux",
status: "working",
- updatedAt: new Date().toISOString(),
- createdAt: new Date(Date.now() - 4 * 60 * 60 * 1000).toISOString(),
+ updatedAt: now,
+ createdAt: hoursAgo(4),
changedFiles: [
{
path: "internal/mux/terminal_mux.go",
@@ -26,6 +29,30 @@ export const mockWorkspaces: WorkspaceSummary[] = [
],
commitMessage: "refactor terminal mux",
},
+ {
+ id: "fix-auth-timeouts",
+ workspaceId: "api-gateway",
+ workspaceName: "api-gateway",
+ title: "fix auth timeout retry loop",
+ provider: "codex",
+ branch: "fix/auth-timeouts",
+ status: "ci_failed",
+ updatedAt: hoursAgo(1),
+ createdAt: hoursAgo(6),
+ pullRequest: { number: 184, state: "open" },
+ },
+ {
+ id: "rate-limit-headers",
+ workspaceId: "api-gateway",
+ workspaceName: "api-gateway",
+ title: "add rate limit headers",
+ provider: "opencode",
+ branch: "feat/rate-limit-headers",
+ status: "review_pending",
+ updatedAt: hoursAgo(2),
+ createdAt: hoursAgo(9),
+ pullRequest: { number: 185, state: "open" },
+ },
],
},
{
@@ -37,12 +64,159 @@ export const mockWorkspaces: WorkspaceSummary[] = [
id: "fix-webgl-fallback",
workspaceId: "webgl-preview",
workspaceName: "webgl-preview",
- title: "fix-webgl-fallback",
+ title: "Restore fallback renderer after WebGL init fails",
provider: "codex",
branch: "fix/webgl-fallback",
status: "needs_input",
- updatedAt: new Date().toISOString(),
- createdAt: new Date(Date.now() - 4 * 60 * 60 * 1000).toISOString(),
+ updatedAt: now,
+ createdAt: hoursAgo(4),
+ },
+ {
+ id: "shader-cache",
+ workspaceId: "webgl-preview",
+ workspaceName: "webgl-preview",
+ title: "cache compiled shader programs",
+ provider: "claude-code",
+ branch: "feat/shader-cache",
+ status: "working",
+ updatedAt: hoursAgo(0.5),
+ createdAt: hoursAgo(2),
+ changedFiles: [
+ { path: "src/render/shader-cache.ts", additions: 86, deletions: 12 },
+ { path: "src/render/webgl-context.ts", additions: 24, deletions: 5 },
+ ],
+ },
+ {
+ id: "texture-leak",
+ workspaceId: "webgl-preview",
+ workspaceName: "webgl-preview",
+ title: "stop texture leak on scene reload",
+ provider: "codex",
+ branch: "fix/texture-leak",
+ status: "ci_failed",
+ updatedAt: hoursAgo(1.5),
+ createdAt: hoursAgo(7),
+ pullRequest: { number: 51, state: "open" },
+ },
+ {
+ id: "review-camera-pan",
+ workspaceId: "webgl-preview",
+ workspaceName: "webgl-preview",
+ title: "smooth camera pan controls",
+ provider: "aider",
+ branch: "feat/camera-pan",
+ status: "review_pending",
+ updatedAt: hoursAgo(3),
+ createdAt: hoursAgo(10),
+ pullRequest: { number: 52, state: "open" },
+ },
+ {
+ id: "draft-webgpu-probe",
+ workspaceId: "webgl-preview",
+ workspaceName: "webgl-preview",
+ title: "probe WebGPU support before init",
+ provider: "opencode",
+ branch: "feat/webgpu-probe",
+ status: "draft",
+ updatedAt: hoursAgo(5),
+ createdAt: hoursAgo(12),
+ pullRequest: { number: 53, state: "draft" },
+ },
+ {
+ id: "merge-frame-stats",
+ workspaceId: "webgl-preview",
+ workspaceName: "webgl-preview",
+ title: "ship frame statistics overlay",
+ provider: "codex",
+ branch: "feat/frame-stats",
+ status: "mergeable",
+ updatedAt: hoursAgo(0.25),
+ createdAt: hoursAgo(14),
+ pullRequest: { number: 54, state: "open" },
+ },
+ {
+ id: "approved-pixel-ratio",
+ workspaceId: "webgl-preview",
+ workspaceName: "webgl-preview",
+ title: "respect device pixel ratio",
+ provider: "claude-code",
+ branch: "fix/device-pixel-ratio",
+ status: "approved",
+ updatedAt: hoursAgo(2.5),
+ createdAt: hoursAgo(16),
+ pullRequest: { number: 55, state: "open" },
+ },
+ {
+ id: "input-pointer-lock",
+ workspaceId: "webgl-preview",
+ workspaceName: "webgl-preview",
+ title: "pointer lock escape handling",
+ provider: "codex",
+ branch: "fix/pointer-lock",
+ status: "changes_requested",
+ updatedAt: hoursAgo(4),
+ createdAt: hoursAgo(18),
+ pullRequest: { number: 56, state: "open" },
+ },
+ ],
+ },
+ {
+ id: "mobile-shell",
+ name: "mobile-shell",
+ path: "/Users/me/mobile-shell",
+ sessions: [
+ {
+ id: "nav-gesture",
+ workspaceId: "mobile-shell",
+ workspaceName: "mobile-shell",
+ title: "repair back swipe gesture",
+ provider: "codex",
+ branch: "fix/back-swipe",
+ status: "working",
+ updatedAt: hoursAgo(0.75),
+ createdAt: hoursAgo(3),
+ },
+ {
+ id: "profile-sheet",
+ workspaceId: "mobile-shell",
+ workspaceName: "mobile-shell",
+ title: "profile sheet accessibility pass",
+ provider: "claude-code",
+ branch: "fix/profile-sheet-a11y",
+ status: "mergeable",
+ updatedAt: hoursAgo(1.25),
+ createdAt: hoursAgo(8),
+ pullRequest: { number: 92, state: "open" },
+ },
+ ],
+ },
+ {
+ id: "billing-portal",
+ name: "billing-portal",
+ path: "/Users/me/billing-portal",
+ sessions: [
+ {
+ id: "invoice-export",
+ workspaceId: "billing-portal",
+ workspaceName: "billing-portal",
+ title: "invoice CSV export",
+ provider: "opencode",
+ branch: "feat/invoice-export",
+ status: "review_pending",
+ updatedAt: hoursAgo(2.25),
+ createdAt: hoursAgo(11),
+ pullRequest: { number: 117, state: "open" },
+ },
+ {
+ id: "tax-id-validation",
+ workspaceId: "billing-portal",
+ workspaceName: "billing-portal",
+ title: "tax id validation errors",
+ provider: "codex",
+ branch: "fix/tax-id-validation",
+ status: "needs_input",
+ updatedAt: hoursAgo(1.75),
+ createdAt: hoursAgo(5),
},
],
},
diff --git a/frontend/src/renderer/styles.css b/frontend/src/renderer/styles.css
index cf39e4688..31932ae73 100644
--- a/frontend/src/renderer/styles.css
+++ b/frontend/src/renderer/styles.css
@@ -442,18 +442,19 @@ body.is-resizing-x [data-slot="sidebar-container"] {
align-items: center;
gap: 6px;
border-radius: 7px;
- border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
+ border: 1px solid var(--border);
padding: 0 15px;
font-size: 13px;
font-weight: 600;
line-height: 1;
- background: var(--accent-weak);
- color: var(--accent);
+ background: var(--raised);
+ color: var(--fg-muted);
transition: background 0.12s ease;
}
.dashboard-app-header__accent-btn:hover {
- background: color-mix(in srgb, var(--accent-weak) 80%, transparent);
+ background: var(--surface);
+ color: var(--fg);
}
.session-topbar__lead {