Skip to content
174 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

Merge commit authors and hovercards: commits show their accounts, every name opens a card (repos 0016)1import { User } from "lucide-react";
2import { Link } from "react-router";
3
Coding agents show as themselves on commits and contributors: Claude, Codex, Copilot, Gemini, Jules, Cursor, Devin, Aider, Dependabot, Renovate and GitHub Actions are known by the address they write on their commits (never by name), shown with their maker's mark, their own name and an agent or bot label, and a card saying what each is. Anyone else not on g1t gets a card too, saying so and how to claim the commits by adding the address. Contributor lists are worked out again (stats version 2) so an agent counts once.4import { codingAgentById } from "@g1t/contracts/coding-agents";
5
Merge commit authors and hovercards: commits show their accounts, every name opens a card (repos 0016)6import { type CommitPerson, type ShownCommit, profileHref, shownName } from "../lib/commit-people";
7import { cn } from "../lib/cn";
The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after.8
Coding agents show as themselves on commits and contributors: Claude, Codex, Copilot, Gemini, Jules, Cursor, Devin, Aider, Dependabot, Renovate and GitHub Actions are known by the address they write on their commits (never by name), shown with their maker's mark, their own name and an agent or bot label, and a card saying what each is. Anyone else not on g1t gets a card too, saying so and how to claim the commits by adding the address. Contributor lists are worked out again (stats version 2) so an agent counts once.9import { AgentBadge, AgentCard, AgentMark, AuthorCard } from "./coding-agent";
Merge commit authors and hovercards: commits show their accounts, every name opens a card (repos 0016)10import { UserCard } from "./user-card";
The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after.11import { Avatar } from "./ui/avatar";
Merge commit authors and hovercards: commits show their accounts, every name opens a card (repos 0016)12
13/**
14 * A person on a commit, as an avatar: theirs and their card when the
Coding agents show as themselves on commits and contributors: Claude, Codex, Copilot, Gemini, Jules, Cursor, Devin, Aider, Dependabot, Renovate and GitHub Actions are known by the address they write on their commits (never by name), shown with their maker's mark, their own name and an agent or bot label, and a card saying what each is. Anyone else not on g1t gets a card too, saying so and how to claim the commits by adding the address. Contributor lists are worked out again (stats version 2) so an agent counts once.15 * commit is matched to an account (or is g1t's); a coding agent's mark
16 * and card; else a silhouette with a card that says they're not on g1t.
Merge commit authors and hovercards: commits show their accounts, every name opens a card (repos 0016)17 */
18export function CommitAvatar({ person, size = 20 }: { person: CommitPerson; size?: number }) {
19 if (person.kind === "user" || person.kind === "g1t") {
20 const href = profileHref(person);
21 const avatar = <Avatar name={shownName(person)} image={person.avatar} size={size} />;
22 return (
23 <UserCard username={person.username}>
24 {href ? (
25 // Its name beside it is the link a keyboard reaches; this is for the pointer.
26 <Link to={href} tabIndex={-1} aria-hidden="true" className="relative z-10 inline-flex shrink-0 rounded-full">
27 {avatar}
28 </Link>
29 ) : (
30 <span className="relative z-10 inline-flex shrink-0">{avatar}</span>
31 )}
32 </UserCard>
33 );
34 }
Coding agents show as themselves on commits and contributors: Claude, Codex, Copilot, Gemini, Jules, Cursor, Devin, Aider, Dependabot, Renovate and GitHub Actions are known by the address they write on their commits (never by name), shown with their maker's mark, their own name and an agent or bot label, and a card saying what each is. Anyone else not on g1t gets a card too, saying so and how to claim the commits by adding the address. Contributor lists are worked out again (stats version 2) so an agent counts once.35 const agent = person.kind === "agent" ? codingAgentById(person.agent) : null;
36 if (agent) {
37 return (
38 <AgentCard agentId={agent.id}>
39 <span className="relative z-10 inline-flex shrink-0 rounded-full">
40 <AgentMark agent={agent} size={size} />
41 </span>
42 </AgentCard>
43 );
44 }
45 const silhouette = (
Merge commit authors and hovercards: commits show their accounts, every name opens a card (repos 0016)46 <span
47 aria-hidden="true"
48 className="inline-flex shrink-0 items-center justify-center rounded-full bg-line text-faint"
49 style={{ width: size, height: size }}
50 >
51 <User size={Math.round(size * 0.62)} strokeWidth={2.25} />
52 </span>
53 );
Coding agents show as themselves on commits and contributors: Claude, Codex, Copilot, Gemini, Jules, Cursor, Devin, Aider, Dependabot, Renovate and GitHub Actions are known by the address they write on their commits (never by name), shown with their maker's mark, their own name and an agent or bot label, and a card saying what each is. Anyone else not on g1t gets a card too, saying so and how to claim the commits by adding the address. Contributor lists are worked out again (stats version 2) so an agent counts once.54 // A deleted account: a plain silhouette, no card.
55 if (person.kind === "ghost") return silhouette;
56 // Nobody on g1t: the silhouette, with a card that says so.
57 return (
58 <AuthorCard name={person.name}>
59 <span className="relative z-10 inline-flex shrink-0 rounded-full">{silhouette}</span>
60 </AuthorCard>
61 );
Merge commit authors and hovercards: commits show their accounts, every name opens a card (repos 0016)62}
63
64/**
65 * A commit's author and co-authors, as overlapping avatars: the author's
66 * on top, at most `max`.
67 */
68export function CommitAvatars({ commit, size = 20, max = 3 }: { commit: Pick<ShownCommit, "author" | "coAuthors">; size?: number; max?: number }) {
69 const people = [commit.author, ...commit.coAuthors].slice(0, max);
70 if (people.length === 1) return <CommitAvatar person={commit.author} size={size} />;
71 return (
72 <span className="inline-flex shrink-0 items-center">
73 {people.map((person, index) => (
74 <span
75 key={`${index}:${person.username ?? person.name}`}
76 className="inline-flex rounded-full ring-2 ring-bg"
77 style={{ marginLeft: index === 0 ? 0 : -Math.round(size * 0.35), zIndex: people.length - index }}
78 >
79 <CommitAvatar person={person} size={size} />
80 </span>
81 ))}
82 </span>
83 );
84}
85
86/**
87 * A person on a commit, by name: their username, linked to their profile
88 * with their card, for an account; g1t with its label and card; ghost for
89 * a deleted account; else the name written on the commit, as text.
90 */
91export function CommitName({ person, className }: { person: CommitPerson; className?: string }) {
92 if (person.kind === "g1t") {
93 return (
94 <UserCard username="g1t">
95 <span tabIndex={0} className="relative z-10 inline-flex items-baseline gap-1 rounded outline-none focus-visible:ring-2 focus-visible:ring-accent">
96 <span className={className}>g1t</span>
97 <span className="rounded border border-line px-1 text-[0.625rem] leading-[1.35] font-medium text-muted">bot</span>
98 </span>
99 </UserCard>
100 );
101 }
Coding agents show as themselves on commits and contributors: Claude, Codex, Copilot, Gemini, Jules, Cursor, Devin, Aider, Dependabot, Renovate and GitHub Actions are known by the address they write on their commits (never by name), shown with their maker's mark, their own name and an agent or bot label, and a card saying what each is. Anyone else not on g1t gets a card too, saying so and how to claim the commits by adding the address. Contributor lists are worked out again (stats version 2) so an agent counts once.102 const agent = person.kind === "agent" ? codingAgentById(person.agent) : null;
103 if (agent) {
104 return (
105 <AgentCard agentId={agent.id}>
106 <span tabIndex={0} className="relative z-10 inline-flex items-baseline gap-1 rounded outline-none focus-visible:ring-2 focus-visible:ring-accent">
107 <span className={className}>{agent.name}</span>
108 <AgentBadge agent={agent} />
109 </span>
110 </AgentCard>
111 );
112 }
Merge commit authors and hovercards: commits show their accounts, every name opens a card (repos 0016)113 const href = profileHref(person);
Coding agents show as themselves on commits and contributors: Claude, Codex, Copilot, Gemini, Jules, Cursor, Devin, Aider, Dependabot, Renovate and GitHub Actions are known by the address they write on their commits (never by name), shown with their maker's mark, their own name and an agent or bot label, and a card saying what each is. Anyone else not on g1t gets a card too, saying so and how to claim the commits by adding the address. Contributor lists are worked out again (stats version 2) so an agent counts once.114 if (!href) {
115 if (person.kind === "ghost") return <span className={className}>{shownName(person)}</span>;
116 return (
117 <AuthorCard name={person.name}>
118 <span tabIndex={0} className={cn("relative z-10 rounded outline-none focus-visible:ring-2 focus-visible:ring-accent", className)}>
119 {shownName(person)}
120 </span>
121 </AuthorCard>
122 );
123 }
Merge commit authors and hovercards: commits show their accounts, every name opens a card (repos 0016)124 return (
125 <UserCard username={person.username}>
126 <Link to={href} className={cn("relative z-10 hover:underline", className)}>
127 {shownName(person)}
128 </Link>
129 </UserCard>
130 );
131}
132
133/**
134 * Who made a commit, in words: the author, and its co-authors after them
135 * ("ada and grace", "ada, grace and 2 others").
136 */
137export function CommitNames({
138 commit,
139 className,
140 all,
141}: {
142 commit: Pick<ShownCommit, "author" | "coAuthors">;
143 className?: string;
144 /** Name every co-author, rather than counting past the first. */
145 all?: boolean;
146}) {
147 const others = commit.coAuthors;
148 if (others.length === 0) return <CommitName person={commit.author} className={className} />;
149 if (all && others.length > 1) {
150 const people = [commit.author, ...others];
151 return (
152 <>
153 {people.map((person, index) => (
154 <span key={index}>
155 {index > 0 && (index === people.length - 1 ? " and " : ", ")}
156 <CommitName person={person} className={className} />
157 </span>
158 ))}
159 </>
160 );
161 }
162 if (others.length === 1) {
163 return (
164 <>
165 <CommitName person={commit.author} className={className} /> and <CommitName person={others[0]!} className={className} />
166 </>
167 );
168 }
169 return (
170 <>
171 <CommitName person={commit.author} className={className} /> and {others.length} others
172 </>
173 );
174}

This file's history is long; its oldest lines are credited to the oldest commit read.