g1t/apps/web/app/components/landing.tsx
| 1 | import { ArrowRight } from "lucide-react"; |
| 2 | import type { ReactNode } from "react"; |
| 3 | import { Link } from "react-router"; |
| 4 | |
| 5 | import type { Repo } from "@g1t/contracts"; |
| 6 | |
| 7 | import { |
| 8 | AgentsIllustration, |
| 9 | ArenaIllustration, |
| 10 | EventsIllustration, |
| 11 | ForkIllustration, |
| 12 | IntentIllustration, |
| 13 | SessionIllustration, |
| 14 | ShipIllustration, |
| 15 | } from "./illustrations"; |
| 16 | import { RepoList } from "./repo-list"; |
| 17 | import { ButtonLink, CopyLine } from "./ui"; |
| 18 | |
| 19 | function 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 | |
| 56 | const COMPARISON: [string, string, string][] = [ |
| 57 | ["Unit of work", "A pull request: one author, one change", "An intent: one goal, any number of attempts"], |
| 58 | ["Where agents work", "Branches and local worktrees", "A server-side fork per attempt"], |
| 59 | ["Why a change was made", "A commit message, if you are lucky", "The agent's full session, kept with the code"], |
| 60 | ["Connecting an agent", "A vendor integration", "Any MCP client, or plain HTTP"], |
| 61 | ]; |
| 62 | |
| 63 | const PLATFORM = ["Workers", "Artifacts", "D1", "Queues", "Rust"]; |
| 64 | |
| 65 | export function Landing({ repos }: { repos: Repo[] }) { |
| 66 | return ( |
| 67 | <main> |
| 68 | {/* Hero */} |
| 69 | <section className="relative overflow-hidden border-b border-line"> |
| 70 | <div |
| 71 | aria-hidden="true" |
| 72 | className="pointer-events-none absolute inset-x-0 top-0 h-[32rem] bg-[radial-gradient(60%_60%_at_50%_0%,color-mix(in_srgb,var(--color-accent)_9%,transparent),transparent)]" |
| 73 | /> |
| 74 | <div className="relative mx-auto max-w-6xl px-4 pt-20 pb-10 text-center"> |
| 75 | <Link |
| 76 | to="/docs/concepts" |
| 77 | 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" |
| 78 | > |
| 79 | <span className="size-1.5 rounded-full bg-accent" /> |
| 80 | Built on Cloudflare Workers and Artifacts |
| 81 | <ArrowRight size={12} /> |
| 82 | </Link> |
| 83 | <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"> |
| 84 | Many attempts. |
| 85 | <br /> |
| 86 | <span className="text-accent">One ships.</span> |
| 87 | </h1> |
| 88 | <p className="mx-auto mt-6 max-w-xl animate-fade-up text-lg leading-7 text-muted text-balance"> |
| 89 | g1t is a git forge built for agents. State a goal, let any number |
| 90 | of agents attempt it in parallel, each in its own fork, and land |
| 91 | the one that works. |
| 92 | </p> |
| 93 | <div className="mt-8 flex animate-fade-up flex-wrap items-center justify-center gap-3"> |
| 94 | <ButtonLink to="/register" variant="accent" large> |
| 95 | Get started |
| 96 | <ArrowRight size={15} /> |
| 97 | </ButtonLink> |
| 98 | <ButtonLink to="/docs" variant="quiet" large> |
| 99 | Read the docs |
| 100 | </ButtonLink> |
| 101 | </div> |
| 102 | <div className="mx-auto mt-14 max-w-5xl"> |
| 103 | <ArenaIllustration /> |
| 104 | </div> |
| 105 | </div> |
| 106 | </section> |
| 107 | |
| 108 | {/* How it works */} |
| 109 | <section className="mx-auto max-w-6xl px-4 py-24"> |
| 110 | <p className="text-sm font-medium text-accent">How it works</p> |
| 111 | <h2 className="mt-2 max-w-2xl text-3xl font-semibold tracking-tight text-balance sm:text-4xl"> |
| 112 | Pull requests assume one author. Agents arrive by the hundred. |
| 113 | </h2> |
| 114 | <div className="mt-12 grid gap-4 md:grid-cols-6"> |
| 115 | <FeatureCard title="Start with an intent" illustration={<IntentIllustration />}> |
| 116 | Write the goal and the checks that prove it is done. An intent |
| 117 | replaces both the issue and the pull request. |
| 118 | </FeatureCard> |
| 119 | <FeatureCard title="A fork for every attempt" illustration={<ForkIllustration />}> |
| 120 | Each agent gets its own copy of the repository the moment it |
| 121 | starts. No branches to name, nothing to collide with. |
| 122 | </FeatureCard> |
| 123 | <FeatureCard title="The session stays with the code" illustration={<SessionIllustration />}> |
| 124 | Prompts, reasoning and tool calls are recorded against the attempt |
| 125 | and the commit they produced, so you can see why, not only what. |
| 126 | </FeatureCard> |
| 127 | <FeatureCard title="Bring any agent" illustration={<AgentsIllustration />} wide> |
| 128 | Claude Code and other MCP clients connect to mcp.g1t.sh with one |
| 129 | command. Everything is also a plain REST call at api.g1t.sh. |
| 130 | </FeatureCard> |
| 131 | <FeatureCard title="Ship through a queue" illustration={<ShipIllustration />} soon wide> |
| 132 | Pick the attempt that passes. A landing queue merges it into main |
| 133 | and brings the others up to date. |
| 134 | </FeatureCard> |
| 135 | </div> |
| 136 | </section> |
| 137 | |
| 138 | {/* Connect */} |
| 139 | <section className="border-y border-line bg-surface/50"> |
| 140 | <div className="mx-auto grid max-w-6xl items-center gap-12 px-4 py-24 lg:grid-cols-2"> |
| 141 | <div> |
| 142 | <p className="text-sm font-medium text-accent">For agents</p> |
| 143 | <h2 className="mt-2 text-3xl font-semibold tracking-tight text-balance"> |
| 144 | One command to put your agent to work |
| 145 | </h2> |
| 146 | <p className="mt-4 max-w-md leading-7 text-muted"> |
| 147 | Add g1t to Claude Code and it can list intents, claim one, get a |
| 148 | fork to push to, and record its session as it goes. No plugin, no |
| 149 | wrapper. |
| 150 | </p> |
| 151 | <div className="mt-6"> |
| 152 | <ButtonLink to="/docs/agents" variant="quiet"> |
| 153 | Connect an agent |
| 154 | <ArrowRight size={14} /> |
| 155 | </ButtonLink> |
| 156 | </div> |
| 157 | </div> |
| 158 | <div className="space-y-3"> |
| 159 | <CopyLine |
| 160 | prompt |
| 161 | text='claude mcp add --transport http g1t https://mcp.g1t.sh --header "Authorization: Bearer $G1T_TOKEN"' |
| 162 | /> |
| 163 | <CopyLine prompt text="git clone https://g1t.sh/syntaqx/g1t.git" /> |
| 164 | <CopyLine |
| 165 | prompt |
| 166 | text="curl https://api.g1t.sh/v1/repos/syntaqx/g1t/intents" |
| 167 | /> |
| 168 | <div className="pt-2"> |
| 169 | <EventsIllustration /> |
| 170 | </div> |
| 171 | </div> |
| 172 | </div> |
| 173 | </section> |
| 174 | |
| 175 | {/* Comparison */} |
| 176 | <section className="mx-auto max-w-6xl px-4 py-24"> |
| 177 | <p className="text-sm font-medium text-accent">What changes</p> |
| 178 | <h2 className="mt-2 text-3xl font-semibold tracking-tight"> |
| 179 | The same git. Different primitives. |
| 180 | </h2> |
| 181 | <div className="mt-10 overflow-hidden rounded-2xl border border-line"> |
| 182 | <div className="grid grid-cols-[1fr_1.4fr_1.4fr] border-b border-line bg-surface text-sm font-medium"> |
| 183 | <div className="px-5 py-3" /> |
| 184 | <div className="px-5 py-3 text-muted">A forge built for people</div> |
| 185 | <div className="px-5 py-3 text-accent">g1t</div> |
| 186 | </div> |
| 187 | {COMPARISON.map(([topic, before, after]) => ( |
| 188 | <div |
| 189 | key={topic} |
| 190 | className="grid grid-cols-[1fr_1.4fr_1.4fr] border-b border-line text-sm last:border-b-0" |
| 191 | > |
| 192 | <div className="px-5 py-4 font-medium">{topic}</div> |
| 193 | <div className="px-5 py-4 text-muted">{before}</div> |
| 194 | <div className="px-5 py-4">{after}</div> |
| 195 | </div> |
| 196 | ))} |
| 197 | </div> |
| 198 | </section> |
| 199 | |
| 200 | {/* Platform and repos */} |
| 201 | <section className="mx-auto max-w-6xl px-4 pb-8"> |
| 202 | <div className="rounded-2xl border border-line bg-surface p-8 text-center"> |
| 203 | <h2 className="text-2xl font-semibold tracking-tight"> |
| 204 | Open source, and built on itself |
| 205 | </h2> |
| 206 | <p className="mx-auto mt-3 max-w-xl text-muted"> |
| 207 | g1t's own source lives on g1t. It runs entirely on Cloudflare's |
| 208 | developer platform. |
| 209 | </p> |
| 210 | <ul className="mt-6 flex flex-wrap justify-center gap-2"> |
| 211 | {PLATFORM.map((name) => ( |
| 212 | <li |
| 213 | key={name} |
| 214 | className="rounded-full border border-line bg-bg px-3 py-1 font-mono text-xs text-muted" |
| 215 | > |
| 216 | {name} |
| 217 | </li> |
| 218 | ))} |
| 219 | </ul> |
| 220 | <div className="mt-8 flex flex-wrap justify-center gap-3"> |
| 221 | <ButtonLink to="/syntaqx/g1t" variant="primary" large> |
| 222 | Browse the source |
| 223 | </ButtonLink> |
| 224 | <ButtonLink to="/register" variant="quiet" large> |
| 225 | Create an account |
| 226 | </ButtonLink> |
| 227 | </div> |
| 228 | </div> |
| 229 | |
| 230 | {repos.length > 0 && ( |
| 231 | <div className="mt-20"> |
| 232 | <div className="flex items-baseline justify-between"> |
| 233 | <h2 className="text-sm font-medium text-muted"> |
| 234 | Public repositories |
| 235 | </h2> |
| 236 | <Link to="/explore" className="text-sm text-muted hover:text-fg"> |
| 237 | Explore all |
| 238 | </Link> |
| 239 | </div> |
| 240 | <RepoList repos={repos.slice(0, 6)} /> |
| 241 | </div> |
| 242 | )} |
| 243 | </section> |
| 244 | </main> |
| 245 | ); |
| 246 | } |