import * as React from "react"; import { motion } from "framer-motion"; import { FolderGit2, Paperclip, ArrowUp, X, MessagesSquare, Plus, Settings, Monitor, GitCommitHorizontal, ChevronDown, ChevronRight, ListChevronsUpDown, Bot, Check, Loader, SquareTerminal, PanelLeft, Ellipsis, Mic, GitPullRequest, SquarePen, Columns2, Rows2, } from "lucide-react"; import { ClaudeIcon, CodexIcon, SourceControlIcon } from "~/components/mockup"; const USER_BUBBLE_STYLE = { borderRadius: "16px 2px 16px 16px" }; const NEW_BADGE_STYLE = { backgroundColor: "rgba(46, 160, 67, 0.2)" }; // ── Stagger timing ────────────────────────────────────── const D = { shell: 0, sidebar: 0.1, titleBar: 0.15, tabs: 0.25, chat: 0.35, diffPanel: 0.55, }; function fade(delay: number) { return { initial: { opacity: 0, y: 8 } as const, animate: { opacity: 1, y: 0 } as const, transition: { duration: 0.5, delay, ease: "easeOut" as const }, }; } // ── Data ──────────────────────────────────────────────── type ChatItem = | { type: "user"; text: string } | { type: "text"; text: string; bold?: boolean } | { type: "tool"; label: string; summary?: string; status: "running" | "done" }; const CHAT: ChatItem[] = [ { type: "user", text: "Build me an internal dashboard for tracking customer returns" }, { type: "text", text: "I'll break this down into planning and implementation.", bold: true }, { type: "tool", label: "Run plan-technical", summary: "codex · plan-technical", status: "done" }, { type: "tool", label: "Run plan-design", summary: "claude · plan-design", status: "done" }, { type: "tool", label: "Wait for agents", summary: "plan-technical plan-design", status: "done", }, { type: "text", text: "Got the plans. Spinning up Codex for implementation.", bold: true }, { type: "tool", label: "Run implement", summary: "codex · 12 files changed", status: "done" }, { type: "text", text: "Implementation done. Requesting review from Claude." }, { type: "tool", label: "Run review", summary: "claude · no issues found", status: "running" }, { type: "text", text: "All tasks complete. Dashboard is ready.", bold: true }, ]; // ── Sidebar data ──────────────────────────────────────── interface SidebarWorkspace { name: string; kind: "checkout" | "worktree"; status: "running" | "done" | "idle" | "syncing"; selected?: boolean; diffStat?: { additions: number; deletions: number }; pr?: { number: number; state: "open" | "merged" | "closed" }; } interface SidebarProject { initial: string; name: string; workspaces: SidebarWorkspace[]; } const SIDEBAR_PROJECTS: SidebarProject[] = [ { initial: "A", name: "acme/returns-app", workspaces: [ { name: "main", kind: "checkout", status: "syncing", selected: true, diffStat: { additions: 247, deletions: 15 }, }, { name: "feat/dashboard", kind: "worktree", status: "done", pr: { number: 142, state: "open" }, }, ], }, { initial: "P", name: "acme/payments", workspaces: [ { name: "main", kind: "checkout", status: "idle" }, { name: "fix/stripe-webhook", kind: "worktree", status: "done", diffStat: { additions: 38, deletions: 4 }, pr: { number: 89, state: "merged" }, }, ], }, { initial: "I", name: "acme/infra", workspaces: [ { name: "main", kind: "checkout", status: "idle" }, { name: "feat/k8s-autoscale", kind: "worktree", status: "syncing", diffStat: { additions: 91, deletions: 3 }, }, ], }, { initial: "D", name: "acme/design-system", workspaces: [{ name: "main", kind: "checkout", status: "idle" }], }, ]; // ── Tab data ───────────────────────────────────────────── interface TabDef { name: string; provider: "claude" | "codex" | "terminal"; done: boolean; active?: boolean; } const TABS: TabDef[] = [ { name: "Orchestrator", provider: "claude", done: false, active: true }, { name: "Implement", provider: "codex", done: true }, { name: "Review", provider: "claude", done: true }, ]; // ── Diff data ────────────────────────────────────────── type DiffLineType = "add" | "remove" | "context" | "header"; interface DiffLine { type: DiffLineType; ln: string | null; content?: string; tokens?: Array<{ text: string; cls: string }>; } const DIFF_LINES: DiffLine[] = [ { type: "add", ln: "1", tokens: [ { text: "import", cls: "text-syn-keyword" }, { text: " { ", cls: "text-syn-punctuation" }, { text: "useState", cls: "text-syn-variable" }, { text: " } ", cls: "text-syn-punctuation" }, { text: "from", cls: "text-syn-keyword" }, { text: ' "react"', cls: "text-syn-string" }, ], }, { type: "add", ln: "2", tokens: [ { text: "import", cls: "text-syn-keyword" }, { text: " { ", cls: "text-syn-punctuation" }, { text: "ReturnTable", cls: "text-syn-variable" }, { text: " } ", cls: "text-syn-punctuation" }, { text: "from", cls: "text-syn-keyword" }, { text: ' "./components"', cls: "text-syn-string" }, ], }, { type: "add", ln: "3", tokens: [] }, { type: "add", ln: "4", tokens: [ { text: "export", cls: "text-syn-keyword" }, { text: " function", cls: "text-syn-keyword" }, { text: " Dashboard", cls: "text-syn-function" }, { text: "() {", cls: "text-syn-punctuation" }, ], }, { type: "add", ln: "5", tokens: [ { text: " const", cls: "text-syn-keyword" }, { text: " [returns, setReturns]", cls: "text-syn-variable" }, { text: " = ", cls: "text-syn-operator" }, { text: "useState", cls: "text-syn-function" }, { text: "([])", cls: "text-syn-punctuation" }, ], }, { type: "add", ln: "6", tokens: [ { text: " const", cls: "text-syn-keyword" }, { text: " [filter, setFilter]", cls: "text-syn-variable" }, { text: " = ", cls: "text-syn-operator" }, { text: "useState", cls: "text-syn-function" }, { text: '("all")', cls: "text-syn-string" }, ], }, { type: "add", ln: "7", tokens: [] }, { type: "add", ln: "8", tokens: [ { text: " ", cls: "text-syn-punctuation" }, { text: "return", cls: "text-syn-keyword" }, { text: " (", cls: "text-syn-punctuation" }, ], }, { type: "add", ln: "9", tokens: [ { text: " <", cls: "text-syn-punctuation" }, { text: "main", cls: "text-syn-tag" }, { text: " className", cls: "text-syn-property" }, { text: '="', cls: "text-syn-punctuation" }, { text: "min-h-screen p-8", cls: "text-syn-string" }, { text: '">', cls: "text-syn-punctuation" }, ], }, { type: "add", ln: "10", tokens: [ { text: " <", cls: "text-syn-punctuation" }, { text: "h1", cls: "text-syn-tag" }, { text: ">Customer Returns", cls: "text-syn-punctuation" }, ], }, { type: "add", ln: "11", tokens: [ { text: " <", cls: "text-syn-punctuation" }, { text: "FilterBar", cls: "text-syn-tag" }, { text: " value", cls: "text-syn-property" }, { text: "={", cls: "text-syn-punctuation" }, { text: "filter", cls: "text-syn-variable" }, { text: "}", cls: "text-syn-punctuation" }, { text: " onChange", cls: "text-syn-property" }, { text: "={", cls: "text-syn-punctuation" }, { text: "setFilter", cls: "text-syn-variable" }, { text: "} />", cls: "text-syn-punctuation" }, ], }, { type: "add", ln: "12", tokens: [ { text: " <", cls: "text-syn-punctuation" }, { text: "ReturnTable", cls: "text-syn-tag" }, { text: " data", cls: "text-syn-property" }, { text: "={", cls: "text-syn-punctuation" }, { text: "returns", cls: "text-syn-variable" }, { text: "} />", cls: "text-syn-punctuation" }, ], }, { type: "add", ln: "13", tokens: [ { text: " <", cls: "text-syn-punctuation" }, { text: "StatusChart", cls: "text-syn-tag" }, { text: " data", cls: "text-syn-property" }, { text: "={", cls: "text-syn-punctuation" }, { text: "returns", cls: "text-syn-variable" }, { text: "} />", cls: "text-syn-punctuation" }, ], }, { type: "add", ln: "14", tokens: [ { text: " ", cls: "text-syn-punctuation" }, ], }, { type: "add", ln: "15", tokens: [{ text: " )", cls: "text-syn-punctuation" }] }, { type: "add", ln: "16", tokens: [{ text: "}", cls: "text-syn-punctuation" }] }, ]; // ── Implementation agent chat data ────────────────────── // ── Terminal log lines ───────────────────────────────── const TERMINAL_LINES = [ { id: "cmd", text: "$ npm run dev", cls: "text-mock-fg" }, { id: "blank1", text: "", cls: "" }, { id: "pkg", text: "> acme-returns@0.1.0 dev", cls: "text-mock-fg-muted" }, { id: "pkg-cmd", text: "> next dev --turbopack", cls: "text-mock-fg-muted" }, { id: "blank2", text: "", cls: "" }, { id: "next-ver", text: " ▲ Next.js 15.3.1 (Turbopack)", cls: "text-mock-fg" }, { id: "next-local", text: " - Local: http://localhost:3000", cls: "text-mock-fg-muted" }, { id: "blank3", text: "", cls: "" }, { id: "starting", text: " ✓ Starting...", cls: "text-mock-green" }, { id: "ready", text: " ✓ Ready in 1.2s", cls: "text-mock-green" }, { id: "compiling-dashboard", text: " ○ Compiling /dashboard ...", cls: "text-mock-fg-muted" }, { id: "compiled-dashboard", text: " ✓ Compiled /dashboard in 340ms", cls: "text-mock-green" }, { id: "compiling-api", text: " ○ Compiling /api/returns ...", cls: "text-mock-fg-muted" }, { id: "compiled-api", text: " ✓ Compiled /api/returns in 120ms", cls: "text-mock-green" }, ]; // ── Sub-components ────────────────────────────────────── function TrafficLights() { return (
); } function ProviderIcon({ provider, muted = false, }: { provider: "claude" | "codex" | "terminal"; muted?: boolean; }) { const cls = muted ? "text-mock-fg-muted" : "text-mock-fg"; if (provider === "terminal") return ; return provider === "claude" ? ( ) : ( ); } function TabBarAction({ icon: Icon, }: { icon: React.ComponentType<{ size?: number; className?: string; strokeWidth?: number }>; }) { return (
); } function PaneTabBar({ tabs, focused = false }: { tabs: TabDef[]; focused?: boolean }) { return (
{tabs.map((tab) => (
{tab.active && (
)}
{tab.name}
))}
); } function Composer({ provider, model, focused = false, }: { provider: "claude" | "codex"; model: string; focused?: boolean; }) { const Icon = provider === "claude" ? ClaudeIcon : CodexIcon; return (
{focused && ( )} Message the agent, tag @files, or use /commands and /skills
{model}
); } function prStateLabel(state: "open" | "merged" | "closed"): string { if (state === "open") return "Open"; if (state === "merged") return "Merged"; return "Closed"; } function WorkspaceIcon({ workspace }: { workspace: SidebarWorkspace }) { if (workspace.status === "syncing") return ; const icon = workspace.kind === "worktree" ? ( ) : ( ); return ( <> {icon} {workspace.status !== "idle" && (
)} ); } function SidebarWorkspaceRow({ workspace }: { workspace: SidebarWorkspace }) { return (
{workspace.name} {workspace.diffStat && (
+{workspace.diffStat.additions} -{workspace.diffStat.deletions}
)}
{workspace.pr && (
#{workspace.pr.number} · {prStateLabel(workspace.pr.state)}
)}
); } function diffLineBg(type: DiffLineType): string { if (type === "add") return "bg-mock-diff-add"; if (type === "remove") return "bg-mock-diff-remove"; return "bg-mock-surface1"; } function diffLineNumCls(type: DiffLineType): string { if (type === "add") return "text-mock-green-400"; if (type === "remove") return "text-mock-red"; return "text-mock-fg-muted"; } function DiffLineRow({ line }: { line: DiffLine }) { const lineBg = diffLineBg(line.type); const lineNumCls = diffLineNumCls(line.type); return (
{line.ln ?? ""}
{line.tokens?.map((tok) => ( {tok.text} ))}
); } function chatItemKey(item: ChatItem): string { if (item.type === "user") return `user-${item.text}`; if (item.type === "tool") return `tool-${item.label}-${item.summary ?? ""}`; return `text-${item.text}`; } function ChatMessages({ items }: { items: ChatItem[] }) { return (
{items.map((item) => { const key = chatItemKey(item); if (item.type === "user") { return (
{item.text}
); } if (item.type === "tool") { const isDone = item.status === "done"; return (
{item.label} {item.summary && ( {item.summary} )}
{isDone ? ( ) : ( )}
); } return (

{item.text}

); })}
); } function ChatArea() { return (
); } function TerminalPane() { return (
{TERMINAL_LINES.map((line) => (
{line.text}
))}
); } function ExplorerSidebar() { return (
{/* Explorer header — h-12, Changes/Files tabs */}
Changes
Files
{/* Secondary header — h-9, Uncommitted dropdown + filter icons */}
Uncommitted
{/* File header — expanded */}
dashboard.tsx src/pages New
+16 -0
{/* Diff lines */}
{DIFF_LINES.map((line) => ( ))}
{/* Collapsed file rows */} {[ { name: "return-table.tsx", dir: "src/components", added: 42, removed: 8 }, { name: "filter-bar.tsx", dir: "src/components", added: 28, removed: 3 }, { name: "status-chart.tsx", dir: "src/components", added: 19, removed: 0, isNew: true }, { name: "returns.ts", dir: "src/api", added: 12, removed: 5 }, { name: "index.tsx", dir: "src/pages", added: 6, removed: 2 }, ].map((file) => (
{file.name} {file.dir} {file.isNew && ( New )}
+{file.added} -{file.removed}
))}
); } // Snake traversal order matching the real app: [0, 1, 3, 5, 4, 2] (2-col × 3-row grid) // DOT_SEQUENCE[sequenceIndex] = dotIndex, so sequenceIndex = indexOf(dotIndex) const DOT_SEQUENCE = [0, 1, 3, 5, 4, 2] as const; const SYNCED_LOADER_DURATION_MS = 950; const DOT_COUNT = 6; const DOT_INDICES = Array.from({ length: DOT_COUNT }, (_, i) => i); const STEP_MS = SYNCED_LOADER_DURATION_MS / DOT_COUNT; // 158.333…ms per step interface SyncedLoaderDotProps { dotIndex: number; dotSize: number; gap: number; } function SyncedLoaderDot({ dotIndex, dotSize, gap }: SyncedLoaderDotProps) { const col = dotIndex % 2; const row = Math.floor(dotIndex / 2); const sequenceIndex = DOT_SEQUENCE.indexOf(dotIndex as (typeof DOT_SEQUENCE)[number]); const delayMs = -(sequenceIndex * STEP_MS); const style = React.useMemo( () => ({ position: "absolute" as const, left: col * (dotSize + gap), top: row * (dotSize + gap), width: dotSize, height: dotSize, borderRadius: "50%", backgroundColor: "#f59e0b", animationName: "synced-snake-dot", animationDuration: `${SYNCED_LOADER_DURATION_MS}ms`, animationTimingFunction: "linear", animationIterationCount: "infinite", animationDelay: `${delayMs}ms`, }), [col, row, dotSize, gap, delayMs], ); return
; } function SyncedLoader({ size = 11 }: { size?: number }) { const gap = Math.max(1, Math.round(size * 0.12)); const dotSize = Math.max(2, Math.floor((size - gap * 2) / 3)); const gridW = dotSize * 2 + gap; const gridH = dotSize * 3 + gap * 2; const outerStyle = React.useMemo( () => ({ width: size, height: size, display: "flex", alignItems: "center", justifyContent: "center", flexShrink: 0, }), [size], ); const innerStyle = React.useMemo( () => ({ position: "relative" as const, width: gridW, height: gridH }), [gridW, gridH], ); return (
{DOT_INDICES.map((dotIndex) => ( ))}
); } function WorkspaceStatusDot({ status }: { status: SidebarWorkspace["status"] }) { if (status === "running") return
; if (status === "done") return
; return null; } function Sidebar() { // Shared left edge: pl-3 (12px) for traffic lights, icons, footer dot // Workspace indent: pl-7 (28px) // PR badge aligns with workspace text: pl-[42px] return (
{/* Traffic lights */}
{/* Sessions header */}
Sessions
{/* Project list */}
{SIDEBAR_PROJECTS.map((project) => (
{/* Project row — icon aligns with traffic lights / sessions */}
{project.initial}
{project.name}
{/* Workspace rows — indented one level */} {project.workspaces.map((workspace) => ( ))}
))}
{/* Footer — dot aligns with traffic lights / icons */}
MacBook Pro
); } const TERMINAL_TABS: TabDef[] = [ { name: "npm run dev", provider: "terminal", done: false, active: true }, ]; function TitleBarLeft() { return (
main acme/returns-app
); } function TitleBarRight() { return (
Commit
+247 -15
); } function DesktopSplitPanes() { return (
{/* Left pane: all agent tabs + chat */}
{/* Resize handle */}
{/* Right pane: terminal only */}
); } // ── Desktop Mockup ────────────────────────────────────── function DesktopMockup() { const containerRef = React.useRef(null); const [scale, setScale] = React.useState(1); React.useEffect(() => { function updateScale() { if (!containerRef.current) return; const parentWidth = containerRef.current.parentElement?.clientWidth ?? 1200; const designWidth = 1200; setScale(Math.min(1, parentWidth / designWidth)); } updateScale(); window.addEventListener("resize", updateScale); return () => window.removeEventListener("resize", updateScale); }, []); const containerStyle = React.useMemo(() => ({ height: `${1200 * (9 / 16) * scale}px` }), [scale]); const innerStyle = React.useMemo(() => ({ width: 1200, transform: `scale(${scale})` }), [scale]); return (
{/* Top-level: left sidebar | center column | explorer sidebar — all full height */}
{/* Left sidebar — full height */} {/* Center column: title bar + split panes */}
{/* Title bar — belongs to center column only */}
{/* Explorer sidebar — full height, pushes center column */}
); } // ── Export ─────────────────────────────────────────────── export function HeroMockup() { return ; }