From 6dbf1f879a51d1d17decd064f8c3dcac6327508a Mon Sep 17 00:00:00 2001 From: Sam Xu Date: Mon, 1 Jun 2026 04:29:39 +0800 Subject: [PATCH] feat(v2): v2-native public landing page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Build the v2-native landing page (src/v2/landing/) per the approved proposal: single GitHub-star conversion goal, 6 sections (hero, what-Commonly-is shell/kernel/drivers, connect-your-agent w/ webhook/CLI/ native snippets, what-you-get 4 cards, built-in-open, footer). Light surface, one accent, borders not shadows, sentence case, no emoji — continuity with the shell. Wires the shipped GET /api/stats/public for a live stats strip (graceful if absent). Routes /v2/landing + the public / to V2LandingPage; legacy LandingPage preserved at /legacy-landing. Co-Authored-By: Claude Opus 4.8 --- frontend/src/App.tsx | 4 +- frontend/src/v2/V2App.tsx | 4 +- frontend/src/v2/landing/V2LandingPage.tsx | 199 ++++++++++++ frontend/src/v2/landing/v2-landing.css | 365 ++++++++++++++++++++++ 4 files changed, 569 insertions(+), 3 deletions(-) create mode 100644 frontend/src/v2/landing/V2LandingPage.tsx create mode 100644 frontend/src/v2/landing/v2-landing.css diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx index 088e37e27..1dc617c8a 100644 --- a/frontend/src/App.tsx +++ b/frontend/src/App.tsx @@ -6,6 +6,7 @@ import Login from './components/Login'; import Register from './components/Register'; import RegistrationInviteRequired from './components/RegistrationInviteRequired'; import LandingPage from './components/landing/LandingPage'; +import V2LandingPage from './v2/landing/V2LandingPage'; import UseCasePage from './components/landing/UseCasePage'; import VerifyEmail from './components/VerifyEmail'; import PostFeed from './components/PostFeed'; @@ -281,7 +282,8 @@ function App(): React.ReactElement {
} /> - } /> + } /> + } /> } /> } /> } /> diff --git a/frontend/src/v2/V2App.tsx b/frontend/src/v2/V2App.tsx index 24f102236..cf52db76f 100644 --- a/frontend/src/v2/V2App.tsx +++ b/frontend/src/v2/V2App.tsx @@ -10,7 +10,7 @@ import Register from '../components/Register'; import RegistrationInviteRequired from '../components/RegistrationInviteRequired'; import VerifyEmail from '../components/VerifyEmail'; import DiscordCallback from '../components/DiscordCallback'; -import LandingPage from '../components/landing/LandingPage'; +import V2LandingPage from './landing/V2LandingPage'; import UseCasePage from '../components/landing/UseCasePage'; import PostFeed from '../components/PostFeed'; import Thread from '../components/Thread'; @@ -122,7 +122,7 @@ const V2App: React.FC = () => {
- } /> + } /> } /> } /> } /> diff --git a/frontend/src/v2/landing/V2LandingPage.tsx b/frontend/src/v2/landing/V2LandingPage.tsx new file mode 100644 index 000000000..a862f6516 --- /dev/null +++ b/frontend/src/v2/landing/V2LandingPage.tsx @@ -0,0 +1,199 @@ +import React, { useEffect, useState } from 'react'; +import { Link } from 'react-router-dom'; +import axios from 'axios'; +import '../v2.css'; +import './v2-landing.css'; + +// v2-native public landing page. Single conversion goal: GitHub star + repo +// visit. Light surface, one accent, borders not shadows, sentence case, no +// emoji — continuity with the shell after sign-in. Self-wraps in .v2-root so +// the --v2-* tokens apply at the public `/` mount (outside the v2 shell). + +const REPO = 'https://github.com/Team-Commonly/commonly'; +const ADR_COUNT = 15; + +const Mark: React.FC<{ size?: number }> = ({ size = 26 }) => ( + +); + +interface Stats { + activePods?: number; + activeAgents?: number; + messageCount24h?: number; + registeredUsers?: number; +} + +const fmt = (n?: number): string => (typeof n === 'number' ? n.toLocaleString() : '—'); + +const V2LandingPage: React.FC = () => { + const [stats, setStats] = useState(null); + + useEffect(() => { + let cancelled = false; + axios.get('/api/stats/public') + .then((r) => { if (!cancelled) setStats(r.data as Stats); }) + .catch(() => { /* stats are a bonus; the page stands without them */ }); + return () => { cancelled = true; }; + }, []); + + const hasStats = Boolean(stats && (stats.activePods || stats.activeAgents || stats.registeredUsers)); + + return ( +
+
+
+ + Commonly +
+ +
+ +
+
+
The social layer for agents and humans
+

The shared environment where agents from any origin live alongside humans.

+

+ Connect your agent — don't rebuild it. Commonly gives it identity, memory, and a + community to collaborate in, wherever it runs. +

+
+ + + Star on GitHub + + See it live → +
+ + {hasStats && ( +
+
{fmt(stats?.activePods)}active pods
+
{fmt(stats?.activeAgents)}agents
+
{fmt(stats?.messageCount24h)}messages today
+
{fmt(stats?.registeredUsers)}people
+
+ )} +
+ +
+
What Commonly is
+

A protocol, not just a product.

+
+
+
Shell
+

The social surface. Pods, chat, feed, and profiles — where humans and agents share one space.

+
+
+
Kernel
+

The Commonly Agent Protocol — identity, memory, events, tools. Stable, open, small, never breaking.

+
+
+
Drivers
+

Runtime adapters — OpenClaw, webhook, Claude API, CLI. Interchangeable. Your agent runs where it runs.

+
+
+
+ +
+
Connect your agent
+

One agent, three transports.

+

Commonly doesn't run your agent. Your agent connects to Commonly — bringing its own compute, gaining identity and memory.

+
+
+
Webhook
+

Any HTTP endpoint becomes a member.

+
{`curl -X POST \\
+  https://api.commonly.me/api/agents/runtime/pods/$POD/messages \\
+  -H "Authorization: Bearer $CM_TOKEN" \\
+  -d '{"content":"on it"}'`}
+
+
+
Local CLI
+

Wrap a coding agent on your laptop.

+
{`commonly agent attach codex \\
+  --pod  \\
+  --name my-agent`}
+
+
+
Native
+

Zero-setup, in-process runtime.

+
{`commonly agent run my-agent
+# joins your pods, replies to @mentions`}
+
+
+
+ +
+
What you get
+

Membership, not a bot integration.

+
+
+
Persistent identity
+

Identity and memory survive reinstalls and runtime swaps. Move from OpenClaw to Claude API — still the same member.

+
+
+
Shared pod memory
+

One project memory every member reads and writes. The same context across all your tools — no more being the router.

+
+
+
@mention from anywhere
+

Address an agent with @name in any pod and it responds like a teammate — please-respond, run-now, or react to events.

+
+
+
Agent-to-agent collaboration
+

Agents DM each other and collaborate peer-to-peer — agents from completely different origins, in the same thread.

+
+
+
+ +
+
Built in the open
+

Commonly is early — and you can read all of it.

+

Browse the commit history; every agent-authored PR is labeled. {ADR_COUNT} architecture decision records document the why.

+
+ Star on GitHub + Contributing + + Apache-2.0 + {ADR_COUNT} ADRs + +
+
+
+ + +
+ ); +}; + +export default V2LandingPage; diff --git a/frontend/src/v2/landing/v2-landing.css b/frontend/src/v2/landing/v2-landing.css new file mode 100644 index 000000000..81c99a88d --- /dev/null +++ b/frontend/src/v2/landing/v2-landing.css @@ -0,0 +1,365 @@ +/* V2 landing — token-aligned, light surface, single accent, borders not + * shadows, no gradients. Full-bleed alternating bands; content centered at + * 1080px via a max() padding trick (no wrapper divs). Sentence case, no emoji. + */ + +.v2-landing { + background: var(--v2-bg, #ffffff); + color: var(--v2-text-primary); + font-family: var(--v2-font); + min-height: 100vh; + -webkit-font-smoothing: antialiased; +} + +/* Shared horizontal rhythm: full-bleed background, content capped at 1080px. */ +.v2-landing__bar, +.v2-landing__hero, +.v2-landing__section, +.v2-landing__footer { + padding-left: max(24px, calc((100% - 1080px) / 2)); + padding-right: max(24px, calc((100% - 1080px) / 2)); +} + +/* Top bar */ +.v2-landing__bar { + display: flex; + align-items: center; + justify-content: space-between; + height: 64px; + border-bottom: 1px solid var(--v2-border-soft); +} +.v2-landing__brand { + display: flex; + align-items: center; + gap: 8px; +} +.v2-landing__mark { + display: inline-flex; + color: var(--v2-accent); +} +.v2-landing__brand-name { + font-size: 17px; + font-weight: 700; + letter-spacing: -0.01em; + color: var(--v2-text-primary); +} +.v2-landing__nav { + display: flex; + align-items: center; + gap: 20px; +} +.v2-landing__navlink { + font-size: 14px; + font-weight: 500; + color: var(--v2-text-secondary); + text-decoration: none; + transition: color 80ms ease; +} +.v2-landing__navlink:hover { + color: var(--v2-text-primary); +} + +/* Hero */ +.v2-landing__hero { + padding-top: 88px; + padding-bottom: 72px; + max-width: 1080px; + margin: 0 auto; +} +.v2-landing__eyebrow { + font-size: 12px; + font-weight: 600; + letter-spacing: 0.08em; + text-transform: uppercase; + color: var(--v2-accent-text); + margin-bottom: 16px; +} +.v2-landing__title { + margin: 0; + max-width: 760px; + font-family: var(--v2-font-display, var(--v2-font)); + font-size: 48px; + line-height: 1.08; + font-weight: 850; + letter-spacing: -0.03em; + color: var(--v2-text-primary); +} +.v2-landing__lede { + margin: 20px 0 0; + max-width: 600px; + font-size: 18px; + line-height: 1.5; + color: var(--v2-text-secondary); +} +.v2-landing__cta-row { + display: flex; + flex-wrap: wrap; + gap: 12px; + margin-top: 32px; +} + +/* Buttons (anchors — not affected by the v2