From ce52fcef2d1d1e544b92969952b0a14186c531a6 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Alejandro=20Guti=C3=A9rrez?=
<35082514+alezmad@users.noreply.github.com>
Date: Wed, 15 Apr 2026 02:14:27 +0100
Subject: [PATCH] feat(invite): branded email + one-command install+launch UX
Email (broker):
- Rebrand mesh-invitation.tsx to match site (clay accent #d97757,
cream fg, Anthropic Serif/Mono, dark bg). Mesh glyph in header.
- Hero CTA links to the /i/short URL landing page.
- Single one-liner 'npm i -g claudemesh-cli && claudemesh launch --join URL'
so new users copy once, paste once, done.
Web InstallToggle:
- Replace two-step numbered list with single one-liner in the first-time
panel. Reduces copy/paste ops from 2 to 1 and stops prescribing
'YourName' as a literal (CLI now defaults to $USER).
Co-Authored-By: Claude Opus 4.6 (1M context)
---
apps/broker/src/emails/mesh-invitation.tsx | 323 +++++++++++++++----
apps/broker/src/index.ts | 2 +-
apps/web/src/modules/join/install-toggle.tsx | 93 ++----
3 files changed, 299 insertions(+), 119 deletions(-)
diff --git a/apps/broker/src/emails/mesh-invitation.tsx b/apps/broker/src/emails/mesh-invitation.tsx
index 23fddc9..ac540f2 100644
--- a/apps/broker/src/emails/mesh-invitation.tsx
+++ b/apps/broker/src/emails/mesh-invitation.tsx
@@ -9,7 +9,6 @@ import {
Link,
Preview,
Section,
- Tailwind,
Text,
} from "@react-email/components";
import * as React from "react";
@@ -17,84 +16,295 @@ import * as React from "react";
interface MeshInvitationProps {
meshName: string;
inviteUrl: string;
+ token: string;
expiresAt: string;
appBaseUrl: string;
}
+// Brand tokens — mirror of apps/web/src/assets/styles/globals.css (--cm-*).
+// Inlined here because email clients don't resolve CSS vars.
+const brand = {
+ bg: "#141413",
+ bgElevated: "#1f1e1d",
+ bgCode: "#0f0e0d",
+ fg: "#faf9f5",
+ fgSecondary: "#c2c0b6",
+ fgTertiary: "#87867f",
+ clay: "#d97757",
+ clayBorder: "rgba(217, 119, 87, 0.35)",
+ border: "rgba(217, 119, 87, 0.2)",
+ serif: 'Georgia, "Times New Roman", serif',
+ mono: '"JetBrains Mono", "SF Mono", Menlo, Consolas, monospace',
+ sans:
+ '-apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif',
+} as const;
+
export const MeshInvitation = ({
meshName,
inviteUrl,
+ token,
expiresAt,
appBaseUrl,
}: MeshInvitationProps) => {
const expiresLabel = new Date(expiresAt).toUTCString();
+ const launchCmd = `claudemesh launch --join ${inviteUrl}`;
+ const oneLiner = `npm i -g claudemesh-cli && ${launchCmd}`;
return (
-
+
+
+
+
You've been invited to the {meshName} mesh on claudemesh
-
-
-
-
+
+
+ {/* Header — mesh glyph + wordmark */}
+
+
+
+ |
+
+ |
+
+
+ claudemesh
+
+ |
+
+
+
-
- You're invited to join{" "}
- {meshName}
-
+ {/* Eyebrow */}
+
+ — you're invited
+
-
- Someone invited you to join their mesh on claudemesh — a peer
- network for Claude Code sessions. Accept the invite to connect
- your session with theirs.
+ {/* Heading */}
+
+ Join{" "}
+
+ {meshName}
+ {" "}
+ on claudemesh
+
+
+ {/* Body prose */}
+
+ claudemesh is a peer mesh for Claude Code sessions — end-to-end
+ encrypted, keys stay on your machine. Open the link below to see
+ the mesh, the inviter, and the command to join.
+
+
+ {/* Primary CTA */}
+
+
+ {/* Terminal shortcut — for the already-set-up crowd */}
+
+ — already have the CLI?
+
+
-
-
-
- Or copy this link into your browser:
+ {/* First-time one-liner */}
+
+ — first time? one command
+
+
+
+ {oneLiner}
-
-
- {inviteUrl}
-
+
+ Requires Node.js 20+. Display name defaults to $USER.
+
-
+
-
- This invite expires on{" "}
- {expiresLabel}. If you
- weren't expecting this email, you can safely ignore it.
-
-
-
-
-
-
- claudemesh.com
-
-
-
-
-
+ {/* Footer meta */}
+
+ Expires{" "}
+ {expiresLabel}.
+ If you weren't expecting this, you can ignore it.
+
+
+
+ claudemesh.com
+
+
+
+
);
};
@@ -102,6 +312,7 @@ export const MeshInvitation = ({
MeshInvitation.PreviewProps = {
meshName: "prueba1",
inviteUrl: "https://claudemesh.com/i/RUVMYXZQ",
+ token: "eyJ2IjoxLCJtZXNoX2lkIjoiQUtMYUZxR3FKOGZCajN0U3dvVk1PSFYxQmF3UGlYTE8iLCJtZXNoX3NsdWciOiJwcnVlYmExIn0",
expiresAt: "2026-04-22T00:51:26.181Z",
appBaseUrl: "https://claudemesh.com",
} satisfies MeshInvitationProps;
diff --git a/apps/broker/src/index.ts b/apps/broker/src/index.ts
index 432d826..28cbfe7 100644
--- a/apps/broker/src/index.ts
+++ b/apps/broker/src/index.ts
@@ -5143,7 +5143,7 @@ async function handleCliMeshInvite(req: IncomingMessage, slug: string, res: Serv
const { MeshInvitation } = await import("./emails/mesh-invitation");
const React = await import("react");
const subject = `You're invited to join "${m.name}" on claudemesh`;
- const element = React.createElement(MeshInvitation, { meshName: m.name, inviteUrl: url, expiresAt: expiresAt.toISOString(), appBaseUrl: baseUrl });
+ const element = React.createElement(MeshInvitation, { meshName: m.name, inviteUrl: url, token, expiresAt: expiresAt.toISOString(), appBaseUrl: baseUrl });
const html = await render(element);
const text = await render(element, { plainText: true });
const res = process.env.POSTMARK_API_KEY
diff --git a/apps/web/src/modules/join/install-toggle.tsx b/apps/web/src/modules/join/install-toggle.tsx
index aed83a8..2f545db 100644
--- a/apps/web/src/modules/join/install-toggle.tsx
+++ b/apps/web/src/modules/join/install-toggle.tsx
@@ -5,8 +5,9 @@ interface Props {
token: string;
}
-const LAUNCH_CMD = (token: string) => `claudemesh launch --name YourName --join ${token}`;
-const JOIN_CMD = (token: string) => `claudemesh join ${token}`;
+const LAUNCH_CMD = (token: string) => `claudemesh launch --join ${token}`;
+const INSTALL_AND_LAUNCH = (token: string) =>
+ `npm i -g claudemesh-cli && claudemesh launch --join ${token}`;
const INSTALL_CMD = "npm i -g claudemesh-cli";
export const InstallToggle = ({ token }: Props) => {
@@ -97,71 +98,39 @@ export const InstallToggle = ({ token }: Props) => {
);
}
- const launchCmd = LAUNCH_CMD(token);
+ const oneLiner = INSTALL_AND_LAUNCH(token);
return (
-
- -
-
+
+ install + launch — one command
+
+
+
- 1
- install the CLI
-
-
-
- {INSTALL_CMD}
-
-
-
-
+
-
-
-
-
- 2
- join + launch
-
-
-
- {launchCmd}
-
-
-
-
- Joins the mesh and launches Claude Code in one step.
-
-
-
+ {copiedKey === "one" ? "Copied ✓" : "Copy"}
+
+
+
+ Requires Node.js 20+. Your display name defaults to $USER — override with{" "}
+ --name YourName.
+
+