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.
| Pull requests show their tabs right under the title; agent panels move into Conversation | 1 | import { Check } from "lucide-react"; |
| Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039) | 2 | import { useEffect, useMemo, useState } from "react"; |
| Merge branch 'worktree-agent-a985c01f43538af01' | 3 | import { Form, Link } from "react-router"; |
| Pull requests show their tabs right under the title; agent panels move into Conversation | 4 | |
| 5 | import { PROFILE_LIMITS, type Profile } from "@g1t/contracts"; | |
| 6 | ||
| Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039) | 7 | import { browserTimeZone, timeZoneLabel, timeZoneNames, utcOffset } from "../lib/time-zone"; |
| Merge branch 'worktree-agent-a985c01f43538af01' | 8 | import { SubmitButton, usePending } from "./ui"; |
| 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. | 9 | import { Button } from "./ui/button"; |
| Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039) | 10 | import { Combobox } from "./ui/combobox"; |
| Pull requests show their tabs right under the title; agent panels move into Conversation | 11 | import { Field, FieldDescription, FieldError, FieldLabel } from "./ui/field"; |
| 12 | import { Input } from "./ui/input"; | |
| 13 | import { Textarea } from "./ui/textarea"; | |
| 14 | ||
| 15 | /** | |
| 16 | * The Profile section of a person's settings: what everyone sees at | |
| 17 | * `/u/<username>`. Posts `intent=profile`; identity checks every field. | |
| 18 | */ | |
| 19 | export function ProfileSection({ | |
| 20 | username, | |
| 21 | profile, | |
| 22 | error, | |
| 23 | saved, | |
| 24 | }: { | |
| 25 | username: string; | |
| 26 | profile: Profile | null; | |
| 27 | error?: string; | |
| 28 | saved: boolean; | |
| 29 | }) { | |
| Merge branch 'worktree-agent-a985c01f43538af01' | 30 | const busy = usePending({ intent: "profile" }); |
| Pull requests show their tabs right under the title; agent panels move into Conversation | 31 | const [bio, setBio] = useState(profile?.bio ?? ""); |
| Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039) | 32 | const [timezone, setTimezone] = useState(profile?.timezone ?? ""); |
| 33 | // The browser's zone is only known once the page runs in it. | |
| 34 | const [browserZone, setBrowserZone] = useState<string | null>(null); | |
| 35 | useEffect(() => setBrowserZone(browserTimeZone()), []); | |
| 36 | const zones = useMemo(() => { | |
| 37 | const now = Date.now(); | |
| 38 | return [ | |
| 39 | { value: "", label: "Not shown" }, | |
| 40 | ...timeZoneNames(profile?.timezone).map((zone) => ({ | |
| 41 | value: zone, | |
| 42 | label: timeZoneLabel(zone), | |
| 43 | description: utcOffset(zone, now) ?? undefined, | |
| 44 | keywords: [zone], | |
| 45 | })), | |
| 46 | ]; | |
| 47 | }, [profile?.timezone]); | |
| Pull requests show their tabs right under the title; agent panels move into Conversation | 48 | return ( |
| 49 | <section id="profile" className="scroll-mt-20"> | |
| 50 | <div className="flex flex-wrap items-baseline justify-between gap-2"> | |
| status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas | 51 | <h2 className="font-medium">Public profile</h2> |
| Pull requests show their tabs right under the title; agent panels move into Conversation | 52 | <Link to={`/u/${username}`} className="text-sm text-muted underline-offset-4 hover:text-fg hover:underline"> |
| 53 | View your profile | |
| 54 | </Link> | |
| 55 | </div> | |
| 56 | <p className="mt-1 text-sm text-muted"> | |
| 57 | Shown to everyone at <span className="font-mono text-fg">g1t.sh/u/{username}</span>. All of it is | |
| 58 | optional; your email address is never shown. | |
| 59 | </p> | |
| 60 | <Form method="post" className="mt-4 grid gap-4 sm:grid-cols-2"> | |
| 61 | <input type="hidden" name="intent" value="profile" /> | |
| 62 | <Field> | |
| 63 | <FieldLabel htmlFor="profile-name">Name</FieldLabel> | |
| 64 | <Input | |
| 65 | id="profile-name" | |
| 66 | name="name" | |
| 67 | defaultValue={profile?.name ?? ""} | |
| 68 | maxLength={PROFILE_LIMITS.name} | |
| 69 | placeholder={username} | |
| 70 | /> | |
| 71 | </Field> | |
| 72 | <Field> | |
| 73 | <FieldLabel htmlFor="profile-pronouns">Pronouns</FieldLabel> | |
| 74 | <Input | |
| 75 | id="profile-pronouns" | |
| 76 | name="pronouns" | |
| 77 | defaultValue={profile?.pronouns ?? ""} | |
| 78 | maxLength={PROFILE_LIMITS.pronouns} | |
| 79 | placeholder="they/them" | |
| 80 | /> | |
| 81 | </Field> | |
| 82 | <Field className="sm:col-span-2"> | |
| 83 | <FieldLabel htmlFor="profile-bio">Bio</FieldLabel> | |
| 84 | <Textarea | |
| 85 | id="profile-bio" | |
| 86 | name="bio" | |
| 87 | rows={2} | |
| 88 | value={bio} | |
| 89 | onChange={(event) => setBio(event.currentTarget.value)} | |
| 90 | maxLength={PROFILE_LIMITS.bio} | |
| 91 | placeholder="What you work on, in a line or two." | |
| 92 | /> | |
| 93 | <FieldDescription> | |
| 94 | {bio.length}/{PROFILE_LIMITS.bio} characters. Also what a link to your profile says. | |
| 95 | </FieldDescription> | |
| 96 | </Field> | |
| 97 | <Field> | |
| 98 | <FieldLabel htmlFor="profile-location">Location</FieldLabel> | |
| 99 | <Input | |
| 100 | id="profile-location" | |
| 101 | name="location" | |
| 102 | defaultValue={profile?.location ?? ""} | |
| 103 | maxLength={PROFILE_LIMITS.location} | |
| 104 | placeholder="Portland, Oregon" | |
| 105 | /> | |
| 106 | </Field> | |
| 107 | <Field> | |
| 108 | <FieldLabel htmlFor="profile-website">Website</FieldLabel> | |
| 109 | <Input | |
| 110 | id="profile-website" | |
| 111 | name="website" | |
| 112 | inputMode="url" | |
| 113 | defaultValue={profile?.website ?? ""} | |
| 114 | maxLength={PROFILE_LIMITS.website} | |
| 115 | placeholder="https://example.com" | |
| 116 | /> | |
| 117 | <FieldDescription>An https:// address.</FieldDescription> | |
| 118 | </Field> | |
| Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039) | 119 | <Field> |
| 120 | <FieldLabel htmlFor="profile-timezone">Time zone</FieldLabel> | |
| 121 | <Combobox | |
| 122 | id="profile-timezone" | |
| 123 | name="timezone" | |
| 124 | value={timezone} | |
| 125 | onValueChange={setTimezone} | |
| 126 | options={zones} | |
| 127 | placeholder="Not shown" | |
| 128 | searchPlaceholder="Find a city or region" | |
| 129 | emptyText="No time zone by that name." | |
| 130 | /> | |
| 131 | <FieldDescription> | |
| 132 | The card over your name shows your local time. | |
| 133 | {browserZone && browserZone !== timezone && ( | |
| 134 | <> | |
| 135 | {" "} | |
| 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. | 136 | <Button |
| Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039) | 137 | type="button" |
| 138 | onClick={() => setTimezone(browserZone)} | |
| 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. | 139 | variant="link" |
| 140 | size="inline" | |
| 141 | className="font-normal" | |
| Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039) | 142 | > |
| 143 | Use my browser's time zone ({timeZoneLabel(browserZone)}) | |
| 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. | 144 | </Button> |
| Merge leftovers: plan activity filtered in the query, pushes counted by account, ghost during the deletion window, profile time zones (identity 0039) | 145 | </> |
| 146 | )} | |
| 147 | </FieldDescription> | |
| 148 | </Field> | |
| Pull requests show their tabs right under the title; agent panels move into Conversation | 149 | <div className="flex flex-wrap items-center gap-3 sm:col-span-2"> |
| Merge branch 'worktree-agent-a985c01f43538af01' | 150 | <SubmitButton pending="Saving…" match={{ intent: "profile" }}> |
| 151 | Save profile | |
| 152 | </SubmitButton> | |
| Pull requests show their tabs right under the title; agent panels move into Conversation | 153 | {saved && !busy && !error && ( |
| 154 | <span className="inline-flex items-center gap-1.5 text-sm text-muted"> | |
| Merge branch 'worktree-agent-ad7c6d88d93adc817' | 155 | <Check size={14} className="text-success" /> |
| Pull requests show their tabs right under the title; agent panels move into Conversation | 156 | Saved |
| 157 | </span> | |
| 158 | )} | |
| 159 | <FieldError>{error}</FieldError> | |
| 160 | </div> | |
| 161 | </Form> | |
| 162 | </section> | |
| 163 | ); | |
| 164 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.