Skip to content
375 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.1import { AlarmClock, Bell, Bookmark, Check, CheckCheck, CircleCheck, CircleX, Ellipsis, Hand, Info, Mail, MailOpen, Undo2 } from "lucide-react";
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all2import { type ReactNode, useEffect, useState } from "react";
3import { Link, useFetcher } from "react-router";
4
5import type { InboxCounts, InboxItem, InboxSeverity } from "@g1t/contracts";
6
Phones reach every page: each mode's menu from the top bar or by tapping the tab you're on, Docs first in the avatar sheet, a project's pages as a swipeable strip; dialogs as bottom sheets above the keyboard, safe areas, menus kept on screen, 40px targets, touch-visible controls, save bars above the tab bar7import { WaitingCards } from "./notifications/card-actions";
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all8import { SubmitButton } from "./ui";
9import { Badge, type BadgeTone } from "./ui/badge";
Merge branch 'worktree-agent-ae1299e92e4462012'10import { Hint } from "./ui/hint";
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all11import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger } from "./ui/dropdown-menu";
12import { Sheet, SheetClose, SheetContent, SheetDescription, SheetFooter, SheetHeader, SheetTitle, SheetTrigger } from "./ui/sheet";
13import { Skeleton } from "./ui/skeleton";
14import { Tabs, TabsList, TabsTrigger } from "./ui/tabs";
15import { cn } from "../lib/cn";
16import { isPending } from "../lib/pending";
Inbox on the site: reasons, a reason filter, Subscribe, Watch and settings17import { INBOX_TABS, type InboxTab, REASON_LABEL, SEVERITY_LABEL, SNOOZES, bellCount, emptyFor, isUnread, tabCount, updatesLabel, whenShort } from "../lib/inbox";
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all18import type { InboxPanelData } from "../routes/inbox-json";
19
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.20/** Where every notifications form posts (routes/notifications.tsx). */
21const ACTION = "/notifications";
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all22/** How often the bell asks for its count while the page is in view. */
23const COUNT_EVERY_MS = 60_000;
24
25const TONE: Record<InboxSeverity, BadgeTone> = {
26 error: "danger",
27 warning: "warn",
28 success: "accent",
29 info: "info",
30};
31
32const ICON: Record<InboxSeverity, { icon: ReactNode; ring: string }> = {
33 error: { icon: <CircleX size={15} />, ring: "bg-danger/10 text-danger ring-danger/30" },
34 warning: { icon: <Hand size={15} />, ring: "bg-warn/10 text-warn ring-warn/30" },
Merge branch 'worktree-agent-ad7c6d88d93adc817'35 success: { icon: <CircleCheck size={15} />, ring: "bg-success/10 text-success ring-success/30" },
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all36 info: { icon: <Info size={15} />, ring: "bg-info/10 text-info ring-info/30" },
37};
38
39/** The round mark that says what kind of item it is. */
40function SeverityMark({ severity }: { severity: InboxSeverity }) {
41 const { icon, ring } = ICON[severity];
42 return (
43 <span aria-hidden="true" className={cn("mt-0.5 flex size-8 shrink-0 items-center justify-center rounded-full ring-1", ring)}>
44 {icon}
45 </span>
46 );
47}
48
49/** Hidden fields naming one item's action. */
50function Fields({ intent, id, extra }: { intent: string; id: string; extra?: Record<string, string> }) {
51 return (
52 <>
53 <input type="hidden" name="intent" value={intent} />
54 <input type="hidden" name="id" value={id} />
55 {Object.entries(extra ?? {}).map(([name, value]) => (
56 <input key={name} type="hidden" name={name} value={value} />
57 ))}
58 </>
59 );
60}
61
62/**
63 * One item: what happened, to what, when, and how much it matters. The
64 * whole card opens what it is about and marks it read; Done is at hand,
65 * and the menu has the rest. Read items are dimmed. While Done or Snooze
66 * is on its way the card steps out of the list.
67 */
68export function InboxCard({ item, onOpen }: { item: InboxItem; onOpen?: () => void }) {
69 const fetcher = useFetcher<{ error?: string }>();
70 const unread = isUnread(item);
71 const leaving = isPending(fetcher, { id: item.id }) && ["done", "snooze", "undone"].includes(String(fetcher.formData?.get("intent")));
72 const [now, setNow] = useState(() => Date.now());
Inbox on the site: reasons, a reason filter, Subscribe, Watch and settings73 useEffect(() => setNow(Date.now()), [item.updatedAt]);
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all74 if (leaving) return null;
75
76 const submit = (intent: string, extra?: Record<string, string>) =>
77 fetcher.submit({ intent, id: item.id, ...extra }, { method: "post", action: ACTION });
78 const open = () => {
79 if (unread) submit("read");
80 onOpen?.();
81 };
82
83 return (
84 <li
85 className={cn(
86 "group relative flex gap-3 rounded-lg border border-line bg-bg/40 p-3 transition-colors hover:border-line-strong hover:bg-raised/50",
87 !unread && "opacity-60 hover:opacity-100",
88 )}
89 >
90 <SeverityMark severity={item.severity} />
91 <div className="min-w-0 flex-1">
92 <Link
93 to={item.url}
94 onClick={open}
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.95 className="block truncate pr-14 text-sm font-semibold text-fg outline-none [@media(hover:none)]:pr-24 after:absolute after:inset-0 after:rounded-lg focus-visible:after:ring-2 focus-visible:after:ring-accent"
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all96 >
97 {item.title}
98 </Link>
99 {item.body && <p className="mt-0.5 truncate text-xs text-muted">{item.body}</p>}
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.100 <div className="mt-2 flex min-w-0 items-center gap-2 text-xs text-faint [@media(hover:none)]:pr-24">
Inbox on the site: reasons, a reason filter, Subscribe, Watch and settings101 <time dateTime={item.updatedAt} suppressHydrationWarning className="shrink-0 whitespace-nowrap">
102 {whenShort(item.updatedAt, now)}
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all103 </time>
Inbox on the site: reasons, a reason filter, Subscribe, Watch and settings104 <Badge tone={TONE[item.severity]} className="shrink-0">{SEVERITY_LABEL[item.severity]}</Badge>
105 {/* Why they were told, and how much has happened, said quietly. */}
106 <span className="truncate">{REASON_LABEL[item.reason] ?? item.reason}</span>
107 {updatesLabel(item.count) && (
108 <>
109 <span aria-hidden="true">·</span>
110 <span className="shrink-0 tabular-nums">{updatesLabel(item.count)}</span>
111 </>
112 )}
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all113 {item.saved && (
114 <span className="inline-flex items-center gap-1 text-faint">
115 <Bookmark size={12} aria-hidden="true" />
116 Saved
117 </span>
118 )}
119 </div>
120 {fetcher.data?.error && <p className="mt-1 text-xs text-danger">{fetcher.data.error}</p>}
121 </div>
122 {unread && <span aria-label="Unread" className="absolute top-3.5 right-3 size-2 rounded-full bg-accent group-focus-within:hidden group-hover:hidden [@media(hover:none)]:hidden" />}
123 {/* Above the card's link, so they act instead of opening it. On hover or focus, and always on a touch screen. */}
124 <div className="absolute top-2 right-2 z-10 hidden items-center gap-0.5 group-focus-within:flex group-hover:flex [@media(hover:none)]:flex">
125 {item.doneAt ? (
126 <fetcher.Form method="post" action={ACTION}>
127 <Fields intent="undone" id={item.id} />
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.128 <Hint label="Move back to Notifications">
129 <SubmitButton fetcher={fetcher} icon aria-label="Move back to Notifications" className={ICON_BUTTON}>
Merge branch 'worktree-agent-ae1299e92e4462012'130 <Undo2 size={14} />
131 </SubmitButton>
132 </Hint>
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all133 </fetcher.Form>
134 ) : (
135 <fetcher.Form method="post" action={ACTION}>
136 <Fields intent="done" id={item.id} />
Merge branch 'worktree-agent-ae1299e92e4462012'137 <Hint label="Done">
138 <SubmitButton fetcher={fetcher} icon aria-label="Done" className={ICON_BUTTON}>
139 <Check size={14} />
140 </SubmitButton>
141 </Hint>
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all142 </fetcher.Form>
143 )}
144 <DropdownMenu>
145 <DropdownMenuTrigger aria-label="More actions" className={ICON_BUTTON}>
146 <Ellipsis size={14} />
147 </DropdownMenuTrigger>
148 <DropdownMenuContent align="end">
149 <DropdownMenuItem onSelect={() => submit(unread ? "read" : "unread")}>
150 {unread ? <MailOpen /> : <Mail />}
151 {unread ? "Mark as read" : "Mark as unread"}
152 </DropdownMenuItem>
153 <DropdownMenuItem onSelect={() => submit(item.saved ? "unsave" : "save")}>
154 <Bookmark />
155 {item.saved ? "Unsave" : "Save"}
156 </DropdownMenuItem>
157 {!item.doneAt && (
158 <>
159 <DropdownMenuSeparator />
160 <DropdownMenuLabel>Snooze until</DropdownMenuLabel>
161 {SNOOZES.map((snooze) => (
162 <DropdownMenuItem key={snooze.key} onSelect={() => submit("snooze", { for: snooze.key })}>
163 <AlarmClock />
164 {snooze.label}
165 </DropdownMenuItem>
166 ))}
167 </>
168 )}
169 </DropdownMenuContent>
170 </DropdownMenu>
171 </div>
172 </li>
173 );
174}
175
176const ICON_BUTTON =
Phones reach every page: each mode's menu from the top bar or by tapping the tab you're on, Docs first in the avatar sheet, a project's pages as a swipeable strip; dialogs as bottom sheets above the keyboard, safe areas, menus kept on screen, 40px targets, touch-visible controls, save bars above the tab bar177 "flex size-7 max-sm:size-10 items-center justify-center rounded-md border border-line bg-surface text-muted transition-colors outline-none hover:border-line-strong hover:text-fg focus-visible:ring-2 focus-visible:ring-accent disabled:opacity-60";
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all178
179/** What an empty list says, for its tab. */
180export function InboxEmpty({ tab, view }: { tab: InboxTab; view?: "inbox" | "saved" | "done" }) {
181 const { title, detail } = emptyFor(tab, view);
182 return (
183 <div className="flex flex-col items-center gap-2 rounded-lg border border-dashed border-line px-6 py-12 text-center">
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.184 <Bell size={20} className="text-faint" aria-hidden="true" />
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all185 <p className="text-sm font-medium">{title}</p>
186 <p className="max-w-64 text-xs text-muted">{detail}</p>
187 </div>
188 );
189}
190
191/** The tab row, with what is unread under each. */
192export function InboxTabs({ tab, counts, onChange }: { tab: InboxTab; counts: InboxCounts | null; onChange: (tab: InboxTab) => void }) {
193 return (
194 <Tabs value={tab} onValueChange={(value) => onChange(value as InboxTab)}>
195 <TabsList className="flex w-full overflow-x-auto [scrollbar-width:none]">
196 {INBOX_TABS.map((entry) => {
197 const count = tabCount(counts, entry.tab);
198 return (
199 <TabsTrigger key={entry.tab} value={entry.tab} className="flex shrink-0 grow items-center justify-center gap-1.5">
200 {entry.label}
201 {count > 0 && (
202 <span
203 className={cn(
204 "rounded-full px-1.5 text-[0.6875rem] tabular-nums",
205 entry.tab === "needs" ? "bg-warn/15 text-warn" : "bg-line text-muted",
206 )}
207 >
208 {count}
209 </span>
210 )}
211 </TabsTrigger>
212 );
213 })}
214 </TabsList>
215 </Tabs>
216 );
217}
218
219/** Mark all read, for the tab being looked at. */
220export function MarkAllRead({ tab, disabled }: { tab: InboxTab; disabled: boolean }) {
221 const fetcher = useFetcher();
222 return (
223 <fetcher.Form method="post" action={ACTION}>
224 <input type="hidden" name="intent" value="read_all" />
225 <input type="hidden" name="tab" value={tab} />
226 <SubmitButton
227 fetcher={fetcher}
228 disabled={disabled}
229 pending="Marking…"
230 className="inline-flex items-center gap-1.5 rounded-md px-2 py-1 text-xs font-medium text-muted transition-colors hover:bg-raised hover:text-fg disabled:opacity-50 disabled:hover:bg-transparent disabled:hover:text-muted"
231 >
232 <CheckCheck size={14} />
233 Mark all read
234 </SubmitButton>
235 </fetcher.Form>
236 );
237}
238
239/**
240 * The bell in the top bar, and the panel it opens from the right: the
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.241 * notifications's tabs and newest items, to work through without leaving the page.
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all242 * Its count comes with the page (root.tsx), and is asked for again every
243 * minute while the page is in view, and after anything is marked.
244 */
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.245export function NotificationsBell({ counts: loaded }: { counts: InboxCounts | null }) {
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all246 const [open, setOpen] = useState(false);
247 const [tab, setTab] = useState<InboxTab>("all");
248 const list = useFetcher<InboxPanelData>({ key: "inbox-panel" });
249 const poll = useFetcher<InboxPanelData>({ key: "inbox-count" });
250
251 useEffect(() => {
252 if (open) list.load(`/inbox.json?tab=${tab}`);
253 // eslint-disable-next-line react-hooks/exhaustive-deps
254 }, [open, tab]);
255 useEffect(() => {
256 const ask = () => {
257 if (document.visibilityState === "visible") poll.load("/inbox.json?only=counts");
258 };
259 const timer = setInterval(ask, COUNT_EVERY_MS);
260 window.addEventListener("focus", ask);
261 return () => {
262 clearInterval(timer);
263 window.removeEventListener("focus", ask);
264 };
265 // eslint-disable-next-line react-hooks/exhaustive-deps
266 }, []);
267
268 const counts = (open ? list.data?.counts : null) ?? poll.data?.counts ?? loaded;
269 const unread = counts?.unread ?? 0;
270 const items = list.data?.tab === tab ? list.data.items : null;
271 const shown = bellCount(unread);
272
273 return (
274 <Sheet open={open} onOpenChange={setOpen}>
275 <SheetTrigger
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.276 aria-label={unread ? `Notifications, ${unread} unread` : "Notifications"}
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all277 className="relative flex size-9 items-center justify-center rounded-md text-muted transition-colors hover:bg-raised hover:text-fg"
278 >
279 <Bell size={17} />
280 {shown && (
281 // Amber while an agent waits on them, red for a failure, else the accent.
282 <span
283 className={cn(
284 "absolute -top-0.5 -right-0.5 flex h-4 min-w-4 items-center justify-center rounded-full px-1 text-[0.625rem] font-semibold tabular-nums text-bg ring-2 ring-bg",
285 (counts?.warning ?? 0) > 0 ? "bg-warn" : (counts?.error ?? 0) > 0 ? "bg-danger" : "bg-accent",
286 )}
287 >
288 {shown}
289 </span>
290 )}
291 </SheetTrigger>
292 <SheetContent
293 // Focus starts on the tab being looked at, not on Mark all read.
294 onOpenAutoFocus={(event) => {
295 event.preventDefault();
296 (event.currentTarget as HTMLElement | null)?.querySelector<HTMLElement>('[role="tab"][data-state="active"]')?.focus();
297 }}
298 >
299 <SheetHeader className="flex-row items-center justify-between gap-3">
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.300 <SheetTitle>Notifications</SheetTitle>
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all301 <MarkAllRead tab={tab} disabled={tabCount(counts, tab) === 0} />
302 </SheetHeader>
303 <div className="border-b border-line px-5 py-3">
304 <InboxTabs tab={tab} counts={counts} onChange={setTab} />
305 </div>
306 <div className="min-h-0 flex-1 overflow-y-auto px-5 py-4">
Phones reach every page: each mode's menu from the top bar or by tapping the tab you're on, Docs first in the avatar sheet, a project's pages as a swipeable strip; dialogs as bottom sheets above the keyboard, safe areas, menus kept on screen, 40px targets, touch-visible controls, save bars above the tab bar307 {/* Chat cards waiting on you (live notifications), to act on from here. */}
308 <WaitingCards onNavigate={() => setOpen(false)} />
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all309 {items == null ? (
310 list.state === "idle" && list.data?.tab === tab ? (
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.311 <p className="text-sm text-muted">Notifications could not be loaded. Try again in a moment.</p>
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all312 ) : (
313 <ul aria-busy="true" className="space-y-2">
314 {Array.from({ length: 4 }, (_, index) => (
315 <li key={index} className="flex gap-3 rounded-lg border border-line p-3">
316 <Skeleton className="size-8 rounded-full" />
317 <div className="flex-1 space-y-2">
318 <Skeleton className="h-3.5 w-3/4" />
319 <Skeleton className="h-3 w-1/2" />
320 </div>
321 </li>
322 ))}
323 </ul>
324 )
325 ) : items.length === 0 ? (
326 <InboxEmpty tab={tab} />
327 ) : (
328 <ul className="space-y-2">
329 {items.map((item) => (
330 <InboxCard key={item.id} item={item} onOpen={() => setOpen(false)} />
331 ))}
332 </ul>
333 )}
334 </div>
335 <SheetFooter className="justify-between">
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.336 <SheetDescription className="text-xs">Done, saved and snoozed items are on the Notifications page.</SheetDescription>
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all337 <SheetClose asChild>
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.338 <Link to={tab === "all" ? "/notifications" : `/notifications?tab=${tab}`} className="shrink-0 text-sm font-medium text-accent hover:underline">
339 Open Notifications
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all340 </Link>
341 </SheetClose>
342 </SheetFooter>
343 </SheetContent>
344 </Sheet>
345 );
346}
347
348/**
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.349 * Mission control's card of what is unread in the person's notifications and
Usage, Billing settings and prepaid AI credit; fixes from the UX audit350 * worth a look: items an agent is waiting on, then failures. Titled for
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.351 * notifications, not "Needs you", which on mission control is the work list's
Usage, Billing settings and prepaid AI credit; fixes from the UX audit352 * own count. Not shown when there are none.
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all353 */
354export function InboxNeedsCard({ items, total }: { items: InboxItem[]; total: number }) {
355 if (items.length === 0) return null;
356 return (
357 <section className="rounded-xl border border-line bg-surface p-5">
358 <div className="flex items-center justify-between gap-3">
359 <h2 className="flex items-center gap-2 text-sm font-semibold">
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.360 <Bell size={14} className="text-warn" aria-hidden="true" />
361 From your notifications
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all362 <span className="rounded-full bg-warn/15 px-1.5 text-[0.6875rem] font-medium tabular-nums text-warn">{total}</span>
363 </h2>
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.364 <Link to="/notifications" className="text-xs font-medium text-accent hover:underline">
365 Open Notifications
Inbox: a bell in the top bar opens it beside the page, and /inbox holds it all366 </Link>
367 </div>
368 <ul className="mt-3 space-y-2">
369 {items.map((item) => (
370 <InboxCard key={item.id} item={item} />
371 ))}
372 </ul>
373 </section>
374 );
375}

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