feat(site): rewrite landing page — Claude Code Command Center
Reposition cladm from "monitor & launcher" to "command center" with embedded PTY grid, tabbed workspaces, and pane controls. New hero animation shows picker → grid transition. JSX workspace mockup with 4 active panes, traffic-light buttons, and live terminal content. Updated features, two-mode controls, and metadata throughout. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,9 +14,9 @@ const pixel = Silkscreen({
|
|||||||
});
|
});
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "cladm — Monitor & launch Claude Code sessions",
|
title: "cladm — Claude Code Command Center",
|
||||||
description:
|
description:
|
||||||
"Multi-project Claude Code session monitor. Track busy/idle status in real time, see usage costs, get notified when Claude finishes, and launch everything in parallel.",
|
"Multiproject workspace for Claude Code. Embedded terminal grid with tabbed workspaces, pane controls, real-time status tracking, usage monitoring, and full keyboard-driven workflow.",
|
||||||
icons: {
|
icons: {
|
||||||
icon: [
|
icon: [
|
||||||
{ url: "/favicon.ico", sizes: "32x32" },
|
{ url: "/favicon.ico", sizes: "32x32" },
|
||||||
@@ -26,8 +26,8 @@ export const metadata: Metadata = {
|
|||||||
apple: "/apple-touch-icon.png",
|
apple: "/apple-touch-icon.png",
|
||||||
},
|
},
|
||||||
openGraph: {
|
openGraph: {
|
||||||
title: "cladm",
|
title: "cladm — Claude Code Command Center",
|
||||||
description: "Monitor & launch Claude Code sessions across all your projects",
|
description: "Manage all your Claude Code sessions from one terminal. Embedded PTY grid, tabbed workspaces, live monitoring, and pane controls.",
|
||||||
url: "https://claudm.com",
|
url: "https://claudm.com",
|
||||||
siteName: "cladm",
|
siteName: "cladm",
|
||||||
type: "website",
|
type: "website",
|
||||||
@@ -36,14 +36,14 @@ export const metadata: Metadata = {
|
|||||||
url: "/og-image.png",
|
url: "/og-image.png",
|
||||||
width: 1200,
|
width: 1200,
|
||||||
height: 630,
|
height: 630,
|
||||||
alt: "cladm — Monitor & launch Claude Code sessions",
|
alt: "cladm — Claude Code Command Center",
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
twitter: {
|
twitter: {
|
||||||
card: "summary_large_image",
|
card: "summary_large_image",
|
||||||
title: "cladm",
|
title: "cladm — Claude Code Command Center",
|
||||||
description: "Monitor & launch Claude Code sessions across all your projects",
|
description: "Manage all your Claude Code sessions from one terminal. Embedded PTY grid, tabbed workspaces, live monitoring, and pane controls.",
|
||||||
images: ["/og-image.png"],
|
images: ["/og-image.png"],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -11,10 +11,7 @@ import {
|
|||||||
NetworkIcon,
|
NetworkIcon,
|
||||||
GamepadIcon,
|
GamepadIcon,
|
||||||
BlocksIcon,
|
BlocksIcon,
|
||||||
ArrowRightIcon,
|
|
||||||
ExternalLinkIcon,
|
|
||||||
LinkedinIcon,
|
LinkedinIcon,
|
||||||
MailIcon,
|
|
||||||
SpaceInvadersIcon,
|
SpaceInvadersIcon,
|
||||||
EyeIcon,
|
EyeIcon,
|
||||||
BellIcon,
|
BellIcon,
|
||||||
@@ -90,13 +87,56 @@ function FeatureBlock({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function GridPaneMockup({
|
||||||
|
name,
|
||||||
|
status,
|
||||||
|
elapsed,
|
||||||
|
children,
|
||||||
|
focused,
|
||||||
|
}: {
|
||||||
|
name: string;
|
||||||
|
status: "busy" | "idle";
|
||||||
|
elapsed?: string;
|
||||||
|
children: React.ReactNode;
|
||||||
|
focused?: boolean;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<div className={`bg-bg border ${focused ? "border-accent" : "border-border"}`}>
|
||||||
|
{/* Pane title bar */}
|
||||||
|
<div className="flex items-center justify-between px-3 py-1 border-b border-border bg-surface-2/60">
|
||||||
|
<div className="font-[family-name:var(--font-mono)] text-[10px] flex items-center gap-1.5">
|
||||||
|
{status === "busy" ? (
|
||||||
|
<span className="text-green">●</span>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<span className="text-yellow">◉</span>
|
||||||
|
{elapsed && <span className="text-dim">{elapsed}</span>}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<span className="text-text">{name}</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-1">
|
||||||
|
<span className="text-cyan text-[8px]">●</span>
|
||||||
|
<span className="text-dim text-[8px]">─</span>
|
||||||
|
<span className="text-[#27c93f] text-[8px]">●</span>
|
||||||
|
<span className="text-[#ff5f56] text-[8px]">●</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/* Pane content */}
|
||||||
|
<div className="p-3 font-[family-name:var(--font-mono)] text-[10px] text-dim leading-relaxed">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-bg selection:bg-accent/30">
|
<div className="min-h-screen bg-bg selection:bg-accent/30">
|
||||||
<SubscribeModal />
|
<SubscribeModal />
|
||||||
|
|
||||||
{/* ══════ HERO ══════ */}
|
{/* ══════ HERO ══════ */}
|
||||||
<section className="relative overflow-hidden scanlines">
|
<section className="relative overflow-hidden scanlines">
|
||||||
{/* Grid background */}
|
|
||||||
<div
|
<div
|
||||||
className="absolute inset-0 opacity-[0.04]"
|
className="absolute inset-0 opacity-[0.04]"
|
||||||
style={{
|
style={{
|
||||||
@@ -142,14 +182,13 @@ export default function Home() {
|
|||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p className="font-[family-name:var(--font-pixel)] text-accent text-lg md:text-xl mb-5">
|
<p className="font-[family-name:var(--font-pixel)] text-accent text-lg md:text-xl mb-5">
|
||||||
MULTI-PROJECT CLAUDE CODE MONITOR
|
CLAUDE CODE COMMAND CENTER
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<p className="font-[family-name:var(--font-mono)] text-dim text-sm max-w-md leading-relaxed mb-8">
|
<p className="font-[family-name:var(--font-mono)] text-dim text-sm max-w-md leading-relaxed mb-8">
|
||||||
Track all your Claude Code sessions in one place. See
|
Manage all your Claude Code sessions from one terminal.
|
||||||
busy/idle status in real time, monitor usage costs, get
|
An embedded PTY grid with tabbed workspaces, pane controls,
|
||||||
notified when Claude finishes, and launch everything in
|
real-time status tracking, and full keyboard-driven workflow.
|
||||||
parallel Terminal windows.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
{/* Install command */}
|
{/* Install command */}
|
||||||
@@ -169,7 +208,7 @@ export default function Home() {
|
|||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Right — terminal cascade */}
|
{/* Right — terminal cascade: picker → grid */}
|
||||||
<div className="flex-1 w-full max-w-xl">
|
<div className="flex-1 w-full max-w-xl">
|
||||||
<TerminalCascade />
|
<TerminalCascade />
|
||||||
</div>
|
</div>
|
||||||
@@ -186,50 +225,185 @@ export default function Home() {
|
|||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ══════ DEMO GIF ══════ */}
|
{/* ══════ THE WORKSPACE ══════ */}
|
||||||
<section className="max-w-5xl mx-auto px-6 py-20">
|
<section className="max-w-5xl mx-auto px-6 py-20">
|
||||||
<div className="text-center mb-10">
|
<div className="text-center mb-4">
|
||||||
<h2 className="font-[family-name:var(--font-pixel)] text-accent text-sm uppercase tracking-[0.3em] mb-3">
|
<h2 className="font-[family-name:var(--font-pixel)] text-accent text-sm uppercase tracking-[0.3em] mb-3">
|
||||||
// SEE IT IN ACTION
|
// THE WORKSPACE
|
||||||
</h2>
|
</h2>
|
||||||
|
<p className="font-[family-name:var(--font-mono)] text-dim text-xs max-w-2xl mx-auto leading-relaxed">
|
||||||
|
Every Claude Code session runs in an embedded terminal pane — no separate windows.
|
||||||
|
See all your projects at once, switch focus with a click, and never lose track of what Claude is doing.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<TerminalWindow title="cladm">
|
{/* Grid workspace mockup */}
|
||||||
<Image
|
<div className="mt-10">
|
||||||
src="/demo.gif"
|
<div className="pixel-border bg-surface overflow-hidden">
|
||||||
alt="cladm demo showing project navigation"
|
{/* Tab bar */}
|
||||||
width={980}
|
<div className="flex items-center bg-surface-2 border-b-2 border-border">
|
||||||
height={500}
|
<div className="px-4 py-2 border-b-2 border-accent font-[family-name:var(--font-mono)] text-xs">
|
||||||
className="w-full"
|
<span className="text-green">●</span>
|
||||||
unoptimized
|
<span className="text-text"> acme-api</span>
|
||||||
/>
|
<span className="text-dim"> · </span>
|
||||||
</TerminalWindow>
|
<span className="text-yellow">◉</span>
|
||||||
|
<span className="text-text"> quantum-dash</span>
|
||||||
|
</div>
|
||||||
|
<div className="px-4 py-2 font-[family-name:var(--font-mono)] text-xs text-dim border-b-2 border-transparent">
|
||||||
|
<span className="text-green">●</span>
|
||||||
|
<span> ml-pipeline</span>
|
||||||
|
<span className="text-dim"> · </span>
|
||||||
|
<span className="text-green">●</span>
|
||||||
|
<span> infra-k8s</span>
|
||||||
|
</div>
|
||||||
|
<div className="ml-auto px-3 py-2 font-[family-name:var(--font-mono)] text-[10px] text-dim">
|
||||||
|
<span className="text-accent">+</span> add pane
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Pane grid */}
|
||||||
|
<div className="grid grid-cols-2 gap-[2px] p-[2px]">
|
||||||
|
{/* Pane 1: acme-api */}
|
||||||
|
<GridPaneMockup name="acme-api" status="busy" focused>
|
||||||
|
<div className="text-green mb-1">> I'll analyze the authentication module and</div>
|
||||||
|
<div className="text-green">{" "}fix the token refresh bug you mentioned.</div>
|
||||||
|
<div className="mt-2">
|
||||||
|
<span className="text-accent">⏺</span> Reading src/auth/token.ts
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-accent">⏺</span> Reading src/auth/middleware.ts
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-accent">⏺</span> Grep: refreshToken pattern
|
||||||
|
</div>
|
||||||
|
<div className="text-green mt-1">
|
||||||
|
Found 3 files with stale token logic.
|
||||||
|
<span className="cursor-blink text-accent">_</span>
|
||||||
|
</div>
|
||||||
|
</GridPaneMockup>
|
||||||
|
|
||||||
|
{/* Pane 2: quantum-dash */}
|
||||||
|
<GridPaneMockup name="quantum-dash" status="idle" elapsed="4m">
|
||||||
|
<div className="text-text">I've updated the chart component to use</div>
|
||||||
|
<div className="text-text">the new streaming data format. Changes:</div>
|
||||||
|
<div className="mt-2">
|
||||||
|
<span className="text-green">✓</span> src/components/chart.tsx
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-green">✓</span> src/hooks/useChartData.ts
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-green">✓</span> src/types/stream.d.ts
|
||||||
|
</div>
|
||||||
|
<div className="mt-2 text-yellow">Waiting for your input...</div>
|
||||||
|
</GridPaneMockup>
|
||||||
|
|
||||||
|
{/* Pane 3: ml-pipeline */}
|
||||||
|
<GridPaneMockup name="ml-pipeline" status="busy">
|
||||||
|
<div className="text-green">> Building the BERT fine-tuning pipeline</div>
|
||||||
|
<div className="text-green">{" "}with the new training dataset.</div>
|
||||||
|
<div className="mt-2">
|
||||||
|
<span className="text-accent">⏺</span> Writing src/train.py
|
||||||
|
</div>
|
||||||
|
<div className="mt-1">
|
||||||
|
Processing: epoch 3/10{" "}
|
||||||
|
<span className="text-accent">████████</span>
|
||||||
|
<span className="text-border">░░░░░░░░░░░░</span>{" "}
|
||||||
|
<span className="text-text">30%</span>
|
||||||
|
</div>
|
||||||
|
</GridPaneMockup>
|
||||||
|
|
||||||
|
{/* Pane 4: infra-k8s */}
|
||||||
|
<GridPaneMockup name="infra-k8s" status="busy">
|
||||||
|
<div className="text-green">> Updating the Kubernetes deployment</div>
|
||||||
|
<div className="text-green">{" "}manifests for staging.</div>
|
||||||
|
<div className="mt-2">
|
||||||
|
<span className="text-accent">⏺</span> Reading k8s/staging/deployment.yaml
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<span className="text-accent">⏺</span> Reading k8s/staging/service.yaml
|
||||||
|
</div>
|
||||||
|
<div className="mt-1 text-green">
|
||||||
|
Scaling replicas 2 → 4 for load test
|
||||||
|
<span className="cursor-blink text-accent">_</span>
|
||||||
|
</div>
|
||||||
|
</GridPaneMockup>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Feature callouts */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-6 mt-10">
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="font-[family-name:var(--font-pixel)] text-accent text-xs uppercase tracking-wider mb-2">
|
||||||
|
Embedded PTY Grid
|
||||||
|
</div>
|
||||||
|
<p className="font-[family-name:var(--font-mono)] text-dim text-[10px] leading-relaxed">
|
||||||
|
Each pane runs a real pseudo-terminal via forkpty(). Full I/O, ANSI colors, resize — no tmux needed.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="font-[family-name:var(--font-pixel)] text-accent text-xs uppercase tracking-wider mb-2">
|
||||||
|
Tabbed Workspaces
|
||||||
|
</div>
|
||||||
|
<p className="font-[family-name:var(--font-mono)] text-dim text-[10px] leading-relaxed">
|
||||||
|
Group sessions into tabs. Inline pane names with status icons show what's running at a glance.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
<div className="text-center">
|
||||||
|
<div className="font-[family-name:var(--font-pixel)] text-accent text-xs uppercase tracking-wider mb-2">
|
||||||
|
Pane Controls
|
||||||
|
</div>
|
||||||
|
<p className="font-[family-name:var(--font-mono)] text-dim text-[10px] leading-relaxed">
|
||||||
|
Traffic-light buttons on every pane: close, expand, minimize, plus a folder-open button. Fully mouse-driven.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<PixelDivider />
|
<PixelDivider />
|
||||||
|
|
||||||
{/* ══════ SCREENSHOTS ══════ */}
|
{/* ══════ SMART PICKER ══════ */}
|
||||||
<section className="max-w-5xl mx-auto px-6 py-16">
|
<section className="max-w-5xl mx-auto px-6 py-16">
|
||||||
<h2 className="font-[family-name:var(--font-pixel)] text-accent text-sm uppercase tracking-[0.3em] mb-12 text-center">
|
<div className="text-center mb-4">
|
||||||
// SCREENSHOTS
|
<h2 className="font-[family-name:var(--font-pixel)] text-accent text-sm uppercase tracking-[0.3em] mb-3">
|
||||||
</h2>
|
// THE SMART PICKER
|
||||||
|
</h2>
|
||||||
|
<p className="font-[family-name:var(--font-mono)] text-dim text-xs max-w-2xl mx-auto leading-relaxed">
|
||||||
|
It starts with a smart project picker. cladm reads{" "}
|
||||||
|
<code className="text-accent">~/.claude/history.jsonl</code> to discover every project
|
||||||
|
you've used with Claude Code — git branch, sync status, dirty state, session history, stack detection — all loaded in parallel.
|
||||||
|
Select what you need, hit Enter, and the grid workspace takes over.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div className="space-y-16">
|
<div className="mt-8">
|
||||||
{/* Main view */}
|
<TerminalWindow title="cladm — 8 projects">
|
||||||
|
<Image
|
||||||
|
src="/demo.gif"
|
||||||
|
alt="cladm smart picker showing project navigation and selection"
|
||||||
|
width={980}
|
||||||
|
height={500}
|
||||||
|
className="w-full"
|
||||||
|
unoptimized
|
||||||
|
/>
|
||||||
|
</TerminalWindow>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Picker screenshots */}
|
||||||
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 mt-12">
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-4 mb-4">
|
<div className="flex items-center gap-4 mb-3">
|
||||||
<div className="h-[2px] flex-1 bg-border" />
|
<div className="h-[2px] flex-1 bg-border" />
|
||||||
<h3 className="font-[family-name:var(--font-pixel)] text-text text-xs uppercase tracking-wider whitespace-nowrap">
|
<h3 className="font-[family-name:var(--font-pixel)] text-text text-xs uppercase tracking-wider whitespace-nowrap">
|
||||||
PROJECT LIST
|
PROJECT LIST
|
||||||
</h3>
|
</h3>
|
||||||
<div className="h-[2px] flex-1 bg-border" />
|
<div className="h-[2px] flex-1 bg-border" />
|
||||||
</div>
|
</div>
|
||||||
<p className="font-[family-name:var(--font-mono)] text-dim text-xs text-center mb-6">
|
<p className="font-[family-name:var(--font-mono)] text-dim text-[10px] text-center mb-4">
|
||||||
All your projects sorted by recent Claude usage. Git branch, sync
|
Sorted by recent Claude usage. Git metadata, session count, and stack tags at a glance.
|
||||||
status, dirty state, session count, and auto-detected stack at a
|
|
||||||
glance.
|
|
||||||
</p>
|
</p>
|
||||||
<TerminalWindow title="cladm — 8 projects">
|
<TerminalWindow title="cladm — project list">
|
||||||
<Image
|
<Image
|
||||||
src="/screenshot-main.png"
|
src="/screenshot-main.png"
|
||||||
alt="cladm main project list view"
|
alt="cladm main project list view"
|
||||||
@@ -240,25 +414,21 @@ export default function Home() {
|
|||||||
</TerminalWindow>
|
</TerminalWindow>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Expanded view */}
|
|
||||||
<div>
|
<div>
|
||||||
<div className="flex items-center gap-4 mb-4">
|
<div className="flex items-center gap-4 mb-3">
|
||||||
<div className="h-[2px] flex-1 bg-border" />
|
<div className="h-[2px] flex-1 bg-border" />
|
||||||
<h3 className="font-[family-name:var(--font-pixel)] text-text text-xs uppercase tracking-wider whitespace-nowrap">
|
<h3 className="font-[family-name:var(--font-pixel)] text-text text-xs uppercase tracking-wider whitespace-nowrap">
|
||||||
EXPANDED VIEW
|
EXPANDED VIEW
|
||||||
</h3>
|
</h3>
|
||||||
<div className="h-[2px] flex-1 bg-border" />
|
<div className="h-[2px] flex-1 bg-border" />
|
||||||
</div>
|
</div>
|
||||||
<p className="font-[family-name:var(--font-mono)] text-dim text-xs text-center mb-6">
|
<p className="font-[family-name:var(--font-mono)] text-dim text-[10px] text-center mb-4">
|
||||||
Press <Keycap>→</Keycap> to expand. Browse branches, see
|
Browse branches, past sessions with conversation previews. Resume any session directly.
|
||||||
session conversations with last prompt and Claude's response.
|
|
||||||
Running sessions show <span className="text-green">● running</span> or{" "}
|
|
||||||
<span className="text-yellow">◉ idle</span> status inline. Resume any session directly.
|
|
||||||
</p>
|
</p>
|
||||||
<TerminalWindow title="cladm — 2 selected (1 branch switch)">
|
<TerminalWindow title="cladm — expanded">
|
||||||
<Image
|
<Image
|
||||||
src="/screenshot-expanded.png"
|
src="/screenshot-expanded.png"
|
||||||
alt="cladm expanded view with sessions and branches"
|
alt="cladm expanded view with sessions"
|
||||||
width={980}
|
width={980}
|
||||||
height={600}
|
height={600}
|
||||||
className="w-full"
|
className="w-full"
|
||||||
@@ -273,104 +443,83 @@ export default function Home() {
|
|||||||
{/* ══════ LIVE MONITORING ══════ */}
|
{/* ══════ LIVE MONITORING ══════ */}
|
||||||
<section className="max-w-5xl mx-auto px-6 py-16">
|
<section className="max-w-5xl mx-auto px-6 py-16">
|
||||||
<h2 className="font-[family-name:var(--font-pixel)] text-accent text-sm uppercase tracking-[0.3em] mb-12 text-center">
|
<h2 className="font-[family-name:var(--font-pixel)] text-accent text-sm uppercase tracking-[0.3em] mb-12 text-center">
|
||||||
// LIVE SESSION MONITORING
|
// REAL-TIME STATUS
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div className="max-w-2xl mx-auto">
|
<div className="max-w-3xl mx-auto">
|
||||||
<div className="pixel-border bg-surface p-6">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-8">
|
||||||
<p className="font-[family-name:var(--font-mono)] text-dim text-xs leading-relaxed mb-5">
|
{/* Status indicators */}
|
||||||
cladm detects all running Claude Code sessions across every project and shows their real-time status.
|
<div className="pixel-border bg-surface p-6">
|
||||||
When any session finishes, a sound plays and the dock icon bounces — so you never miss it, even across dozens of parallel sessions.
|
<h3 className="font-[family-name:var(--font-pixel)] text-text text-xs uppercase tracking-wider mb-4">
|
||||||
</p>
|
Session Status
|
||||||
|
|
||||||
<div className="space-y-3 font-[family-name:var(--font-mono)] text-xs">
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<span className="text-green text-base">●</span>
|
|
||||||
<span className="text-text">Busy</span>
|
|
||||||
<span className="text-dim">— Claude is actively processing</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<span className="text-yellow text-base">◉</span>
|
|
||||||
<span className="text-dim">3m</span>
|
|
||||||
<span className="text-text">Idle</span>
|
|
||||||
<span className="text-dim">— Claude finished 3 min ago, waiting for input</span>
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-3">
|
|
||||||
<span className="text-dim text-base">○</span>
|
|
||||||
<span className="text-text ml-[22px]">No session</span>
|
|
||||||
<span className="text-dim">— No active Claude process</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="mt-5 pt-4 border-t border-border">
|
|
||||||
<p className="font-[family-name:var(--font-mono)] text-dim text-[10px] leading-relaxed">
|
|
||||||
Detection reads the tail of each session's JSONL in{" "}
|
|
||||||
<code className="text-accent">~/.claude/projects/</code>. A session is
|
|
||||||
busy if the file was written recently OR the last assistant message
|
|
||||||
has a pending tool call. This prevents false idle triggers during
|
|
||||||
long-running tools and subtasks.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
<PixelDivider />
|
|
||||||
|
|
||||||
{/* ══════ USAGE + IDLE PANELS ══════ */}
|
|
||||||
<section className="max-w-5xl mx-auto px-6 py-16">
|
|
||||||
<h2 className="font-[family-name:var(--font-pixel)] text-accent text-sm uppercase tracking-[0.3em] mb-12 text-center">
|
|
||||||
// USAGE & IDLE PANELS
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<div className="space-y-12 max-w-4xl mx-auto">
|
|
||||||
{/* Usage panel screenshot */}
|
|
||||||
<div>
|
|
||||||
<div className="flex items-center gap-4 mb-4">
|
|
||||||
<div className="h-[2px] flex-1 bg-border" />
|
|
||||||
<h3 className="font-[family-name:var(--font-pixel)] text-text text-xs uppercase tracking-wider whitespace-nowrap">
|
|
||||||
USAGE TRACKING
|
|
||||||
</h3>
|
</h3>
|
||||||
<div className="h-[2px] flex-1 bg-border" />
|
<div className="space-y-3 font-[family-name:var(--font-mono)] text-xs">
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<span className="text-green text-base">●</span>
|
||||||
|
<span className="text-text">Busy</span>
|
||||||
|
<span className="text-dim">— Claude is actively working</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<span className="text-yellow text-base">◉</span>
|
||||||
|
<span className="text-dim">3m</span>
|
||||||
|
<span className="text-text">Idle</span>
|
||||||
|
<span className="text-dim">— waiting for your input</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-3">
|
||||||
|
<span className="text-dim text-base">○</span>
|
||||||
|
<span className="text-text ml-[22px]">No session</span>
|
||||||
|
<span className="text-dim">— not running</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 pt-3 border-t border-border">
|
||||||
|
<p className="font-[family-name:var(--font-mono)] text-dim text-[10px] leading-relaxed">
|
||||||
|
Status visible in both picker rows and grid pane headers.
|
||||||
|
Sound + dock bounce on idle transitions.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="font-[family-name:var(--font-mono)] text-dim text-xs text-center mb-6">
|
|
||||||
Press <Keycap>u</Keycap> to toggle. Tracks session (5h window), weekly
|
|
||||||
all-model and sonnet-only costs against configurable plan limits, plus monthly totals.
|
|
||||||
</p>
|
|
||||||
<TerminalWindow title="cladm — usage panel">
|
|
||||||
<Image
|
|
||||||
src="/screenshot-usage.png"
|
|
||||||
alt="cladm usage tracking panel with session, weekly, and monthly cost bars"
|
|
||||||
width={980}
|
|
||||||
height={500}
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
</TerminalWindow>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Idle sessions screenshot */}
|
{/* Usage tracking */}
|
||||||
<div>
|
<div className="pixel-border bg-surface p-6">
|
||||||
<div className="flex items-center gap-4 mb-4">
|
<h3 className="font-[family-name:var(--font-pixel)] text-text text-xs uppercase tracking-wider mb-4">
|
||||||
<div className="h-[2px] flex-1 bg-border" />
|
Usage Tracking
|
||||||
<h3 className="font-[family-name:var(--font-pixel)] text-text text-xs uppercase tracking-wider whitespace-nowrap">
|
|
||||||
IDLE SESSIONS
|
|
||||||
</h3>
|
</h3>
|
||||||
<div className="h-[2px] flex-1 bg-border" />
|
<div className="space-y-3 font-[family-name:var(--font-mono)] text-[10px]">
|
||||||
|
<div>
|
||||||
|
<div className="flex justify-between mb-1">
|
||||||
|
<span className="text-dim">session (5h)</span>
|
||||||
|
<span className="text-text">$2.40 / $5.00</span>
|
||||||
|
</div>
|
||||||
|
<div className="h-2 bg-bg border border-border">
|
||||||
|
<div className="h-full bg-accent" style={{ width: "48%" }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="flex justify-between mb-1">
|
||||||
|
<span className="text-dim">weekly all-model</span>
|
||||||
|
<span className="text-text">$18.50 / $100</span>
|
||||||
|
</div>
|
||||||
|
<div className="h-2 bg-bg border border-border">
|
||||||
|
<div className="h-full bg-green" style={{ width: "18.5%" }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div className="flex justify-between mb-1">
|
||||||
|
<span className="text-dim">monthly total</span>
|
||||||
|
<span className="text-text">$67.20</span>
|
||||||
|
</div>
|
||||||
|
<div className="h-2 bg-bg border border-border">
|
||||||
|
<div className="h-full bg-cyan" style={{ width: "33.6%" }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="mt-4 pt-3 border-t border-border">
|
||||||
|
<p className="font-[family-name:var(--font-mono)] text-dim text-[10px] leading-relaxed">
|
||||||
|
Press <Keycap>u</Keycap> in picker mode. Tracks session, weekly,
|
||||||
|
and monthly costs against configurable plan limits.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<p className="font-[family-name:var(--font-mono)] text-dim text-xs text-center mb-6">
|
|
||||||
Press <Keycap>i</Keycap> to toggle. Shows sessions waiting for your
|
|
||||||
input, sorted by most recently idle. Press Enter to focus a session's
|
|
||||||
Terminal tab directly.
|
|
||||||
</p>
|
|
||||||
<TerminalWindow title="cladm — idle sessions (2)">
|
|
||||||
<Image
|
|
||||||
src="/screenshot-idle.png"
|
|
||||||
alt="cladm idle sessions panel showing waiting sessions with elapsed time"
|
|
||||||
width={980}
|
|
||||||
height={500}
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
</TerminalWindow>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
@@ -384,81 +533,84 @@ export default function Home() {
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
|
||||||
|
<FeatureBlock
|
||||||
|
icon={<TerminalIcon size={28} />}
|
||||||
|
title="EMBEDDED GRID"
|
||||||
|
desc="Run multiple Claude Code sessions side by side in a tiled terminal grid. Each pane is a real PTY with full I/O — no separate windows needed."
|
||||||
|
/>
|
||||||
|
<FeatureBlock
|
||||||
|
icon={<BlocksIcon size={28} />}
|
||||||
|
title="TABBED WORKSPACES"
|
||||||
|
desc="Group sessions into named tabs. Inline pane indicators show project names and busy/idle status at a glance."
|
||||||
|
/>
|
||||||
|
<FeatureBlock
|
||||||
|
icon={<GamepadIcon size={28} />}
|
||||||
|
title="PANE CONTROLS"
|
||||||
|
desc="Traffic-light buttons on every pane: close, minimize, expand to full screen. Blue button opens the project folder."
|
||||||
|
/>
|
||||||
|
<FeatureBlock
|
||||||
|
icon={<SearchIcon size={28} />}
|
||||||
|
title="SELECT MODE"
|
||||||
|
desc="Double-click any pane to enter select mode. Copy text from the full scrollback buffer — up to 5,000 lines of history."
|
||||||
|
/>
|
||||||
<FeatureBlock
|
<FeatureBlock
|
||||||
icon={<EyeIcon size={28} />}
|
icon={<EyeIcon size={28} />}
|
||||||
title="LIVE MONITORING"
|
title="LIVE MONITORING"
|
||||||
desc="Track all Claude sessions across every project. Busy/idle status updates in real time with elapsed timers."
|
desc="Track busy/idle status across all sessions in real time. Elapsed timers show how long each session has been waiting."
|
||||||
/>
|
/>
|
||||||
<FeatureBlock
|
<FeatureBlock
|
||||||
icon={<TrendingUpIcon size={28} />}
|
icon={<TrendingUpIcon size={28} />}
|
||||||
title="USAGE TRACKING"
|
title="USAGE TRACKING"
|
||||||
desc="Session, weekly, and monthly cost bars. Track all-model and sonnet-only usage against configurable plan limits."
|
desc="Session, weekly, and monthly cost bars against configurable plan limits. Track all-model and sonnet-only usage."
|
||||||
/>
|
/>
|
||||||
<FeatureBlock
|
<FeatureBlock
|
||||||
icon={<BellIcon size={28} />}
|
icon={<BellIcon size={28} />}
|
||||||
title="NOTIFICATIONS"
|
title="NOTIFICATIONS"
|
||||||
desc="Sound + dock bounce when any session finishes. Never miss a completed task across dozens of parallel sessions."
|
desc="Sound + dock bounce when any session finishes. Never miss a completed task across dozens of parallel sessions."
|
||||||
/>
|
/>
|
||||||
<FeatureBlock
|
|
||||||
icon={<ThunderIcon size={28} />}
|
|
||||||
title="FOCUS SESSION"
|
|
||||||
desc="Press Enter on any idle session to instantly focus its Terminal tab. Flash animation highlights the window."
|
|
||||||
/>
|
|
||||||
<FeatureBlock
|
|
||||||
icon={<SearchIcon size={28} />}
|
|
||||||
title="AUTO-DISCOVERY"
|
|
||||||
desc="Reads ~/.claude/history.jsonl to find every project you've used with Claude Code. No config needed."
|
|
||||||
/>
|
|
||||||
<FeatureBlock
|
|
||||||
icon={<NetworkIcon size={28} />}
|
|
||||||
title="GIT METADATA"
|
|
||||||
desc="Branch, sync status (ahead/behind), last commit, dirty state — all loaded in parallel per project."
|
|
||||||
/>
|
|
||||||
<FeatureBlock
|
<FeatureBlock
|
||||||
icon={<FolderIcon size={28} />}
|
icon={<FolderIcon size={28} />}
|
||||||
title="SESSION BROWSER"
|
title="AUTO-DISCOVERY"
|
||||||
desc="Expand any project to browse past sessions. See conversation previews and resume directly."
|
desc="Reads ~/.claude/history.jsonl to find every project. Git branch, sync status, dirty state — all loaded in parallel."
|
||||||
/>
|
/>
|
||||||
<FeatureBlock
|
<FeatureBlock
|
||||||
icon={<TerminalIcon size={28} />}
|
icon={<ThunderIcon size={28} />}
|
||||||
title="PARALLEL LAUNCH"
|
title="DIRECT PTY"
|
||||||
desc="Select multiple projects and hit Enter. Each opens in a new Terminal.app window simultaneously."
|
desc="Native pseudo-terminal management via forkpty(). No tmux dependency. Zero configuration. Just works."
|
||||||
/>
|
|
||||||
<FeatureBlock
|
|
||||||
icon={<BlocksIcon size={28} />}
|
|
||||||
title="STACK DETECTION"
|
|
||||||
desc="Auto-detects project stack: TypeScript, Python, Rust, Go, Docker, and more from config files."
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<PixelDivider />
|
<PixelDivider />
|
||||||
|
|
||||||
{/* ══════ KEYBINDINGS ══════ */}
|
{/* ══════ CONTROLS ══════ */}
|
||||||
<section className="max-w-5xl mx-auto px-6 py-16">
|
<section className="max-w-5xl mx-auto px-6 py-16">
|
||||||
<h2 className="font-[family-name:var(--font-pixel)] text-accent text-sm uppercase tracking-[0.3em] mb-12 text-center">
|
<h2 className="font-[family-name:var(--font-pixel)] text-accent text-sm uppercase tracking-[0.3em] mb-12 text-center">
|
||||||
// CONTROLS
|
// CONTROLS
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<div className="max-w-2xl mx-auto">
|
<div className="grid grid-cols-1 md:grid-cols-2 gap-6 max-w-4xl mx-auto">
|
||||||
|
{/* Picker mode */}
|
||||||
<div className="pixel-border bg-surface p-6">
|
<div className="pixel-border bg-surface p-6">
|
||||||
<div className="grid grid-cols-2 gap-y-3 font-[family-name:var(--font-mono)] text-xs">
|
<h3 className="font-[family-name:var(--font-pixel)] text-accent text-xs uppercase tracking-wider mb-4 text-center">
|
||||||
|
Picker Mode
|
||||||
|
</h3>
|
||||||
|
<div className="grid grid-cols-2 gap-y-2 font-[family-name:var(--font-mono)] text-xs">
|
||||||
{[
|
{[
|
||||||
["↑ ↓", "Navigate"],
|
["↑ ↓", "Navigate"],
|
||||||
["Space", "Toggle selection"],
|
["Space", "Toggle select"],
|
||||||
["→", "Expand project"],
|
["→", "Expand project"],
|
||||||
["←", "Collapse"],
|
["←", "Collapse"],
|
||||||
["Enter", "Launch selected / focus session"],
|
["Enter", "Launch grid"],
|
||||||
["i", "Toggle idle sessions panel"],
|
["/", "Filter"],
|
||||||
["u", "Toggle usage panel"],
|
|
||||||
["/", "Filter projects"],
|
|
||||||
["a", "Select all"],
|
["a", "Select all"],
|
||||||
["n", "Deselect all"],
|
["n", "Deselect all"],
|
||||||
["s", "Cycle sort mode"],
|
["s", "Cycle sort"],
|
||||||
["f", "Open folder in Finder"],
|
["u", "Usage panel"],
|
||||||
["g", "Go to active session"],
|
["i", "Idle sessions"],
|
||||||
["PgUp PgDn", "Jump 15 rows"],
|
["f", "Open folder"],
|
||||||
["q / Esc", "Quit"],
|
["g", "Go to session"],
|
||||||
|
["q", "Quit"],
|
||||||
].map(([key, desc]) => (
|
].map(([key, desc]) => (
|
||||||
<div key={key} className="contents">
|
<div key={key} className="contents">
|
||||||
<div className="text-accent">{key}</div>
|
<div className="text-accent">{key}</div>
|
||||||
@@ -467,12 +619,58 @@ export default function Home() {
|
|||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Grid mode */}
|
||||||
|
<div className="pixel-border bg-surface p-6">
|
||||||
|
<h3 className="font-[family-name:var(--font-pixel)] text-accent text-xs uppercase tracking-wider mb-4 text-center">
|
||||||
|
Grid Mode
|
||||||
|
</h3>
|
||||||
|
<div className="grid grid-cols-2 gap-y-2 font-[family-name:var(--font-mono)] text-xs">
|
||||||
|
{[
|
||||||
|
["Click", "Focus pane"],
|
||||||
|
["Dbl-click", "Select mode"],
|
||||||
|
["Alt+1-9", "Switch tab"],
|
||||||
|
["Alt+n/p", "Next/prev tab"],
|
||||||
|
["+ button", "Add pane"],
|
||||||
|
["Esc", "Back to picker"],
|
||||||
|
].map(([key, desc]) => (
|
||||||
|
<div key={key} className="contents">
|
||||||
|
<div className="text-accent">{key}</div>
|
||||||
|
<div className="text-dim">{desc}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="mt-4 pt-3 border-t border-border">
|
||||||
|
<div className="font-[family-name:var(--font-pixel)] text-text text-[10px] uppercase tracking-wider mb-2">
|
||||||
|
Pane Buttons
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-2 gap-y-2 font-[family-name:var(--font-mono)] text-xs">
|
||||||
|
{[
|
||||||
|
["● blue", "Open folder"],
|
||||||
|
["● green", "Expand pane"],
|
||||||
|
["● yellow", "Minimize"],
|
||||||
|
["● red", "Close pane"],
|
||||||
|
].map(([key, desc], i) => (
|
||||||
|
<div key={key} className="contents">
|
||||||
|
<div className={
|
||||||
|
i === 0 ? "text-cyan" :
|
||||||
|
i === 1 ? "text-[#27c93f]" :
|
||||||
|
i === 2 ? "text-yellow" :
|
||||||
|
"text-[#ff5f56]"
|
||||||
|
}>{key}</div>
|
||||||
|
<div className="text-dim">{desc}</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<PixelDivider />
|
<PixelDivider />
|
||||||
|
|
||||||
{/* ══════ INSTALL ══════ */}
|
{/* ══════ QUICK START ══════ */}
|
||||||
<section className="max-w-5xl mx-auto px-6 py-16">
|
<section className="max-w-5xl mx-auto px-6 py-16">
|
||||||
<h2 className="font-[family-name:var(--font-pixel)] text-accent text-sm uppercase tracking-[0.3em] mb-12 text-center">
|
<h2 className="font-[family-name:var(--font-pixel)] text-accent text-sm uppercase tracking-[0.3em] mb-12 text-center">
|
||||||
// QUICK START
|
// QUICK START
|
||||||
@@ -514,91 +712,13 @@ export default function Home() {
|
|||||||
|
|
||||||
<div className="mt-6 text-center">
|
<div className="mt-6 text-center">
|
||||||
<p className="font-[family-name:var(--font-mono)] text-dim text-xs">
|
<p className="font-[family-name:var(--font-mono)] text-dim text-xs">
|
||||||
Or try with mock data:{" "}
|
Try with mock data:{" "}
|
||||||
<code className="text-yellow">cladm --demo</code>
|
<code className="text-yellow">cladm --demo</code>
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
{/* ══════ LAUNCH RESULT ══════ */}
|
|
||||||
<section className="max-w-5xl mx-auto px-6 py-16">
|
|
||||||
<h2 className="font-[family-name:var(--font-pixel)] text-accent text-sm uppercase tracking-[0.3em] mb-4 text-center">
|
|
||||||
// HIT ENTER
|
|
||||||
</h2>
|
|
||||||
<p className="font-[family-name:var(--font-mono)] text-dim text-xs text-center mb-10 max-w-lg mx-auto">
|
|
||||||
Select your projects, press Enter, and watch them all launch in
|
|
||||||
parallel. Each project opens a fresh Claude Code session in its own
|
|
||||||
Terminal window.
|
|
||||||
</p>
|
|
||||||
|
|
||||||
<div className="flex flex-col md:flex-row items-center gap-6">
|
|
||||||
{/* Mini cladm picker */}
|
|
||||||
<div className="flex-1 w-full">
|
|
||||||
<TerminalWindow title="cladm — 3 selected">
|
|
||||||
<div className="p-3 font-[family-name:var(--font-mono)] text-[10px] leading-relaxed">
|
|
||||||
<div className="text-dim mb-1">
|
|
||||||
{" PROJECT BRANCH LAST USE"}
|
|
||||||
</div>
|
|
||||||
<div className="bg-[#283457] px-1">
|
|
||||||
<span className="text-green">●</span>
|
|
||||||
<span className="text-green"> [✓]</span>
|
|
||||||
<span className="text-text">
|
|
||||||
{" "}
|
|
||||||
acme-api{" "}
|
|
||||||
</span>
|
|
||||||
<span className="text-magenta">main</span>
|
|
||||||
<span className="text-cyan">{" "}25m ago</span>
|
|
||||||
</div>
|
|
||||||
<div className="px-1">
|
|
||||||
<span className="text-yellow">◉</span>
|
|
||||||
<span className="text-dim">2m</span>
|
|
||||||
<span className="text-green">[✓]</span>
|
|
||||||
<span className="text-text"> quantum-dashboard{" "}</span>
|
|
||||||
<span className="text-magenta">feat/cha</span>
|
|
||||||
<span className="text-cyan">{" "}1h ago</span>
|
|
||||||
</div>
|
|
||||||
<div className="px-1">
|
|
||||||
<span className="text-green">●</span>
|
|
||||||
<span className="text-green"> [✓]</span>
|
|
||||||
<span className="text-text"> ml-pipeline{" "}</span>
|
|
||||||
<span className="text-magenta">exp/bert</span>
|
|
||||||
<span className="text-cyan">{" "}just now</span>
|
|
||||||
</div>
|
|
||||||
<div className="px-1">
|
|
||||||
<span className="text-dim">○</span>
|
|
||||||
<span className="text-dim"> [ ]</span>
|
|
||||||
<span className="text-dim"> pixel-engine{" "}develop{" "}3h ago</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</TerminalWindow>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Arrow */}
|
|
||||||
<div className="font-[family-name:var(--font-pixel)] text-accent text-2xl flex-shrink-0 rotate-90 md:rotate-0">
|
|
||||||
>>>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
{/* Claude Code terminals */}
|
|
||||||
<div className="flex-1 w-full">
|
|
||||||
<div className="relative">
|
|
||||||
{/* Stacked terminal windows effect */}
|
|
||||||
<div className="absolute top-3 left-3 right-[-3px] bottom-[-3px] border-2 border-border bg-surface-2 opacity-40" />
|
|
||||||
<div className="absolute top-[6px] left-[6px] right-[-6px] bottom-[-6px] border-2 border-border bg-surface-2 opacity-20" />
|
|
||||||
<TerminalWindow title="claude — acme-api">
|
|
||||||
<Image
|
|
||||||
src="/claude-terminal.webp"
|
|
||||||
alt="Claude Code session launched in Terminal"
|
|
||||||
width={960}
|
|
||||||
height={518}
|
|
||||||
className="w-full"
|
|
||||||
/>
|
|
||||||
</TerminalWindow>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</section>
|
|
||||||
|
|
||||||
{/* ══════ NEWSLETTER ══════ */}
|
{/* ══════ NEWSLETTER ══════ */}
|
||||||
<section className="max-w-5xl mx-auto px-6 py-16">
|
<section className="max-w-5xl mx-auto px-6 py-16">
|
||||||
<div className="max-w-md mx-auto">
|
<div className="max-w-md mx-auto">
|
||||||
@@ -665,7 +785,7 @@ export default function Home() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-6 font-[family-name:var(--font-mono)] text-dim text-[10px] text-center">
|
<div className="mt-6 font-[family-name:var(--font-mono)] text-dim text-[10px] text-center">
|
||||||
Built with Bun + OpenTUI. Pixel art by the cladm creatures.
|
Built with Bun + OpenTUI. Direct PTY grid, no tmux. Pixel art by the cladm creatures.
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import { useEffect, useState, useCallback } from "react";
|
import { useEffect, useState, useCallback } from "react";
|
||||||
import Image from "next/image";
|
|
||||||
|
|
||||||
const projects = [
|
const projects = [
|
||||||
{ name: "acme-api", branch: "main", time: "25m ago", status: "busy" as const },
|
{ name: "acme-api", branch: "main", time: "25m ago", status: "busy" as const },
|
||||||
@@ -10,13 +9,13 @@ const projects = [
|
|||||||
];
|
];
|
||||||
|
|
||||||
type Phase =
|
type Phase =
|
||||||
| "typing" // cladm console visible, cursor selecting projects
|
| "typing"
|
||||||
| "selecting" // checkboxes toggling on one by one
|
| "selecting"
|
||||||
| "enter" // "Enter" flash, cladm fades
|
| "enter"
|
||||||
| "cascade" // terminals fly in
|
| "grid"
|
||||||
| "hold" // terminals visible
|
| "hold"
|
||||||
| "fadeout" // everything fades, restart
|
| "fadeout"
|
||||||
| "pause"; // brief gap before loop
|
| "pause";
|
||||||
|
|
||||||
export function TerminalCascade() {
|
export function TerminalCascade() {
|
||||||
const [phase, setPhase] = useState<Phase>("typing");
|
const [phase, setPhase] = useState<Phase>("typing");
|
||||||
@@ -27,31 +26,18 @@ export function TerminalCascade() {
|
|||||||
setSelectedCount(0);
|
setSelectedCount(0);
|
||||||
setPhase("typing");
|
setPhase("typing");
|
||||||
|
|
||||||
// Typing/appear cladm console
|
|
||||||
const t1 = setTimeout(() => setPhase("selecting"), 800);
|
const t1 = setTimeout(() => setPhase("selecting"), 800);
|
||||||
|
|
||||||
// Toggle checkboxes one by one
|
|
||||||
const t2 = setTimeout(() => setSelectedCount(1), 1200);
|
const t2 = setTimeout(() => setSelectedCount(1), 1200);
|
||||||
const t3 = setTimeout(() => setSelectedCount(2), 1600);
|
const t3 = setTimeout(() => setSelectedCount(2), 1600);
|
||||||
const t4 = setTimeout(() => setSelectedCount(3), 2000);
|
const t4 = setTimeout(() => setSelectedCount(3), 2000);
|
||||||
|
|
||||||
// Enter pressed
|
|
||||||
const t5 = setTimeout(() => setPhase("enter"), 2600);
|
const t5 = setTimeout(() => setPhase("enter"), 2600);
|
||||||
|
const t6 = setTimeout(() => setPhase("grid"), 3400);
|
||||||
// Cascade terminals in
|
const t7 = setTimeout(() => setPhase("hold"), 4000);
|
||||||
const t6 = setTimeout(() => setPhase("cascade"), 3200);
|
const t8 = setTimeout(() => setPhase("fadeout"), 7000);
|
||||||
|
|
||||||
// Hold
|
|
||||||
const t7 = setTimeout(() => setPhase("hold"), 3800);
|
|
||||||
|
|
||||||
// Fade out
|
|
||||||
const t8 = setTimeout(() => setPhase("fadeout"), 6200);
|
|
||||||
|
|
||||||
// Pause then restart
|
|
||||||
const t9 = setTimeout(() => {
|
const t9 = setTimeout(() => {
|
||||||
setPhase("pause");
|
setPhase("pause");
|
||||||
setCycle((c) => c + 1);
|
setCycle((c) => c + 1);
|
||||||
}, 7000);
|
}, 7800);
|
||||||
|
|
||||||
return [t1, t2, t3, t4, t5, t6, t7, t8, t9];
|
return [t1, t2, t3, t4, t5, t6, t7, t8, t9];
|
||||||
}, []);
|
}, []);
|
||||||
@@ -65,19 +51,18 @@ export function TerminalCascade() {
|
|||||||
return () => clearTimeout(start);
|
return () => clearTimeout(start);
|
||||||
}, [cycle, runCycle]);
|
}, [cycle, runCycle]);
|
||||||
|
|
||||||
const showCladm = phase === "typing" || phase === "selecting" || phase === "enter";
|
const showPicker = phase === "typing" || phase === "selecting" || phase === "enter";
|
||||||
const showCascade = phase === "cascade" || phase === "hold" || phase === "fadeout";
|
const showGrid = phase === "grid" || phase === "hold" || phase === "fadeout";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative w-full min-h-[340px]">
|
<div className="relative w-full min-h-[360px]">
|
||||||
{/* ── CLADM console (cause) ── */}
|
{/* ── Picker (select projects) ── */}
|
||||||
<div
|
<div
|
||||||
className={`transition-all duration-500 ${
|
className={`transition-all duration-500 ${
|
||||||
showCladm ? "opacity-100 scale-100" : "opacity-0 scale-95 pointer-events-none absolute inset-0"
|
showPicker ? "opacity-100 scale-100" : "opacity-0 scale-95 pointer-events-none absolute inset-0"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="pixel-border bg-surface overflow-hidden">
|
<div className="pixel-border bg-surface overflow-hidden">
|
||||||
{/* Title bar */}
|
|
||||||
<div className="flex items-center gap-2 px-4 py-2 bg-surface-2 border-b-2 border-border">
|
<div className="flex items-center gap-2 px-4 py-2 bg-surface-2 border-b-2 border-border">
|
||||||
<div className="w-3 h-3 bg-[#ff5f56]" />
|
<div className="w-3 h-3 bg-[#ff5f56]" />
|
||||||
<div className="w-3 h-3 bg-[#ffbd2e]" />
|
<div className="w-3 h-3 bg-[#ffbd2e]" />
|
||||||
@@ -86,7 +71,6 @@ export function TerminalCascade() {
|
|||||||
cladm — {selectedCount} selected
|
cladm — {selectedCount} selected
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
{/* Project rows */}
|
|
||||||
<div className="p-3 font-[family-name:var(--font-mono)] text-[11px] leading-relaxed">
|
<div className="p-3 font-[family-name:var(--font-mono)] text-[11px] leading-relaxed">
|
||||||
<div className="text-dim mb-1 text-[10px]">
|
<div className="text-dim mb-1 text-[10px]">
|
||||||
{" PROJECT BRANCH LAST USE"}
|
{" PROJECT BRANCH LAST USE"}
|
||||||
@@ -121,15 +105,14 @@ export function TerminalCascade() {
|
|||||||
<span>○</span><span> </span>[ ] pixel-engine{" "}develop{" "}3h ago
|
<span>○</span><span> </span>[ ] pixel-engine{" "}develop{" "}3h ago
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Enter hint */}
|
|
||||||
<div className="mt-3 pt-2 border-t border-border text-[10px]">
|
<div className="mt-3 pt-2 border-t border-border text-[10px]">
|
||||||
{phase === "enter" ? (
|
{phase === "enter" ? (
|
||||||
<span className="text-accent font-bold cascade-flash">
|
<span className="text-accent font-bold cascade-flash">
|
||||||
⏎ Launching 3 projects...
|
⏎ Launching 3 sessions into grid...
|
||||||
</span>
|
</span>
|
||||||
) : (
|
) : (
|
||||||
<span className="text-dim">
|
<span className="text-dim">
|
||||||
↑↓ navigate · space toggle · enter launch
|
↑↓ navigate · space toggle · enter launch grid
|
||||||
</span>
|
</span>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
@@ -137,45 +120,92 @@ export function TerminalCascade() {
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* ── Terminal cascade (effect) ── */}
|
{/* ── Grid workspace (result) ── */}
|
||||||
<div
|
<div
|
||||||
className={`transition-opacity duration-500 ${
|
className={`transition-opacity duration-600 ${
|
||||||
showCascade ? "opacity-100" : "opacity-0 pointer-events-none absolute inset-0"
|
showGrid ? "opacity-100" : "opacity-0 pointer-events-none absolute inset-0"
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
<div className="relative h-[320px]">
|
<div className={`pixel-border bg-surface overflow-hidden ${phase === "hold" ? "cascade-glow" : phase === "grid" ? "cascade-in" : ""}`}>
|
||||||
{projects.map((proj, i) => (
|
{/* Tab bar */}
|
||||||
<div
|
<div className="flex items-center bg-surface-2 border-b-2 border-border">
|
||||||
key={`term-${proj.name}-${cycle}`}
|
<div className="px-3 py-1.5 border-b-2 border-accent font-[family-name:var(--font-mono)] text-[9px]">
|
||||||
className={`absolute left-0 right-0 border-2 bg-surface overflow-hidden
|
<span className="text-green">●</span>
|
||||||
${phase === "cascade" || phase === "hold" ? "cascade-in" : ""}
|
<span className="text-text"> acme-api</span>
|
||||||
${phase === "hold" && i === projects.length - 1 ? "cascade-glow" : ""}`}
|
<span className="text-dim"> · </span>
|
||||||
style={{
|
<span className="text-yellow">◉</span>
|
||||||
animationDelay: `${i * 0.2}s`,
|
<span className="text-text"> quantum-dash</span>
|
||||||
top: `${i * 80}px`,
|
|
||||||
marginLeft: `${i * 16}px`,
|
|
||||||
marginRight: `${(projects.length - 1 - i) * 16}px`,
|
|
||||||
zIndex: i + 1,
|
|
||||||
borderColor: "var(--color-border)",
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<div className="flex items-center gap-1.5 px-3 py-1 bg-surface-2 border-b border-border">
|
|
||||||
<div className="w-[7px] h-[7px] bg-[#ff5f56]" />
|
|
||||||
<div className="w-[7px] h-[7px] bg-[#ffbd2e]" />
|
|
||||||
<div className="w-[7px] h-[7px] bg-[#27c93f]" />
|
|
||||||
<span className="ml-2 font-[family-name:var(--font-mono)] text-dim text-[9px] truncate">
|
|
||||||
claude — {proj.name}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<Image
|
|
||||||
src="/claude-welcome.png"
|
|
||||||
alt="Claude Code welcome screen"
|
|
||||||
width={570}
|
|
||||||
height={260}
|
|
||||||
className="w-full h-auto"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
))}
|
<div className="px-3 py-1.5 font-[family-name:var(--font-mono)] text-[9px] text-dim border-b-2 border-transparent">
|
||||||
|
<span className="text-green">●</span>
|
||||||
|
<span> ml-pipeline</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Pane grid */}
|
||||||
|
<div className="grid grid-cols-2 gap-px bg-border">
|
||||||
|
{/* Pane 1: acme-api (busy) */}
|
||||||
|
<div className="bg-surface">
|
||||||
|
<div className="flex items-center justify-between px-2 py-[3px] border-b border-border">
|
||||||
|
<div className="font-[family-name:var(--font-mono)] text-[8px]">
|
||||||
|
<span className="text-green">●</span>
|
||||||
|
<span className="text-text"> acme-api</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-[3px]">
|
||||||
|
<span className="text-cyan text-[6px]">●</span>
|
||||||
|
<span className="text-[#27c93f] text-[6px]">●</span>
|
||||||
|
<span className="text-[#ff5f56] text-[6px]">●</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-2 font-[family-name:var(--font-mono)] text-[8px] text-dim leading-[1.6] h-[85px]">
|
||||||
|
<div className="text-green">> I'll fix the token refresh bug</div>
|
||||||
|
<div>Reading src/auth/token.ts...</div>
|
||||||
|
<div>Reading src/auth/middleware.ts...</div>
|
||||||
|
<div>Grep: refreshToken pattern<span className="cursor-blink text-accent">_</span></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Pane 2: quantum-dash (idle) */}
|
||||||
|
<div className="bg-surface">
|
||||||
|
<div className="flex items-center justify-between px-2 py-[3px] border-b border-border">
|
||||||
|
<div className="font-[family-name:var(--font-mono)] text-[8px]">
|
||||||
|
<span className="text-yellow">◉</span>
|
||||||
|
<span className="text-dim"> 4m </span>
|
||||||
|
<span className="text-text">quantum-dash</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-[3px]">
|
||||||
|
<span className="text-cyan text-[6px]">●</span>
|
||||||
|
<span className="text-[#27c93f] text-[6px]">●</span>
|
||||||
|
<span className="text-[#ff5f56] text-[6px]">●</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-2 font-[family-name:var(--font-mono)] text-[8px] text-dim leading-[1.6] h-[85px]">
|
||||||
|
<div className="text-text">Updated chart component</div>
|
||||||
|
<div className="text-text">New hook: useChartData.ts</div>
|
||||||
|
<div className="text-yellow mt-1">Waiting for input...</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Pane 3: ml-pipeline (busy, full width) */}
|
||||||
|
<div className="bg-surface col-span-2">
|
||||||
|
<div className="flex items-center justify-between px-2 py-[3px] border-b border-border">
|
||||||
|
<div className="font-[family-name:var(--font-mono)] text-[8px]">
|
||||||
|
<span className="text-green">●</span>
|
||||||
|
<span className="text-text"> ml-pipeline</span>
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-[3px]">
|
||||||
|
<span className="text-cyan text-[6px]">●</span>
|
||||||
|
<span className="text-[#27c93f] text-[6px]">●</span>
|
||||||
|
<span className="text-[#ff5f56] text-[6px]">●</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="p-2 font-[family-name:var(--font-mono)] text-[8px] text-dim leading-[1.6] h-[65px]">
|
||||||
|
<div className="text-green">> Building BERT fine-tuning pipeline</div>
|
||||||
|
<div>Processing dataset: train.jsonl</div>
|
||||||
|
<div>Epoch 3/10 <span className="text-accent">████████</span><span className="text-border">░░░░░░░░░░░░</span> 30%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user