Skip to content
201 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.

g1t is described as the product it is in alpha, and its shell takes the new shape: the README, the docs home and docs/PLAN.md label every feature Live, Preview or Coming; a floating dock with the g1t mark, Today, Chat, Notifications, Agents, Code, Artifacts, your pinned apps and the Apps launcher, with People, Workspace and the account menu (now holding help) at its foot; the workspace's logo and switcher heading a flat sidebar that folds away with Ctrl B, or leading the header's breadcrumbs where there is none; the page in a rounded panel with a full-width header; Today as the front page, with first-time acceptance of agents' pull requests, what needs you and what's at stake, spend today and one item to start with, every number from a service; Notifications in place of the Inbox at /notifications; a bottom bar and More sheet on phones; and sign-in, sign-up, two-factor, reset, invites and choosing a workspace on standalone pages with no app around them.1/**
2 * The public frame's header (lib/chrome.ts, `public`): for a visitor who is
3 * not signed in, on every page that is not about signing in. The logo,
4 * search, Explore and Docs, and signing in or up; on a phone the links fold
5 * into one menu. Someone still confirming their address gets their
6 * account's menu in place of signing in.
7 */
8import { BookOpen, ChevronDown, Compass, CreditCard, LogIn, LogOut, Menu, Plus, Search, Settings } from "lucide-react";
9import { useState } from "react";
10import { Form, Link, NavLink, useParams, useSubmit } from "react-router";
11
12import type { User } from "@g1t/contracts";
13
14import { CommandPalette, type PaletteCommand, PaletteKey, usePaletteShortcut } from "./command-palette";
You choose how g1t looks: Auto follows your system, Light and Dark stay put, set from the account menu, the phone's You and help sheet, Settings → Account or the command palette; the page is drawn in your choice from the first byte, every colour has a light value that reads at 4.5:1 or better, code and diffs are highlighted for both, diagrams and the document editor follow along, the product tour and the g1t mark's avatar stay dark, and Dark is what you see until you choose; the workspaces guide says how.15import { THEME_COMMANDS, ThemeMenuSwitch } from "./theme-switch";
g1t is described as the product it is in alpha, and its shell takes the new shape: the README, the docs home and docs/PLAN.md label every feature Live, Preview or Coming; a floating dock with the g1t mark, Today, Chat, Notifications, Agents, Code, Artifacts, your pinned apps and the Apps launcher, with People, Workspace and the account menu (now holding help) at its foot; the workspace's logo and switcher heading a flat sidebar that folds away with Ctrl B, or leading the header's breadcrumbs where there is none; the page in a rounded panel with a full-width header; Today as the front page, with first-time acceptance of agents' pull requests, what needs you and what's at stake, spend today and one item to start with, every number from a service; Notifications in place of the Inbox at /notifications; a bottom bar and More sheet on phones; and sign-in, sign-up, two-factor, reset, invites and choosing a workspace on standalone pages with no app around them.16import { Logo } from "./logo";
17import { Avatar, ButtonLink, notACredential } from "./ui";
18import {
19 DropdownMenu,
20 DropdownMenuContent,
21 DropdownMenuItem,
22 DropdownMenuLabel,
23 DropdownMenuSeparator,
24 DropdownMenuTrigger,
25} from "./ui/dropdown-menu";
26import { useSignUpCopy } from "../lib/registration";
27
28function HeaderLink({ to, children }: { to: string; children: React.ReactNode }) {
29 return (
30 <NavLink
31 to={to}
32 className={({ isActive }) =>
33 `rounded-md px-2.5 py-1.5 text-sm transition-colors hover:bg-raised hover:text-fg ${
34 isActive ? "text-fg" : "text-muted"
35 }`
36 }
37 >
38 {children}
39 </NavLink>
40 );
41}
42
43/** What the palette offers someone without the app's sidebar. */
44const PUBLIC_COMMANDS: PaletteCommand[] = [
45 { label: "Explore", hint: "Public projects", to: "/explore", icon: <Compass size={15} /> },
46 { label: "Search g1t", hint: "Repositories, code, issues, people", to: "/search", icon: <Search size={15} /> },
47 { label: "Pricing", to: "/pricing", icon: <CreditCard size={15} /> },
48 { label: "Documentation", to: "https://docs.g1t.sh/", icon: <BookOpen size={15} /> },
49 { label: "Sign in", to: "/login", icon: <LogIn size={15} /> },
50 { label: "Sign up", to: "/register", icon: <Plus size={15} /> },
51];
52
53/** Sign up: the sign-up page says whether it takes an invite. */
54function SignUpButton() {
55 const copy = useSignUpCopy();
56 return <ButtonLink to="/register">{copy.primary}</ButtonLink>;
57}
58
59export function PublicHeader({ user }: { user: User | null | undefined }) {
60 const submit = useSubmit();
61 const signUp = useSignUpCopy();
62 const [palette, setPalette] = useState(false);
63 usePaletteShortcut(() => setPalette((open) => !open));
64 const params = useParams();
65 const repo = params.owner && params.repo ? `${params.owner}/${params.repo}` : null;
66 return (
67 <header className="sticky top-0 z-40 border-b border-line bg-surface/85 backdrop-blur">
68 <div className="mx-auto flex h-14 max-w-6xl items-center gap-2 px-4">
69 <Link to="/" aria-label="g1t home" className="mr-2 flex">
70 <Logo />
71 </Link>
72 <Form action="/search" role="search" className="relative hidden grow sm:block sm:max-w-xs">
73 <Search
74 size={14}
75 className="pointer-events-none absolute top-1/2 left-3 -translate-y-1/2 text-faint"
76 />
77 <input
78 name="q"
79 {...notACredential()}
80 placeholder="Search g1t"
81 aria-label="Search g1t"
82 className="w-full rounded-md border border-line bg-bg py-1.5 pr-12 pl-8 text-sm outline-none transition-colors placeholder:text-faint hover:border-line-strong focus:border-accent-dim"
83 />
84 <PaletteKey className="pointer-events-none absolute top-1/2 right-2 -translate-y-1/2 rounded bg-raised px-1.5 font-mono text-[0.625rem] text-muted ring-1 ring-line" />
85 </Form>
86 <button
87 type="button"
88 aria-label="Search g1t"
89 onClick={() => setPalette(true)}
90 className="flex size-10 items-center justify-center rounded-md text-muted transition-colors hover:bg-raised hover:text-fg sm:hidden"
91 >
92 <Search size={18} />
93 </button>
94 <CommandPalette
95 open={palette}
96 onOpenChange={setPalette}
You choose how g1t looks: Auto follows your system, Light and Dark stay put, set from the account menu, the phone's You and help sheet, Settings → Account or the command palette; the page is drawn in your choice from the first byte, every colour has a light value that reads at 4.5:1 or better, code and diffs are highlighted for both, diagrams and the document editor follow along, the product tour and the g1t mark's avatar stay dark, and Dark is what you see until you choose; the workspaces guide says how.97 commands={[
98 ...(user ? PUBLIC_COMMANDS.slice(0, 4) : PUBLIC_COMMANDS.map((command) => (command.to === "/register" ? { ...command, label: signUp.primary } : command))),
99 ...THEME_COMMANDS,
100 ]}
g1t is described as the product it is in alpha, and its shell takes the new shape: the README, the docs home and docs/PLAN.md label every feature Live, Preview or Coming; a floating dock with the g1t mark, Today, Chat, Notifications, Agents, Code, Artifacts, your pinned apps and the Apps launcher, with People, Workspace and the account menu (now holding help) at its foot; the workspace's logo and switcher heading a flat sidebar that folds away with Ctrl B, or leading the header's breadcrumbs where there is none; the page in a rounded panel with a full-width header; Today as the front page, with first-time acceptance of agents' pull requests, what needs you and what's at stake, spend today and one item to start with, every number from a service; Notifications in place of the Inbox at /notifications; a bottom bar and More sheet on phones; and sign-in, sign-up, two-factor, reset, invites and choosing a workspace on standalone pages with no app around them.101 repo={repo}
102 />
103 <nav aria-label="Main" className="hidden items-center gap-0.5 sm:flex">
104 <HeaderLink to="/explore">Explore</HeaderLink>
105 <HeaderLink to="https://docs.g1t.sh/">Docs</HeaderLink>
106 </nav>
107 <div className="ml-auto flex items-center gap-2">
108 {/* On a phone the links fold into one menu, so the bar fits. */}
109 <DropdownMenu>
110 <DropdownMenuTrigger
111 aria-label="Menu"
112 className="flex size-10 items-center justify-center rounded-md text-muted outline-none transition-colors hover:bg-raised hover:text-fg focus-visible:ring-2 focus-visible:ring-accent data-[state=open]:bg-raised data-[state=open]:text-fg sm:hidden"
113 >
114 <Menu size={18} />
115 </DropdownMenuTrigger>
116 <DropdownMenuContent align="end" className="w-52">
117 <DropdownMenuItem asChild className="min-h-11">
118 <Link to="/explore">
119 <Compass />
120 Explore
121 </Link>
122 </DropdownMenuItem>
123 <DropdownMenuItem asChild className="min-h-11">
124 <Link to="https://docs.g1t.sh/">
125 <BookOpen />
126 Docs
127 </Link>
128 </DropdownMenuItem>
You choose how g1t looks: Auto follows your system, Light and Dark stay put, set from the account menu, the phone's You and help sheet, Settings → Account or the command palette; the page is drawn in your choice from the first byte, every colour has a light value that reads at 4.5:1 or better, code and diffs are highlighted for both, diagrams and the document editor follow along, the product tour and the g1t mark's avatar stay dark, and Dark is what you see until you choose; the workspaces guide says how.129 <DropdownMenuSeparator />
130 <ThemeMenuSwitch className="px-2" />
g1t is described as the product it is in alpha, and its shell takes the new shape: the README, the docs home and docs/PLAN.md label every feature Live, Preview or Coming; a floating dock with the g1t mark, Today, Chat, Notifications, Agents, Code, Artifacts, your pinned apps and the Apps launcher, with People, Workspace and the account menu (now holding help) at its foot; the workspace's logo and switcher heading a flat sidebar that folds away with Ctrl B, or leading the header's breadcrumbs where there is none; the page in a rounded panel with a full-width header; Today as the front page, with first-time acceptance of agents' pull requests, what needs you and what's at stake, spend today and one item to start with, every number from a service; Notifications in place of the Inbox at /notifications; a bottom bar and More sheet on phones; and sign-in, sign-up, two-factor, reset, invites and choosing a workspace on standalone pages with no app around them.131 {!user && (
132 <>
133 <DropdownMenuSeparator />
134 <DropdownMenuItem asChild className="min-h-11">
135 <Link to="/login">
136 <LogIn />
137 Sign in
138 </Link>
139 </DropdownMenuItem>
140 </>
141 )}
142 </DropdownMenuContent>
143 </DropdownMenu>
144 {user ? (
145 <>
146 <DropdownMenu>
147 <DropdownMenuTrigger
148 aria-label="Account menu"
149 className="flex items-center gap-1.5 rounded-md p-1 outline-none transition-colors hover:bg-raised focus-visible:ring-2 focus-visible:ring-accent data-[state=open]:bg-raised"
150 >
151 <Avatar name={user.username} image={user.avatar} size={24} />
152 <ChevronDown size={14} className="text-faint" />
153 </DropdownMenuTrigger>
154 <DropdownMenuContent align="end" className="w-56">
155 <DropdownMenuLabel>
156 Signed in as{" "}
157 <span className="font-mono font-medium text-fg">
158 {user.username}
159 </span>
160 </DropdownMenuLabel>
161 <DropdownMenuSeparator />
162 <DropdownMenuItem asChild>
163 <Link to="/settings/profile">
164 <Settings />
165 Settings
166 </Link>
167 </DropdownMenuItem>
168 <DropdownMenuItem asChild>
169 <Link to="https://docs.g1t.sh/quickstart/">
170 <BookOpen />
171 Documentation
172 </Link>
173 </DropdownMenuItem>
174 <DropdownMenuSeparator />
You choose how g1t looks: Auto follows your system, Light and Dark stay put, set from the account menu, the phone's You and help sheet, Settings → Account or the command palette; the page is drawn in your choice from the first byte, every colour has a light value that reads at 4.5:1 or better, code and diffs are highlighted for both, diagrams and the document editor follow along, the product tour and the g1t mark's avatar stay dark, and Dark is what you see until you choose; the workspaces guide says how.175 <ThemeMenuSwitch className="px-2" />
176 <DropdownMenuSeparator />
g1t is described as the product it is in alpha, and its shell takes the new shape: the README, the docs home and docs/PLAN.md label every feature Live, Preview or Coming; a floating dock with the g1t mark, Today, Chat, Notifications, Agents, Code, Artifacts, your pinned apps and the Apps launcher, with People, Workspace and the account menu (now holding help) at its foot; the workspace's logo and switcher heading a flat sidebar that folds away with Ctrl B, or leading the header's breadcrumbs where there is none; the page in a rounded panel with a full-width header; Today as the front page, with first-time acceptance of agents' pull requests, what needs you and what's at stake, spend today and one item to start with, every number from a service; Notifications in place of the Inbox at /notifications; a bottom bar and More sheet on phones; and sign-in, sign-up, two-factor, reset, invites and choosing a workspace on standalone pages with no app around them.177 {/* Submitted from here: the menu closes on select, and a button
178 that has left the page cannot submit a form. */}
179 <DropdownMenuItem
180 onSelect={() => submit(null, { method: "post", action: "/logout" })}
181 >
182 <LogOut />
183 Sign out
184 </DropdownMenuItem>
185 </DropdownMenuContent>
186 </DropdownMenu>
187 </>
188 ) : (
189 <>
190 <span className="hidden sm:contents">
191 <HeaderLink to="/login">Sign in</HeaderLink>
192 </span>
193 <SignUpButton />
194 </>
195 )}
196 </div>
197 </div>
198 </header>
199 );
200}
201