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 branch 'worktree-agent-a1b995daa94e4e1b7' | 1 | import { Check, Minus } from "lucide-react"; |
| 2 | import type { ComponentProps } from "react"; | |
| 3 | ||
| 4 | import { | |
| 5 | CAPABILITIES, | |
| 6 | OWNER_ONLY, | |
| 7 | REPO_ROLES, | |
| 8 | REPO_ROLE_LABELS, | |
| 9 | REPO_ROLE_SUMMARIES, | |
| 10 | type RepoRole, | |
| 11 | allows, | |
| 12 | } from "@g1t/contracts"; | |
| 13 | ||
| 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. | 14 | import { Card } from "./ui/card"; |
| Merge branch 'worktree-agent-a1b995daa94e4e1b7' | 15 | import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "./ui/select"; |
| 16 | ||
| 17 | /** A repository role picker, each role with what it is for. */ | |
| 18 | export function RoleSelect({ | |
| 19 | roles = REPO_ROLES, | |
| 20 | label, | |
| 21 | size = "default", | |
| 22 | className, | |
| 23 | ...props | |
| 24 | }: Omit<ComponentProps<typeof Select>, "children"> & { | |
| 25 | roles?: readonly RepoRole[]; | |
| 26 | label: string; | |
| 27 | size?: "sm" | "default"; | |
| 28 | className?: string; | |
| 29 | }) { | |
| 30 | return ( | |
| 31 | <Select {...props}> | |
| 32 | <SelectTrigger size={size} aria-label={label} className={className}> | |
| 33 | <SelectValue /> | |
| 34 | </SelectTrigger> | |
| 35 | <SelectContent align="end" className="w-72"> | |
| 36 | {roles.map((role) => ( | |
| 37 | <SelectItem key={role} value={role} description={REPO_ROLE_SUMMARIES[role]}> | |
| 38 | {REPO_ROLE_LABELS[role]} | |
| 39 | </SelectItem> | |
| 40 | ))} | |
| 41 | </SelectContent> | |
| 42 | </Select> | |
| 43 | ); | |
| 44 | } | |
| 45 | ||
| 46 | /** | |
| 47 | * What each role can do, from the permission table itself: a row for each | |
| 48 | * capability, a column for each role. | |
| 49 | */ | |
| 50 | export function RolesTable() { | |
| 51 | return ( | |
| 52 | <> | |
| 53 | {/* On a phone: each capability with the least role that has it. */} | |
| 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. | 54 | <Card asChild tone="plain" divided className="sm:hidden"> |
| 55 | <ul> | |
| 56 | {CAPABILITIES.map((row) => { | |
| 57 | const owners = (OWNER_ONLY as readonly string[]).includes(row.capability); | |
| 58 | return ( | |
| 59 | <li key={row.capability} className="flex items-start justify-between gap-3 px-4 py-2.5 text-sm"> | |
| 60 | <span className="min-w-0 text-fg/90">{row.about}</span> | |
| 61 | <span className="shrink-0 text-xs whitespace-nowrap text-muted"> | |
| 62 | {owners ? "Owners" : row.role === "admin" ? "Admin" : `${REPO_ROLE_LABELS[row.role]} and up`} | |
| 63 | </span> | |
| 64 | </li> | |
| 65 | ); | |
| 66 | })} | |
| 67 | </ul> | |
| 68 | </Card> | |
| 69 | <Card tone="plain" className="hidden overflow-x-auto sm:block"> | |
| People wear their own avatars wherever g1t knows them only by name, and a workspace is never shown as a person. The activity feeds, recent changes, home's most active faces, issue and pull request authors, assignees, comments and the audit log drew a person from their username alone, so everyone got their letter avatar instead of the picture they uploaded. An avatar given no picture now draws the letter at once and lays the person's own picture over it once it loads, from /-/avatar/user/<username> (routes/avatar-user.ts): a redirect to the picture on the usercontent origin, or not found, read from identity's people only, so a workspace's icon can never stand in for someone. Kept five minutes, public like the profile. Agents, the product tour's people, "someone" and workspaces' square avatars are never looked up (image null), so an account with the same name never lends them its face. | 70 | <table className="w-full min-w-136 text-sm"> |
| Merge branch 'worktree-agent-a1b995daa94e4e1b7' | 71 | <thead> |
| 72 | <tr className="border-b border-line bg-surface text-left text-xs text-muted"> | |
| 73 | <th scope="col" className="px-4 py-2.5 font-medium"> | |
| 74 | Can | |
| 75 | </th> | |
| 76 | {REPO_ROLES.map((role) => ( | |
| People wear their own avatars wherever g1t knows them only by name, and a workspace is never shown as a person. The activity feeds, recent changes, home's most active faces, issue and pull request authors, assignees, comments and the audit log drew a person from their username alone, so everyone got their letter avatar instead of the picture they uploaded. An avatar given no picture now draws the letter at once and lays the person's own picture over it once it loads, from /-/avatar/user/<username> (routes/avatar-user.ts): a redirect to the picture on the usercontent origin, or not found, read from identity's people only, so a workspace's icon can never stand in for someone. Kept five minutes, public like the profile. Agents, the product tour's people, "someone" and workspaces' square avatars are never looked up (image null), so an account with the same name never lends them its face. | 77 | <th key={role} scope="col" className="w-18 px-1 py-2.5 text-center font-medium"> |
| Merge branch 'worktree-agent-a1b995daa94e4e1b7' | 78 | {REPO_ROLE_LABELS[role]} |
| 79 | </th> | |
| 80 | ))} | |
| 81 | </tr> | |
| 82 | </thead> | |
| 83 | <tbody className="divide-y divide-line"> | |
| 84 | {CAPABILITIES.map((row) => { | |
| 85 | const owners = (OWNER_ONLY as readonly string[]).includes(row.capability); | |
| 86 | return ( | |
| 87 | <tr key={row.capability}> | |
| 88 | <th scope="row" className="px-4 py-2 text-left font-normal text-fg/90"> | |
| 89 | {row.about} | |
| 90 | </th> | |
| 91 | {REPO_ROLES.map((role) => ( | |
| 92 | <td key={role} className="px-1 py-2 text-center"> | |
| 93 | {allows(role, row.capability) ? ( | |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 94 | <Check size={15} aria-label="Yes" className={`inline ${owners ? "text-muted" : "text-success"}`} /> |
| Merge branch 'worktree-agent-a1b995daa94e4e1b7' | 95 | ) : ( |
| 96 | <Minus size={13} aria-label="No" className="inline text-line-strong" /> | |
| 97 | )} | |
| 98 | </td> | |
| 99 | ))} | |
| 100 | </tr> | |
| 101 | ); | |
| 102 | })} | |
| 103 | </tbody> | |
| 104 | </table> | |
| 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. | 105 | </Card> |
| Merge branch 'worktree-agent-a1b995daa94e4e1b7' | 106 | </> |
| 107 | ); | |
| 108 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.