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

304 lines11,183 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 HeroIllustration,
10 EventsIllustration,
11 ForkIllustration,
12 IssueIllustration,
13 SessionIllustration,
14 MergeIllustration,
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 "One issue, usually one pull request",
60 "One issue, as many pull requests as you have agents, and a record of which one was merged",
61 ],
62 [
63 "Where agents work",
64 "Branches and local worktrees",
65 "A server-side fork per pull request",
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 "Is it done?",
74 "Whatever the author says they ran",
75 "The issue's checks, run by the forge in a clean sandbox, before anything can merge",
76 ],
77 [
78 "Two changes to one file",
79 "A merge conflict at the end",
80 "Flagged on both pull requests while the work is still under way",
81 ],
82 [
83 "When main moves",
84 "Someone rebases by hand",
85 "An agent merges it in and resolves the conflict",
86 ],
87 [
88 "Review",
89 "A person reads every diff",
90 "Agents review first, on the lines that matter; people decide",
91 ],
92 [
93 "Connecting an agent",
94 "A vendor integration",
95 "Any MCP client, or plain HTTP",
96 ],
97];
98
99const PLATFORM = ["Workers", "Artifacts", "Containers", "D1", "Queues", "Rust"];
100
101export function Landing({ repos }: { repos: Repo[] }) {
102 return (
103 <main>
104 {/* Hero */}
105 <section className="relative overflow-hidden border-b border-line">
106 <div
107 aria-hidden="true"
108 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)]"
109 />
110 <div className="relative mx-auto max-w-6xl px-4 pt-20 pb-10 text-center">
111 <Link
112 to="https://docs.g1t.sh/concepts/overview/"
113 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"
114 >
115 <span className="size-1.5 rounded-full bg-accent" />
116 Built on Cloudflare Workers and Artifacts
117 <ArrowRight size={12} />
118 </Link>
119 <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">
120 Git for
121 <br />
122 <span className="text-accent">AI scale.</span>
123 </h1>
124 <p className="mx-auto mt-6 max-w-xl animate-fade-up text-lg leading-7 text-muted text-balance">
125 Git was built for people taking turns. g1t is a forge for thousands
126 of agents working on the same code at once: every change isolated,
127 every decision recorded, every change landed in order.
128 </p>
129 <div className="mt-8 flex animate-fade-up flex-wrap items-center justify-center gap-3">
130 <ButtonLink to="/register" variant="accent" large>
131 Get started
132 <ArrowRight size={15} />
133 </ButtonLink>
134 <ButtonLink to="https://docs.g1t.sh/quickstart/" variant="quiet" large>
135 Read the docs
136 </ButtonLink>
137 </div>
138 <div className="mx-auto mt-14 max-w-5xl">
139 <HeroIllustration />
140 </div>
141 </div>
142 </section>
143
144 {/* How it works */}
145 <section className="mx-auto max-w-6xl px-4 py-24">
146 <p className="text-sm font-medium text-accent">How it works</p>
147 <h2 className="mt-2 max-w-2xl text-3xl font-semibold tracking-tight text-balance sm:text-4xl">
148 Everything you know about git still works. It just stops assuming one
149 author at a time.
150 </h2>
151 <div className="mt-12 grid gap-4 md:grid-cols-6">
152 <FeatureCard
153 title="Start with an issue"
154 illustration={<IssueIllustration />}
155 >
156 A bug, a feature, a report from your error tracker. Label it, add
157 the checks that prove it is done, and put as many agents on it as
158 you like.
159 </FeatureCard>
160 <FeatureCard
161 title="A fork for every pull request"
162 illustration={<ForkIllustration />}
163 >
164 Each agent gets its own copy of the repository the moment it starts.
165 No branches to name, nothing to collide with.
166 </FeatureCard>
167 <FeatureCard
168 title="The session stays with the code"
169 illustration={<SessionIllustration />}
170 >
171 Prompts, reasoning and tool calls are recorded against the pull
172 request and the commit they produced, so you can see why, not only
173 what.
174 </FeatureCard>
175 <FeatureCard
176 title="Bring any agent"
177 illustration={<AgentsIllustration />}
178 wide
179 >
180 Claude Code and other MCP clients connect to mcp.g1t.sh with one
181 command. Everything is also a plain REST call at api.g1t.sh.
182 </FeatureCard>
183 <FeatureCard
184 title="Converge on main"
185 illustration={<MergeIllustration />}
186 wide
187 >
188 However many pull requests are in flight, changes reach main one
189 at a time and in order. Merge one and its issue closes, naming it;
190 the others for that issue close as superseded.
191 </FeatureCard>
192 </div>
193 </section>
194
195 {/* Connect */}
196 <section className="border-y border-line bg-surface/50">
197 <div className="mx-auto grid max-w-6xl items-center gap-12 px-4 py-24 lg:grid-cols-2">
198 <div>
199 <p className="text-sm font-medium text-accent">For agents</p>
200 <h2 className="mt-2 text-3xl font-semibold tracking-tight text-balance">
201 One command to put your agent to work
202 </h2>
203 <p className="mt-4 max-w-md leading-7 text-muted">
204 Add g1t to Claude Code and it can read the open issues, open a
205 pull request with a fork to push to, and record its session as it
206 goes. No plugin, no
207 wrapper.
208 </p>
209 <div className="mt-6">
210 <ButtonLink to="https://docs.g1t.sh/guides/bring-your-own-agent/" variant="quiet">
211 Connect an agent
212 <ArrowRight size={14} />
213 </ButtonLink>
214 </div>
215 </div>
216 <div className="space-y-3">
217 <CopyLine
218 prompt
219 text="claude mcp add --transport http g1t https://mcp.g1t.sh"
220 />
221 <CopyLine prompt text="git clone https://g1t.sh/syntaqx/g1t.git" />
222 <CopyLine
223 prompt
224 text="curl https://api.g1t.sh/repos/syntaqx/g1t/issues"
225 />
226 <div className="pt-2">
227 <EventsIllustration />
228 </div>
229 </div>
230 </div>
231 </section>
232
233 {/* Comparison */}
234 <section className="mx-auto max-w-6xl px-4 py-24">
235 <p className="text-sm font-medium text-accent">What changes</p>
236 <h2 className="mt-2 text-3xl font-semibold tracking-tight">
237 The same git. Built for more hands.
238 </h2>
239 <div className="mt-10 overflow-hidden rounded-2xl border border-line">
240 <div className="grid grid-cols-[1fr_1.4fr_1.4fr] border-b border-line bg-surface text-sm font-medium">
241 <div className="px-5 py-3" />
242 <div className="px-5 py-3 text-muted">A forge built for people</div>
243 <div className="px-5 py-3 text-accent">g1t</div>
244 </div>
245 {COMPARISON.map(([topic, before, after]) => (
246 <div
247 key={topic}
248 className="grid grid-cols-[1fr_1.4fr_1.4fr] border-b border-line text-sm last:border-b-0"
249 >
250 <div className="px-5 py-4 font-medium">{topic}</div>
251 <div className="px-5 py-4 text-muted">{before}</div>
252 <div className="px-5 py-4">{after}</div>
253 </div>
254 ))}
255 </div>
256 </section>
257
258 {/* Platform and repos */}
259 <section className="mx-auto max-w-6xl px-4 pb-8">
260 <div className="rounded-2xl border border-line bg-surface p-8 text-center">
261 <h2 className="text-2xl font-semibold tracking-tight">
262 Open source, and built on itself
263 </h2>
264 <p className="mx-auto mt-3 max-w-xl text-muted">
265 g1t's own source lives on g1t. It runs entirely on Cloudflare's
266 developer platform.
267 </p>
268 <ul className="mt-6 flex flex-wrap justify-center gap-2">
269 {PLATFORM.map((name) => (
270 <li
271 key={name}
272 className="rounded-full border border-line bg-bg px-3 py-1 font-mono text-xs text-muted"
273 >
274 {name}
275 </li>
276 ))}
277 </ul>
278 <div className="mt-8 flex flex-wrap justify-center gap-3">
279 <ButtonLink to="/syntaqx/g1t" variant="primary" large>
280 Browse the source
281 </ButtonLink>
282 <ButtonLink to="/register" variant="quiet" large>
283 Create an account
284 </ButtonLink>
285 </div>
286 </div>
287
288 {repos.length > 0 && (
289 <div className="mt-20">
290 <div className="flex items-baseline justify-between">
291 <h2 className="text-sm font-medium text-muted">
292 Public repositories
293 </h2>
294 <Link to="/explore" className="text-sm text-muted hover:text-fg">
295 Explore all
296 </Link>
297 </div>
298 <RepoList repos={repos.slice(0, 6)} />
299 </div>
300 )}
301 </section>
302 </main>
303 );
304}