g1t/apps/web/app/components/landing.tsx

281 lines10,559 bytesCodeBlame
1import { ArrowRight } from "lucide-react";
2import type { ReactNode } from "react";
3import { Link } from "react-router";
4
5import type { Repo } from "@g1t/contracts";
6
7import {
8 AgentsIllustration,
9 ArenaIllustration,
10 EventsIllustration,
11 ForkIllustration,
12 IntentIllustration,
13 SessionIllustration,
14 ShipIllustration,
15} from "./illustrations";
16import { RepoList } from "./repo-list";
17import { ButtonLink, CopyLine } from "./ui";
18
19function FeatureCard({
20 title,
21 illustration,
22 soon,
23 wide,
24 children,
25}: {
26 title: string;
27 illustration: ReactNode;
28 soon?: boolean;
29 wide?: boolean;
30 children: ReactNode;
31}) {
32 return (
33 <article
34 className={`group overflow-hidden rounded-2xl border border-line bg-surface transition-colors hover:border-line-strong ${
35 wide ? "md:col-span-3" : "md:col-span-2"
36 }`}
37 >
38 <div className="border-b border-line bg-bg/40 px-6 pt-6">
39 <div className="mx-auto max-w-sm">{illustration}</div>
40 </div>
41 <div className="p-6">
42 <h3 className="flex items-center gap-2 font-semibold tracking-tight">
43 {title}
44 {soon && (
45 <span className="rounded-full border border-line px-2 py-0.5 text-[0.6875rem] font-normal text-faint">
46 in progress
47 </span>
48 )}
49 </h3>
50 <p className="mt-2 text-sm leading-6 text-muted">{children}</p>
51 </div>
52 </article>
53 );
54}
55
56const COMPARISON: [string, string, string][] = [
57 [
58 "Unit of work",
59 "A pull request: one author, one change",
60 "An intent: one goal, any number of attempts. One attempt is a pull request",
61 ],
62 [
63 "Where agents work",
64 "Branches and local worktrees",
65 "A server-side fork per attempt",
66 ],
67 [
68 "Why a change was made",
69 "A commit message, if you are lucky",
70 "The agent's full session, kept with the code",
71 ],
72 [
73 "Connecting an agent",
74 "A vendor integration",
75 "Any MCP client, or plain HTTP",
76 ],
77];
78
79const PLATFORM = ["Workers", "Artifacts", "D1", "Queues", "Rust"];
80
81export function Landing({ repos }: { repos: Repo[] }) {
82 return (
83 <main>
84 {/* Hero */}
85 <section className="relative overflow-hidden border-b border-line">
86 <div
87 aria-hidden="true"
88 className="pointer-events-none absolute inset-x-0 top-0 h-128 bg-[radial-gradient(60%_60%_at_50%_0%,color-mix(in_srgb,var(--color-accent)_9%,transparent),transparent)]"
89 />
90 <div className="relative mx-auto max-w-6xl px-4 pt-20 pb-10 text-center">
91 <Link
92 to="https://docs.g1t.sh/concepts/overview/"
93 className="inline-flex animate-fade-up items-center gap-2 rounded-full border border-line bg-surface px-3 py-1 text-xs text-muted transition-colors hover:border-line-strong hover:text-fg"
94 >
95 <span className="size-1.5 rounded-full bg-accent" />
96 Built on Cloudflare Workers and Artifacts
97 <ArrowRight size={12} />
98 </Link>
99 <h1 className="mx-auto mt-6 max-w-3xl animate-fade-up text-5xl leading-[1.05] font-semibold tracking-[-0.035em] text-balance sm:text-7xl">
100 Git for
101 <br />
102 <span className="text-accent">AI scale.</span>
103 </h1>
104 <p className="mx-auto mt-6 max-w-xl animate-fade-up text-lg leading-7 text-muted text-balance">
105 Git was built for people taking turns. g1t is a forge for thousands
106 of agents working on the same code at once: every attempt isolated,
107 every decision recorded, every change landed in order.
108 </p>
109 <div className="mt-8 flex animate-fade-up flex-wrap items-center justify-center gap-3">
110 <ButtonLink to="/register" variant="accent" large>
111 Get started
112 <ArrowRight size={15} />
113 </ButtonLink>
114 <ButtonLink to="https://docs.g1t.sh/quickstart/" variant="quiet" large>
115 Read the docs
116 </ButtonLink>
117 </div>
118 <div className="mx-auto mt-14 max-w-5xl">
119 <ArenaIllustration />
120 </div>
121 </div>
122 </section>
123
124 {/* How it works */}
125 <section className="mx-auto max-w-6xl px-4 py-24">
126 <p className="text-sm font-medium text-accent">How it works</p>
127 <h2 className="mt-2 max-w-2xl text-3xl font-semibold tracking-tight text-balance sm:text-4xl">
128 Everything you know about git still works. It just stops assuming one
129 author at a time.
130 </h2>
131 <div className="mt-12 grid gap-4 md:grid-cols-6">
132 <FeatureCard
133 title="Start with an intent"
134 illustration={<IntentIllustration />}
135 >
136 Write the goal and the checks that prove it is done. Think of it as
137 an issue that can hold any number of competing pull requests.
138 </FeatureCard>
139 <FeatureCard
140 title="A fork for every attempt"
141 illustration={<ForkIllustration />}
142 >
143 Each agent gets its own copy of the repository the moment it starts.
144 No branches to name, nothing to collide with.
145 </FeatureCard>
146 <FeatureCard
147 title="The session stays with the code"
148 illustration={<SessionIllustration />}
149 >
150 Prompts, reasoning and tool calls are recorded against the attempt
151 and the commit they produced, so you can see why, not only what.
152 </FeatureCard>
153 <FeatureCard
154 title="Bring any agent"
155 illustration={<AgentsIllustration />}
156 wide
157 >
158 Claude Code and other MCP clients connect to mcp.g1t.sh with one
159 command. Everything is also a plain REST call at api.g1t.sh.
160 </FeatureCard>
161 <FeatureCard
162 title="Converge on main"
163 illustration={<ShipIllustration />}
164 wide
165 >
166 However many attempts are in flight, changes reach main one at a
167 time and in order. An attempt that has fallen behind is told, and
168 catches up before it lands.
169 </FeatureCard>
170 </div>
171 </section>
172
173 {/* Connect */}
174 <section className="border-y border-line bg-surface/50">
175 <div className="mx-auto grid max-w-6xl items-center gap-12 px-4 py-24 lg:grid-cols-2">
176 <div>
177 <p className="text-sm font-medium text-accent">For agents</p>
178 <h2 className="mt-2 text-3xl font-semibold tracking-tight text-balance">
179 One command to put your agent to work
180 </h2>
181 <p className="mt-4 max-w-md leading-7 text-muted">
182 Add g1t to Claude Code and it can list intents, claim one, get a
183 fork to push to, and record its session as it goes. No plugin, no
184 wrapper.
185 </p>
186 <div className="mt-6">
187 <ButtonLink to="https://docs.g1t.sh/guides/bring-your-own-agent/" variant="quiet">
188 Connect an agent
189 <ArrowRight size={14} />
190 </ButtonLink>
191 </div>
192 </div>
193 <div className="space-y-3">
194 <CopyLine
195 prompt
196 text='claude mcp add --transport http g1t https://mcp.g1t.sh --header "Authorization: Bearer $G1T_TOKEN"'
197 />
198 <CopyLine prompt text="git clone https://g1t.sh/syntaqx/g1t.git" />
199 <CopyLine
200 prompt
201 text="curl https://api.g1t.sh/v1/repos/syntaqx/g1t/intents"
202 />
203 <div className="pt-2">
204 <EventsIllustration />
205 </div>
206 </div>
207 </div>
208 </section>
209
210 {/* Comparison */}
211 <section className="mx-auto max-w-6xl px-4 py-24">
212 <p className="text-sm font-medium text-accent">What changes</p>
213 <h2 className="mt-2 text-3xl font-semibold tracking-tight">
214 The same git. Different primitives.
215 </h2>
216 <div className="mt-10 overflow-hidden rounded-2xl border border-line">
217 <div className="grid grid-cols-[1fr_1.4fr_1.4fr] border-b border-line bg-surface text-sm font-medium">
218 <div className="px-5 py-3" />
219 <div className="px-5 py-3 text-muted">A forge built for people</div>
220 <div className="px-5 py-3 text-accent">g1t</div>
221 </div>
222 {COMPARISON.map(([topic, before, after]) => (
223 <div
224 key={topic}
225 className="grid grid-cols-[1fr_1.4fr_1.4fr] border-b border-line text-sm last:border-b-0"
226 >
227 <div className="px-5 py-4 font-medium">{topic}</div>
228 <div className="px-5 py-4 text-muted">{before}</div>
229 <div className="px-5 py-4">{after}</div>
230 </div>
231 ))}
232 </div>
233 </section>
234
235 {/* Platform and repos */}
236 <section className="mx-auto max-w-6xl px-4 pb-8">
237 <div className="rounded-2xl border border-line bg-surface p-8 text-center">
238 <h2 className="text-2xl font-semibold tracking-tight">
239 Open source, and built on itself
240 </h2>
241 <p className="mx-auto mt-3 max-w-xl text-muted">
242 g1t's own source lives on g1t. It runs entirely on Cloudflare's
243 developer platform.
244 </p>
245 <ul className="mt-6 flex flex-wrap justify-center gap-2">
246 {PLATFORM.map((name) => (
247 <li
248 key={name}
249 className="rounded-full border border-line bg-bg px-3 py-1 font-mono text-xs text-muted"
250 >
251 {name}
252 </li>
253 ))}
254 </ul>
255 <div className="mt-8 flex flex-wrap justify-center gap-3">
256 <ButtonLink to="/syntaqx/g1t" variant="primary" large>
257 Browse the source
258 </ButtonLink>
259 <ButtonLink to="/register" variant="quiet" large>
260 Create an account
261 </ButtonLink>
262 </div>
263 </div>
264
265 {repos.length > 0 && (
266 <div className="mt-20">
267 <div className="flex items-baseline justify-between">
268 <h2 className="text-sm font-medium text-muted">
269 Public repositories
270 </h2>
271 <Link to="/explore" className="text-sm text-muted hover:text-fg">
272 Explore all
273 </Link>
274 </div>
275 <RepoList repos={repos.slice(0, 6)} />
276 </div>
277 )}
278 </section>
279 </main>
280 );
281}