import { useState, useEffect, useRef } from 'react'; // Inline SVG icons (Lucide-style paths). The artifact carries no external // module dependency — the sandbox runtime doesn't register lucide-react, so // importing it crashes the whole tree. Self-contained SVGs sidestep that. function Svg({ size = 16, className = '', children }) { return ( ); } const GitFork = (p) => ; const GitBranch = (p) => ; const Sparkles = (p) => ; const Boxes = (p) => ; const Bell = (p) => ; const Check = (p) => ; const RotateCcw = (p) => ; const ChevronRight = (p) => ; const Cpu = (p) => ; const Zap = (p) => ; const Play = (p) => ; const CircleDot = (p) => ; const Database = (p) => ; const SkipForward = (p) => ; // Category palette — mirrors forkhub's signal categories (models.py). const CAT = { feature: { chip: 'bg-emerald-500/15 text-emerald-300 border-emerald-500/30', bar: 'bg-emerald-400', dot: 'bg-emerald-400' }, fix: { chip: 'bg-sky-500/15 text-sky-300 border-sky-500/30', bar: 'bg-sky-400', dot: 'bg-sky-400' }, refactor: { chip: 'bg-violet-500/15 text-violet-300 border-violet-500/30', bar: 'bg-violet-400', dot: 'bg-violet-400' }, dependency: { chip: 'bg-zinc-500/15 text-zinc-300 border-zinc-500/30', bar: 'bg-zinc-400', dot: 'bg-zinc-400' }, }; // The upstream repo and its fork constellation. All data is inline — // the sandbox blocks network calls, so the "GitHub API" is faked here. const UPSTREAM = { name: 'tldr-pages/tldr', head: 'a1f3c9d', stars: '52.1k' }; const FORKS = [ { id: 'alice', head: '7b2e004', changed: true, commits: ['feat: add dark-mode theme', 'style: dim ANSI palette'], files: ['themes/dark.css', 'src/config.js'], signal: { category: 'feature', sig: 8, summary: 'Adds a dark-mode color theme with a dimmed ANSI palette' }, cluster: 'dark-mode', }, { id: 'carol', head: 'c40aa12', changed: true, commits: ['add dark theme support'], files: ['themes/dark.css'], signal: { category: 'feature', sig: 7, summary: 'Adds dark theme support for terminal output' }, cluster: 'dark-mode', }, { id: 'bob', head: 'd91f7a3', changed: true, commits: ['fix: unicode width on CJK glyphs'], files: ['src/render.py'], signal: { category: 'fix', sig: 5, summary: 'Fixes column alignment for wide CJK characters' }, }, { id: 'frank', head: 'f2b0c55', changed: true, commits: ['refactor: extract parser module'], files: ['src/parser.py', 'src/index.py'], signal: { category: 'refactor', sig: 4, summary: 'Extracts the page parser into its own module' }, }, { id: 'dave', head: '5e7c118', changed: true, commits: ['chore: bump deps'], files: ['package.json', 'package-lock.json'], signal: { category: 'dependency', sig: 2, summary: 'Routine dependency version bumps' }, }, { id: 'erin', head: 'a1f3c9d', changed: false, // HEAD == last sync → skipped commits: [], files: [], }, ]; const STAGES = [ { key: 'discover', label: 'Discover', icon: GitFork }, { key: 'sync', label: 'Sync', icon: GitBranch }, { key: 'analyze', label: 'Analyze', icon: Sparkles }, { key: 'cluster', label: 'Cluster', icon: Boxes }, { key: 'digest', label: 'Digest', icon: Bell }, ]; const CAPTIONS = { discover: { title: 'Discover the forks', body: 'ForkHub asks the GitHub API for every fork of the upstream repo. Conditional requests with ETags keep this cheap against the rate limit — unchanged pages return 304 with no body.', }, sync: { title: 'Sync — skip what hasn’t moved', body: 'Each fork’s HEAD SHA is compared to what was stored last run. erin/tldr still points at the upstream SHA, so it’s skipped — no diffs fetched, no tokens spent. Five forks actually diverged.', }, analyze: { title: 'Analyze with an agent', body: 'A Claude Agent SDK coordinator dispatches a diff-analyst subagent (Sonnet) per changed fork. It starts with file lists + commit messages (cheap), pulls full diffs only when a file looks interesting, then calls store_signal with a category and a 1–10 significance score. A budget cap halts runaway cost.', }, cluster: { title: 'Cluster the divergences', body: 'Each signal’s summary is embedded into a vector. sqlite-vec finds signals with high cosine similarity. alice and carol independently built dark mode — their signals collapse into one cluster, the kind of cross-fork convergence ForkHub exists to surface.', }, digest: { title: 'Compose the digest', body: 'A digest-writer subagent (Haiku) writes the notification. A significance filter drops low-signal noise — dave’s dependency bump (2) and frank’s refactor (4) fall below the threshold. You get the interesting divergences, not every commit.', }, }; const SIG_THRESHOLD = 5; const COST_PER_ANALYST = 0.012; const MAX_BUDGET = 0.5; function Avatar({ id, dim }) { return (
#{fork.head}
{fork.signal.summary}
{digested && (Watch the fork constellation. Surface what diverged, and why.
sync → analyze → digest pipeline.