Files
claudemesh/apps/web/src/modules/join/install-toggle.tsx
Alejandro Gutiérrez 5cb4cc4fe7
Some checks failed
CI / Lint (push) Has been cancelled
CI / Typecheck (push) Has been cancelled
CI / Broker tests (Postgres) (push) Has been cancelled
CI / Docker build (linux/amd64) (push) Has been cancelled
feat(web): update landing page copy for full feature surface, add getting started + mesh vs MCP
Landing page copy was stuck at the v0.1 feature set (messaging + state + memory + groups).
The CLI now ships 43 MCP tools across 5 persistence backends. This commit brings the site
copy in sync with what's actually built.

Changes:
- Hero, features, pricing, FAQ, CTA, footer: reflect 43 tools, files, SQL, vectors, graphs
- Features section: expanded from 4 tabs to 7 (added Files, Database, Vectors)
- New /getting-started page: full install guide with correct 4-step flow
- New Mesh vs MCP section: side-by-side diagrams + 8-row comparison table
- Fix: install-toggle on /join page had `npx claudemesh@latest init` (init doesn't exist)
  → replaced with `curl -fsSL https://claudemesh.com/install | bash`
- Navigation: added Getting Started to header, footer, hero link
- COPY.md synced with all 6 capability areas

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-07 22:58:22 +01:00

194 lines
7.8 KiB
TypeScript

"use client";
import { useState } from "react";
interface Props {
token: string;
}
const JOIN_CMD = (token: string) => `claudemesh join ${token}`;
const INSTALL_CMD = "curl -fsSL https://claudemesh.com/install | bash";
export const InstallToggle = ({ token }: Props) => {
const [hasCli, setHasCli] = useState<"unknown" | "yes" | "no">("unknown");
const [copiedKey, setCopiedKey] = useState<string | null>(null);
const copy = async (text: string, key: string) => {
await navigator.clipboard.writeText(text);
setCopiedKey(key);
setTimeout(() => setCopiedKey(null), 2000);
};
if (hasCli === "unknown") {
return (
<div className="flex flex-col gap-3 sm:flex-row">
<button
onClick={() => setHasCli("no")}
className="flex-1 rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] p-5 text-left transition-colors hover:border-[var(--cm-clay)] hover:bg-[var(--cm-bg-hover)]"
>
<div
className="mb-1.5 text-[11px] uppercase tracking-[0.18em] text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
first time
</div>
<div
className="text-lg font-medium text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Install claudemesh
</div>
</button>
<button
onClick={() => setHasCli("yes")}
className="flex-1 rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] p-5 text-left transition-colors hover:border-[var(--cm-clay)] hover:bg-[var(--cm-bg-hover)]"
>
<div
className="mb-1.5 text-[11px] uppercase tracking-[0.18em] text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
already set up
</div>
<div
className="text-lg font-medium text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Join with CLI
</div>
</button>
</div>
);
}
if (hasCli === "yes") {
const cmd = JOIN_CMD(token);
return (
<div className="space-y-4">
<div className="rounded-[var(--cm-radius-md)] border border-[var(--cm-clay)]/40 bg-[var(--cm-bg-elevated)] p-5">
<div
className="mb-2 text-[11px] uppercase tracking-[0.18em] text-[var(--cm-clay)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
run this in your terminal
</div>
<div className="flex items-center gap-2">
<code
className="flex-1 overflow-x-auto rounded-[var(--cm-radius-xs)] bg-[var(--cm-bg)] p-3 text-sm text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
{cmd}
</code>
<button
onClick={() => copy(cmd, "join")}
className="rounded-[var(--cm-radius-xs)] bg-[var(--cm-clay)] px-4 py-3 text-sm font-medium text-[var(--cm-fg)] transition-colors hover:bg-[var(--cm-clay-hover)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
{copiedKey === "join" ? "Copied ✓" : "Copy"}
</button>
</div>
</div>
<button
onClick={() => setHasCli("unknown")}
className="text-xs text-[var(--cm-fg-tertiary)] underline underline-offset-4 hover:text-[var(--cm-fg)]"
>
Need to install first?
</button>
</div>
);
}
const joinCmd = JOIN_CMD(token);
return (
<div className="space-y-4">
<ol className="space-y-3">
<li className="rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] p-5">
<div
className="mb-2 flex items-center gap-2 text-[11px] uppercase tracking-[0.18em] text-[var(--cm-clay)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
<span className="rounded-full bg-[var(--cm-clay)]/20 px-1.5">1</span>
install the CLI
</div>
<div className="flex items-center gap-2">
<code
className="flex-1 overflow-x-auto rounded-[var(--cm-radius-xs)] bg-[var(--cm-bg)] p-3 text-sm text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
{INSTALL_CMD}
</code>
<button
onClick={() => copy(INSTALL_CMD, "install")}
className="rounded-[var(--cm-radius-xs)] border border-[var(--cm-border)] px-3 py-3 text-sm text-[var(--cm-fg-secondary)] transition-colors hover:border-[var(--cm-fg)] hover:text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
{copiedKey === "install" ? "Copied ✓" : "Copy"}
</button>
</div>
<p
className="mt-2 text-xs text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Installs the CLI, registers the MCP server + status hooks in
Claude Code. Restart Claude Code after this step.
</p>
</li>
<li className="rounded-[var(--cm-radius-md)] border border-[var(--cm-clay)]/40 bg-[var(--cm-bg-elevated)] p-5">
<div
className="mb-2 flex items-center gap-2 text-[11px] uppercase tracking-[0.18em] text-[var(--cm-clay)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
<span className="rounded-full bg-[var(--cm-clay)]/20 px-1.5">2</span>
join the mesh
</div>
<div className="flex items-center gap-2">
<code
className="flex-1 overflow-x-auto rounded-[var(--cm-radius-xs)] bg-[var(--cm-bg)] p-3 text-sm text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
{joinCmd}
</code>
<button
onClick={() => copy(joinCmd, "join")}
className="rounded-[var(--cm-radius-xs)] bg-[var(--cm-clay)] px-3 py-3 text-sm font-medium text-[var(--cm-fg)] transition-colors hover:bg-[var(--cm-clay-hover)]"
style={{ fontFamily: "var(--cm-font-sans)" }}
>
{copiedKey === "join" ? "Copied ✓" : "Copy"}
</button>
</div>
</li>
<li className="rounded-[var(--cm-radius-md)] border border-[var(--cm-border)] bg-[var(--cm-bg-elevated)] p-5">
<div
className="mb-2 flex items-center gap-2 text-[11px] uppercase tracking-[0.18em] text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
<span className="rounded-full bg-[var(--cm-border)] px-1.5">3</span>
launch with push messaging
</div>
<div className="flex items-center gap-2">
<code
className="flex-1 overflow-x-auto rounded-[var(--cm-radius-xs)] bg-[var(--cm-bg)] p-3 text-sm text-[var(--cm-fg)]"
style={{ fontFamily: "var(--cm-font-mono)" }}
>
claudemesh launch --name YourName
</code>
</div>
<p
className="mt-2 text-xs text-[var(--cm-fg-tertiary)]"
style={{ fontFamily: "var(--cm-font-serif)" }}
>
Restart Claude Code first, then launch. Peers see you appear on
the mesh. Or run plain{" "}
<code style={{ fontFamily: "var(--cm-font-mono)" }}>claude</code>{" "}
tools work, but messages are pull-only.
</p>
</li>
</ol>
<button
onClick={() => setHasCli("unknown")}
className="text-xs text-[var(--cm-fg-tertiary)] underline underline-offset-4 hover:text-[var(--cm-fg)]"
>
Back
</button>
</div>
);
};