Files
claudemesh/apps/web/src/modules/marketing/home/timeline.tsx
Alejandro Gutiérrez 64d9f9f6f9
Some checks failed
CI / Typecheck (push) Has been cancelled
CI / Lint (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled
feat(web): refresh marketing site — accurate timeline, live changelog, cross-boundary positioning
The site had drifted ~6 months behind the product. Three problems
addressed in one push:

1. Timeline ("Shipped, not promised") topped out at v0.6–0.8 and
   claimed "66 npm releases" — both stale. Adds a v0.9 → 1.34 tier
   covering daemon, multi-mesh, multi-session correctness train,
   refuse-to-kick on control-plane, env-var fallback. Updates count
   to "120+ npm releases through v1.34.15." Rewrites the "next"
   block from the now-shipped "Daemon redesign · per-topic
   encryption" to the actually-pending "HKDF cross-machine identity
   · session capabilities · A2A interop · self-host packaging ·
   federation."

2. Hero subhead leaned into the original "Claude Code peer mesh"
   framing, which is undercut by Anthropic Agent Teams (Feb 2026,
   single-machine native mailbox). Now reframes claudemesh as the
   encrypted backbone where Claude Code sessions, autonomous
   agents, and humans coordinate "across machines, across users,
   across organizations" — the four words that distinguish the
   product from anything Anthropic structurally can ship from
   inside Claude Code.

3. /changelog had three entries from April 2026 (v0.1.2 → v0.1.4)
   and was 70+ versions out of date. Replaced with a curated
   16-entry timeline from v0.1.0 → v1.34.15, hand-picked to tell
   the story (load-bearing ships, not every patch). Adds links
   back to docs/roadmap.md, .artifacts/specs/, and GitHub Releases.

New module: apps/web/src/modules/marketing/home/changelog-data.ts
holds the curated entries as a single source of truth. Imported by
both the /changelog page and a new home-page component
LatestReleases (compact 5-entry strip, slotted between Timeline
and Pricing) so they never disagree.

Misc fixes pulled in:
- timeline.tsx had glyph="layers" which isn't in SectionIcon's
  valid set; switched to "grid" (changelog-data.ts uses same).
- changelog data extracted to a non-route module so Next.js's
  route-export validator stops complaining about exporting
  CHANGELOG_ENTRIES from app/.../changelog/page.tsx.

Pre-existing typecheck noise in packages/ui/web/sidebar.tsx
(csstype version mismatch) + billing modules unrelated to this
change. My files all typecheck clean.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-04 22:55:30 +01:00

243 lines
9.2 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use client";
import { useRef } from "react";
import { Reveal, SectionIcon } from "./_reveal";
const MILESTONES = [
{
version: "v0.1",
phase: "Foundation",
color: "var(--cm-clay)",
items: [
"E2E encrypted messaging (libsodium crypto_box)",
"WSS broker with reconnect + priority routing",
"ed25519 identity + signed invite links",
"claudemesh launch with dev-channel push",
"Named sessions + ephemeral keypairs",
"Production hardening (stale sweep, sender exclusion)",
],
stat: "16 releases",
},
{
version: "v0.2",
phase: "Groups",
color: "var(--cm-fig)",
items: [
"@group routing with roles (lead, member, observer)",
"Interactive wizard for launch configuration",
"Dynamic join/leave groups at runtime",
"Multicast delivery with sender exclusion",
],
stat: "6 coordination patterns",
},
{
version: "v0.3",
phase: "Shared Intelligence",
color: "var(--cm-cactus)",
items: [
"Shared state — live key-value with push notifications",
"Memory — persistent knowledge with full-text search",
"Message status — per-recipient delivery tracking",
"MCP instructions — dynamic identity + tool guide",
],
stat: "Peers learn collectively",
},
{
version: "v0.4",
phase: "Files & Targeting",
color: "var(--cm-oat)",
items: [
"MinIO file sharing with per-peer access control",
"Message attachments (ephemeral, 24h TTL)",
"Multi-target messages with deduplication",
"Targeted views — per-audience message tailoring",
],
stat: "Binary artifacts + text",
},
{
version: "v0.5",
phase: "Data Platform",
color: "var(--cm-clay)",
items: [
"Per-mesh SQL database (Postgres schema)",
"Vector search (Qdrant semantic embeddings)",
"Graph database (Neo4j entity relationships)",
"Context sharing between peer sessions",
"Tasks — create, claim, complete work items",
"Streams — real-time pub/sub data channels",
],
stat: "5 persistence backends",
},
{
version: "v0.60.8",
phase: "Platform",
color: "var(--cm-fig)",
items: [
"Mesh MCP proxy — dynamic tool sharing between peers",
"Skills catalog — publish + discover reusable instructions",
"Signed hash-chain audit log for mesh events",
"Inbound webhooks for external integrations",
"Scheduled messages + cron-based reminders",
"Mesh services — deploy MCP servers with vault + scopes",
"Runner container for git/npx service sources",
"URL watch — broker polls URLs, notifies on change",
"Telegram bridge with multi-tenant routing",
"Peer stats reporting (messages, uptime, errors)",
],
stat: "43 MCP tools total",
},
{
version: "v0.9 → 1.34",
phase: "Daemon · multi-mesh · multi-session",
color: "var(--cm-cactus)",
items: [
"Persistent daemon — long-lived broker WS, durable outbox/inbox",
"Universal multi-mesh daemon — one process, every joined mesh",
"Per-session IPC tokens — auto-scope to the launched session",
"Per-session broker presence — sibling sessions see each other",
"Self-healing daemon lifecycle (auto-spawn, version probe)",
"Multi-session correctness train — per-recipient SSE demux + inbox scoping",
"Refuse-to-kick on control-plane (no more no-op kicks)",
"Caller-stable idempotency on every send",
"Stale CLAUDEMESH_CONFIG_DIR fallback",
],
stat: "1.34.15 shipped",
},
];
export const Timeline = () => {
const trackRef = useRef<HTMLDivElement>(null);
return (
<section className="border-b border-[var(--cm-border)] bg-[var(--cm-bg)] px-6 py-24 md:px-12 md:py-32">
<div className="mx-auto max-w-[var(--cm-max-w)]">
<Reveal className="mb-6 flex justify-center">
<SectionIcon glyph="grid" />
</Reveal>
<Reveal delay={1}>
<h2
className="text-center text-[clamp(2rem,4.5vw,3.25rem)] font-medium leading-[1.1] text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Shipped, not promised
</h2>
</Reveal>
<Reveal delay={2}>
<p
className="mx-auto mt-4 max-w-xl text-center text-[15px] leading-[1.6] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
120+ npm releases through v1.34.15. Every feature below is in
production today.
</p>
</Reveal>
<Reveal delay={3}>
<div ref={trackRef} className="relative mt-16">
{/* Vertical line */}
<div
className="absolute left-[24px] top-0 hidden h-full w-px md:block"
style={{ background: "linear-gradient(to bottom, var(--cm-clay), var(--cm-fig), var(--cm-cactus), transparent)" }}
/>
<div className="space-y-12 md:space-y-16">
{MILESTONES.map((m, idx) => (
<div key={m.version} className="relative md:pl-16">
{/* Dot on timeline */}
<div
className="absolute left-[17px] top-[6px] hidden h-[15px] w-[15px] rounded-full border-2 md:block"
style={{
borderColor: m.color,
backgroundColor: "var(--cm-bg)",
}}
>
<div
className="absolute inset-[3px] rounded-full"
style={{ backgroundColor: m.color }}
/>
</div>
{/* Content */}
<div className="rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] p-6 transition-colors hover:border-[color:var(--hover-color)]"
style={{ "--hover-color": m.color } as React.CSSProperties}
>
{/* Header */}
<div className="mb-4 flex items-baseline justify-between gap-4">
<div className="flex items-center gap-3">
<span
className="rounded-[4px] px-2 py-0.5 text-[11px] font-medium"
style={{
fontFamily: "var(--cm-font-mono)",
backgroundColor: m.color,
color: "var(--cm-gray-900)",
}}
>
{m.version}
</span>
<h3
className="text-[18px] font-medium text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
{m.phase}
</h3>
</div>
<span
className="hidden shrink-0 text-[11px] text-[var(--cm-fg-tertiary)] sm:block"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
{m.stat}
</span>
</div>
{/* Items grid */}
<div className="grid gap-x-6 gap-y-1.5 sm:grid-cols-2">
{m.items.map((item) => (
<div
key={item}
className="flex items-start gap-2 text-[13px] leading-[1.5] text-[var(--cm-fg-secondary)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
<span
className="mt-[7px] block h-[5px] w-[5px] shrink-0 rounded-full"
style={{ backgroundColor: m.color, opacity: 0.6 }}
/>
<span>{item}</span>
</div>
))}
</div>
</div>
</div>
))}
</div>
{/* Bottom: what's next */}
<div className="relative mt-12 md:pl-16">
<div
className="absolute left-[17px] top-[6px] hidden h-[15px] w-[15px] rounded-full border-2 border-dashed border-[var(--cm-fg-tertiary)] md:block"
/>
<div
className="rounded-[var(--cm-radius-md)] border border-dashed border-[var(--cm-border)] p-6"
>
<div className="flex items-center gap-3">
<span
className="rounded-[4px] border border-[var(--cm-fg-tertiary)] px-2 py-0.5 text-[11px] text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
next
</span>
<span
className="text-[14px] text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
HKDF cross-machine identity · session capabilities · A2A
interop · self-host packaging · federation
</span>
</div>
</div>
</div>
</div>
</Reveal>
</div>
</section>
);
};