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 the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 1 | import { |
| 2 | Activity, | |
| One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers | 3 | BellOff, |
| 4 | BellRing, | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 5 | BookOpen, |
| 6 | CircleUserRound, | |
| 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. | 7 | Ellipsis, |
| 8 | Keyboard, | |
| 9 | LayoutGrid, | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 10 | LifeBuoy, |
| 11 | LogOut, | |
| One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers | 12 | Moon, |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 13 | Settings, |
| One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers | 14 | Smile, |
| 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. | 15 | Store, |
| One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers | 16 | Sun, |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 17 | } from "lucide-react"; |
| 18 | import { Dialog as Primitive } from "radix-ui"; | |
| 19 | import { type ReactNode, useEffect, useRef, useState } from "react"; | |
| 20 | import { Link, useLocation, useNavigate, useSubmit } from "react-router"; | |
| 21 | ||
| 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 bar | 22 | import { type Abilities, type Membership, type User, hasCodeAccess, shownUsername } from "@g1t/contracts"; |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 23 | |
| 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. | 24 | import { appIcon } from "./apps"; |
| 25 | import { CountBadge, ShortcutsDialog } from "./dock"; | |
| 26 | import { Mark } from "./logo"; | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 27 | import { Avatar } from "./ui"; |
| 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 bar | 28 | import { TabStrip } from "./ui/tab-strip"; |
| One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers | 29 | import { StatusDialog } from "./presence"; |
| 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. | 30 | import { type AppKey, type PinnableApp, appOf } from "../lib/apps"; |
| One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers | 31 | import { setPresence, useOwnPresence } from "../lib/notify-client"; |
| 32 | import { dndOn, liveStatus, pauseUntil, untilLabel } from "../lib/presence"; | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 33 | import { STATUS_URL } from "../lib/status"; |
| 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. | 34 | import { type ModeKey, modeHome, modeOf } from "../lib/workspace-nav"; |
| 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 bar | 35 | import { PROJECT_PAGE_LINKS, type ProjectPage, projectPageAt, projectPages } from "../lib/chrome"; |
| 36 | import { ROADMAP, type RoadmapItem } from "../lib/roadmap"; | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 37 | |
| 38 | /** | |
| 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. | 39 | * The phone's layout (below 768px): a bottom bar of apps with More for |
| 40 | * the rest, the mode's sidebar as a drawer from the left, and each tab a | |
| 41 | * list that pushes its detail full screen. A computer has the dock. | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 42 | */ |
| 43 | ||
| 44 | /** Whether a path is one conversation, which takes the whole screen on a phone. */ | |
| 45 | export function isConversation(pathname: string): boolean { | |
| 46 | return /^\/[^/]+\/-\/chat\/(?!browse\/?$)[^/]+/.test(pathname); | |
| 47 | } | |
| 48 | ||
| 49 | /** | |
| 50 | * Follows the visual viewport, which shrinks when the keyboard opens: its | |
| 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 bar | 51 | * height and top go on the page as `--vv-height` and `--vv-top`, what |
| 52 | * the keyboard covers as `--keyboard-inset`, and | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 53 | * `data-keyboard="open"` on <html> while the keyboard is up, so a |
| 54 | * composer can sit just above it and the tab bar can step aside. | |
| 55 | */ | |
| 56 | export function useVisualViewport() { | |
| 57 | useEffect(() => { | |
| 58 | const viewport = window.visualViewport; | |
| 59 | if (!viewport) return; | |
| 60 | const root = document.documentElement; | |
| 61 | const update = () => { | |
| 62 | root.style.setProperty("--vv-height", `${viewport.height}px`); | |
| 63 | root.style.setProperty("--vv-top", `${viewport.offsetTop}px`); | |
| 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 bar | 64 | // How much of the bottom the keyboard covers, for a sheet to sit on it. |
| 65 | root.style.setProperty("--keyboard-inset", `${Math.max(0, window.innerHeight - viewport.height - viewport.offsetTop)}px`); | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 66 | const keyboard = window.innerHeight - viewport.height > 140; |
| 67 | if (keyboard) root.dataset.keyboard = "open"; | |
| 68 | else delete root.dataset.keyboard; | |
| 69 | }; | |
| 70 | update(); | |
| 71 | viewport.addEventListener("resize", update); | |
| 72 | viewport.addEventListener("scroll", update); | |
| 73 | return () => { | |
| 74 | viewport.removeEventListener("resize", update); | |
| 75 | viewport.removeEventListener("scroll", update); | |
| 76 | }; | |
| 77 | }, []); | |
| 78 | } | |
| 79 | ||
| 80 | /** | |
| 81 | * Swipe from the left edge to go back, as on a phone's own apps: a touch | |
| 82 | * that starts within 28px of the edge and travels 80px right, more across | |
| 83 | * than down. Going back is the browser's own back, so history stays true. | |
| 84 | */ | |
| 85 | export function useSwipeBack(onBack: () => void) { | |
| 86 | const start = useRef<{ x: number; y: number } | null>(null); | |
| 87 | return { | |
| 88 | onTouchStart: (event: React.TouchEvent) => { | |
| 89 | const touch = event.touches[0]; | |
| 90 | start.current = touch && touch.clientX < 28 ? { x: touch.clientX, y: touch.clientY } : null; | |
| 91 | }, | |
| 92 | onTouchMove: (event: React.TouchEvent) => { | |
| 93 | const touch = event.touches[0]; | |
| 94 | if (!start.current || !touch) return; | |
| 95 | const dx = touch.clientX - start.current.x; | |
| 96 | const dy = Math.abs(touch.clientY - start.current.y); | |
| 97 | if (dx > 80 && dx > dy * 1.5) { | |
| 98 | start.current = null; | |
| 99 | onBack(); | |
| 100 | } | |
| 101 | }, | |
| 102 | onTouchEnd: () => { | |
| 103 | start.current = null; | |
| 104 | }, | |
| 105 | }; | |
| 106 | } | |
| 107 | ||
| 108 | /** Back, as the browser would; to `fallback` when this page was the first one opened. */ | |
| 109 | export function useBack(fallback: string) { | |
| 110 | const navigate = useNavigate(); | |
| 111 | return () => { | |
| 112 | const index = (window.history.state as { idx?: number } | null)?.idx ?? 0; | |
| 113 | if (index > 0) navigate(-1); | |
| 114 | else navigate(fallback, { replace: true }); | |
| 115 | }; | |
| 116 | } | |
| 117 | ||
| 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. | 118 | type Tab = { key: ModeKey; label: string; badge?: { count: number; loud: boolean } }; |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 119 | |
| 120 | /** | |
| 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. | 121 | * The bottom bar of a phone, floating 8px from the edges: Today, Chat, |
| 122 | * Notifications, Agents and Code (for a member with Code access), each | |
| 123 | * with what is unread, and More. It steps aside while the keyboard is up | |
| 124 | * and inside a conversation. The tab you are in, tapped again, opens its | |
| 125 | * mode's menu (its sidebar). | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 126 | */ |
| 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. | 127 | export function BottomBar({ |
| 128 | user, | |
| 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 bar | 129 | workspace, |
| 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. | 130 | pins, |
| 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 bar | 131 | unread, |
| 132 | onReselect, | |
| 133 | }: { | |
| 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. | 134 | user: User; |
| 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 bar | 135 | workspace: Membership; |
| 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. | 136 | /** The apps pinned to this person's dock here, for the More sheet. */ |
| 137 | pins: PinnableApp[]; | |
| 138 | unread: { notifications: number; chat: number; mentions: number }; | |
| 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 bar | 139 | /** Tapping the tab you are already in: the shell opens that mode's menu (its sidebar). */ |
| 140 | onReselect?: () => void; | |
| 141 | }) { | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 142 | const { pathname } = useLocation(); |
| 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. | 143 | const [more, setMore] = useState(false); |
| 144 | useEffect(() => setMore(false), [pathname]); | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 145 | const slug = workspace.slug; |
| 146 | const code = hasCodeAccess(workspace); | |
| 147 | const mode = modeOf(pathname, slug); | |
| 148 | const tabs: 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. | 149 | { key: "today", label: "Today" }, |
| 150 | { key: "chat", label: "Chat", badge: { count: unread.mentions > 0 ? unread.mentions : unread.chat, loud: unread.mentions > 0 } }, | |
| 151 | { key: "notifications", label: "Notifications", badge: { count: unread.notifications, loud: true } }, | |
| 152 | { key: "agents", label: "Agents" }, | |
| 153 | ...(code ? [{ key: "code" as const, label: "Code" }] : []), | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 154 | ]; |
| 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. | 155 | const inTabs = tabs.some((tab) => tab.key === mode); |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 156 | if (isConversation(pathname)) return null; |
| 157 | return ( | |
| 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. | 158 | <> |
| 159 | <nav | |
| 160 | aria-label="Apps" | |
| 161 | className="fixed right-2 bottom-[calc(0.5rem+env(safe-area-inset-bottom))] left-2 z-40 h-16 rounded-2xl border border-line bg-dock px-1 shadow-[0_1px_0_rgba(255,255,255,0.03)_inset,0_8px_24px_rgba(0,0,0,0.35)] md:hidden in-data-[keyboard=open]:hidden" | |
| 162 | > | |
| 163 | <ul className="grid h-full" style={{ gridTemplateColumns: `repeat(${tabs.length + 1}, minmax(0, 1fr))` }}> | |
| 164 | {tabs.map((tab) => { | |
| 165 | const current = mode === tab.key && !more; | |
| 166 | const count = tab.badge?.count ?? 0; | |
| 167 | const to = modeHome(tab.key, slug); | |
| 168 | return ( | |
| 169 | <li key={tab.key} className="min-w-0"> | |
| 170 | <Link | |
| 171 | to={to} | |
| 172 | prefetch="intent" | |
| 173 | aria-current={current ? "page" : undefined} | |
| 174 | aria-label={count > 0 ? `${tab.label}, ${count} unread` : tab.label} | |
| 175 | onClick={(event) => { | |
| 176 | // The tab you are in, tapped again: its menu, as a phone's own apps do. | |
| 177 | // Chat's first page on a phone is its sidebar already. | |
| 178 | if (!current || !onReselect || (tab.key === "chat" && pathname.replace(/\/$/, "") === to)) return; | |
| 179 | event.preventDefault(); | |
| 180 | onReselect(); | |
| 181 | }} | |
| 182 | className={BAR_ITEM} | |
| 183 | > | |
| 184 | {current && <TopBar />} | |
| 185 | <BarIcon current={current}> | |
| 186 | {appIcon(tab.key as AppKey, 20)} | |
| 187 | {tab.badge && <CountBadge count={count} loud={tab.badge.loud} />} | |
| 188 | </BarIcon> | |
| 189 | <span className={`w-full truncate text-[0.625rem] leading-tight font-medium max-[380px]:text-[0.5625rem] max-[380px]:tracking-[-0.02em] ${current ? "text-fg" : "text-faint"}`}>{tab.label}</span> | |
| 190 | </Link> | |
| 191 | </li> | |
| 192 | ); | |
| 193 | })} | |
| 194 | <li className="min-w-0"> | |
| 195 | <button type="button" aria-expanded={more} aria-haspopup="dialog" onClick={() => setMore(true)} className={BAR_ITEM}> | |
| 196 | {(more || !inTabs) && <TopBar />} | |
| 197 | <BarIcon current={more || !inTabs}> | |
| 198 | <Ellipsis size={20} /> | |
| 199 | </BarIcon> | |
| 200 | <span className={`w-full truncate text-[0.625rem] leading-tight font-medium ${more || !inTabs ? "text-fg" : "text-faint"}`}>More</span> | |
| 201 | </button> | |
| 202 | </li> | |
| 203 | </ul> | |
| 204 | </nav> | |
| 205 | <MoreSheet open={more} onOpenChange={setMore} user={user} workspace={workspace} pins={pins} /> | |
| 206 | </> | |
| 207 | ); | |
| 208 | } | |
| 209 | ||
| 210 | const BAR_ITEM = "relative flex h-full w-full flex-col items-center justify-center gap-0.5 rounded-xl outline-none transition-colors focus-visible:ring-2 focus-visible:ring-accent active:bg-raised/60"; | |
| 211 | ||
| 212 | /** The bar on the bottom bar's top edge, over the current tab. */ | |
| 213 | function TopBar() { | |
| 214 | return <span aria-hidden="true" className="absolute -top-px left-1/2 h-[3px] w-[22px] -translate-x-1/2 rounded-b-[3px] bg-fg" />; | |
| 215 | } | |
| 216 | ||
| 217 | function BarIcon({ current, children }: { current: boolean; children: ReactNode }) { | |
| 218 | return ( | |
| 219 | <span className={`relative flex size-8 items-center justify-center rounded-[10px] transition-colors ${current ? "bg-raised text-fg shadow-[inset_0_0_0_1px_var(--color-line-strong)]" : "text-muted"}`}> | |
| 220 | {children} | |
| 221 | </span> | |
| 222 | ); | |
| 223 | } | |
| 224 | ||
| 225 | /** A big tile in the More sheet. */ | |
| 226 | function MoreTile({ to, icon, label, onClick, disabled, note }: { to?: string; icon: ReactNode; label: string; onClick?: () => void; disabled?: boolean; note?: string }) { | |
| 227 | const inner = ( | |
| 228 | <> | |
| 229 | <span className={`flex size-11 items-center justify-center rounded-xl ${disabled ? "bg-raised/60 text-faint" : "bg-raised text-fg"}`}>{icon}</span> | |
| 230 | <span className={`line-clamp-2 w-full text-xs leading-tight max-[380px]:text-[0.6875rem] ${disabled ? "text-faint" : "text-fg-soft"}`}>{label}</span> | |
| 231 | {note && <span className="-mt-0.5 rounded-full bg-line px-1.5 text-[0.625rem] font-medium text-muted">{note}</span>} | |
| 232 | </> | |
| 233 | ); | |
| 234 | const className = "flex min-w-0 flex-col items-center gap-1.5 rounded-xl px-1 py-2.5 text-center outline-none focus-visible:ring-2 focus-visible:ring-accent active:bg-raised/60"; | |
| 235 | if (to) { | |
| 236 | return ( | |
| 237 | <Link to={to} className={className}> | |
| 238 | {inner} | |
| 239 | </Link> | |
| 240 | ); | |
| 241 | } | |
| 242 | return ( | |
| 243 | <button type="button" onClick={onClick} disabled={disabled} aria-disabled={disabled} className={className}> | |
| 244 | {inner} | |
| 245 | </button> | |
| 246 | ); | |
| 247 | } | |
| 248 | ||
| 249 | /** | |
| 250 | * More, from the bottom bar: a rounded panel just above it, with g1t's | |
| 251 | * mark at its top and a grid of big tiles: all apps, the Marketplace | |
| 252 | * (coming), your pinned apps, Artifacts, People, Workspace, and "You and | |
| 253 | * help", which opens your account's sheet. | |
| 254 | */ | |
| 255 | function MoreSheet({ open, onOpenChange, user, workspace, pins }: { open: boolean; onOpenChange: (open: boolean) => void; user: User; workspace: Membership; pins: PinnableApp[] }) { | |
| 256 | const slug = workspace.slug; | |
| 257 | const code = hasCodeAccess(workspace); | |
| 258 | const [account, setAccount] = useState(false); | |
| 259 | const shown = pins.filter((key) => code || !appOf(key).code); | |
| 260 | return ( | |
| 261 | <> | |
| 262 | <Primitive.Root open={open} onOpenChange={onOpenChange}> | |
| 263 | <Primitive.Portal> | |
| 264 | <Primitive.Overlay className="fixed inset-0 z-50 bg-black/50 data-[state=open]:animate-fade-in data-[state=closed]:animate-fade-out md:hidden" /> | |
| 265 | <Primitive.Content | |
| 266 | aria-describedby={undefined} | |
| 267 | onOpenAutoFocus={(event) => event.preventDefault()} | |
| 268 | className="fixed right-2 bottom-[calc(4.875rem+env(safe-area-inset-bottom))] left-2 z-50 max-h-[calc(100dvh-7rem)] overflow-y-auto rounded-[18px] border border-line-strong bg-dock px-3 pt-2 pb-3.5 text-fg shadow-2xl shadow-black/60 outline-none data-[state=open]:animate-pop-in data-[state=closed]:animate-pop-out md:hidden" | |
| 269 | > | |
| 270 | <div aria-hidden="true" className="mx-auto mt-0.5 mb-2 h-1 w-9 rounded-full bg-line-strong" /> | |
| 271 | <div className="mb-2 flex items-center gap-2 px-1"> | |
| 272 | <Mark tight className="h-4 w-auto" /> | |
| 273 | <Primitive.Title className="text-sm font-semibold">More</Primitive.Title> | |
| 274 | <span className="ml-auto truncate font-mono text-xs text-faint">{slug}</span> | |
| 275 | </div> | |
| 276 | <div className="grid grid-cols-4 gap-1.5"> | |
| 277 | <MoreTile to={`/${slug}/-/apps`} icon={<LayoutGrid size={20} />} label="All apps" /> | |
| 278 | <MoreTile icon={<Store size={20} />} label="Marketplace" disabled note="Coming" /> | |
| 279 | {shown.map((key) => ( | |
| 280 | <MoreTile key={key} to={appOf(key).path(slug)} icon={appIcon(key, 20)} label={appOf(key).name} /> | |
| 281 | ))} | |
| 282 | <MoreTile to={appOf("artifacts").path(slug)} icon={appIcon("artifacts", 20)} label="Artifacts" /> | |
| 283 | <MoreTile to={appOf("people").path(slug)} icon={appIcon("people", 20)} label="People" /> | |
| 284 | <MoreTile to={appOf("workspace").path(slug)} icon={appIcon("workspace", 20)} label="Workspace" /> | |
| 285 | <MoreTile | |
| 286 | icon={<Avatar name={user.username} image={user.avatar} size={24} />} | |
| 287 | label="You and help" | |
| 288 | onClick={() => { | |
| 289 | onOpenChange(false); | |
| 290 | setAccount(true); | |
| 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 bar | 291 | }} |
| 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. | 292 | /> |
| 293 | </div> | |
| 294 | </Primitive.Content> | |
| 295 | </Primitive.Portal> | |
| 296 | </Primitive.Root> | |
| 297 | <AccountSheet open={account} onOpenChange={setAccount} user={user} /> | |
| 298 | </> | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 299 | ); |
| 300 | } | |
| 301 | /** | |
| 302 | * A sheet that rises from the bottom of a phone, with a handle to drag it | |
| 303 | * back down. Escape, a tap outside or a drag of more than 90px closes it. | |
| 304 | */ | |
| 305 | export function BottomSheet({ | |
| 306 | open, | |
| 307 | onOpenChange, | |
| 308 | title, | |
| 309 | children, | |
| 310 | }: { | |
| 311 | open: boolean; | |
| 312 | onOpenChange: (open: boolean) => void; | |
| 313 | title: string; | |
| 314 | children: ReactNode; | |
| 315 | }) { | |
| 316 | const [drag, setDrag] = useState(0); | |
| 317 | const from = useRef<number | null>(null); | |
| 318 | useEffect(() => { | |
| 319 | if (!open) setDrag(0); | |
| 320 | }, [open]); | |
| 321 | const handlers = { | |
| 322 | onPointerDown: (event: React.PointerEvent) => { | |
| 323 | from.current = event.clientY; | |
| 324 | (event.currentTarget as HTMLElement).setPointerCapture(event.pointerId); | |
| 325 | }, | |
| 326 | onPointerMove: (event: React.PointerEvent) => { | |
| 327 | if (from.current == null) return; | |
| 328 | setDrag(Math.max(0, event.clientY - from.current)); | |
| 329 | }, | |
| 330 | onPointerUp: () => { | |
| 331 | if (drag > 90) onOpenChange(false); | |
| 332 | else setDrag(0); | |
| 333 | from.current = null; | |
| 334 | }, | |
| 335 | }; | |
| 336 | return ( | |
| 337 | <Primitive.Root open={open} onOpenChange={onOpenChange}> | |
| 338 | <Primitive.Portal> | |
| 339 | <Primitive.Overlay className="fixed inset-0 z-50 bg-black/60 data-[state=open]:animate-fade-in data-[state=closed]:animate-fade-out" /> | |
| 340 | <Primitive.Content | |
| 341 | aria-describedby={undefined} | |
| 342 | onOpenAutoFocus={(event) => event.preventDefault()} | |
| 343 | style={{ transform: drag ? `translateY(${drag}px)` : undefined, transition: from.current == null ? "transform 0.2s ease-out" : "none" }} | |
| 344 | className="fixed inset-x-0 bottom-0 z-50 flex max-h-[88dvh] flex-col rounded-t-2xl border-t border-line-strong bg-surface pb-[env(safe-area-inset-bottom)] text-fg shadow-2xl shadow-black/60 outline-none data-[state=open]:animate-[sheet-in-bottom_0.24s_cubic-bezier(0.16,1,0.3,1)]" | |
| 345 | > | |
| 346 | <div {...handlers} className="flex shrink-0 cursor-grab touch-none justify-center pt-2.5 pb-2" aria-hidden="true"> | |
| 347 | <span className="h-1.5 w-10 rounded-full bg-line-strong" /> | |
| 348 | </div> | |
| 349 | <Primitive.Title className="sr-only">{title}</Primitive.Title> | |
| 350 | <div className="min-h-0 grow overflow-y-auto overscroll-contain px-3 pb-3">{children}</div> | |
| 351 | </Primitive.Content> | |
| 352 | </Primitive.Portal> | |
| 353 | </Primitive.Root> | |
| 354 | ); | |
| 355 | } | |
| 356 | ||
| 357 | /** A row in a bottom sheet: 48px tall, an icon, words, and what sits at its end. */ | |
| 358 | export function SheetRow({ to, href, icon, children, end, onClick }: { to?: string; href?: string; icon: ReactNode; children: ReactNode; end?: ReactNode; onClick?: () => void }) { | |
| 359 | const className = "flex h-12 w-full items-center gap-3 rounded-xl px-3 text-left text-[0.9375rem] text-fg-soft transition-colors active:bg-raised"; | |
| 360 | const inner = ( | |
| 361 | <> | |
| 362 | <span className="flex w-6 shrink-0 justify-center text-muted [&_svg]:size-5">{icon}</span> | |
| 363 | <span className="min-w-0 grow truncate">{children}</span> | |
| 364 | {end} | |
| 365 | </> | |
| 366 | ); | |
| 367 | if (to) { | |
| 368 | return ( | |
| 369 | <Link to={to} className={className} onClick={onClick}> | |
| 370 | {inner} | |
| 371 | </Link> | |
| 372 | ); | |
| 373 | } | |
| 374 | if (href) { | |
| 375 | return ( | |
| 376 | <a href={href} className={className}> | |
| 377 | {inner} | |
| 378 | </a> | |
| 379 | ); | |
| 380 | } | |
| 381 | return ( | |
| 382 | <button type="button" onClick={onClick} className={className}> | |
| 383 | {inner} | |
| 384 | </button> | |
| 385 | ); | |
| 386 | } | |
| 387 | ||
| 388 | function SheetGroup({ title, children }: { title: string; children: ReactNode }) { | |
| 389 | return ( | |
| 390 | <section className="mt-2 border-t border-line pt-2"> | |
| 391 | <h3 className="px-3 pt-1 pb-1 text-xs font-medium text-faint">{title}</h3> | |
| 392 | {children} | |
| 393 | </section> | |
| 394 | ); | |
| 395 | } | |
| 396 | ||
| One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers | 397 | /** Your status, away and pausing notifications, as the account menu has them on a computer. */ |
| 398 | function OwnPresenceRows({ onEdit }: { onEdit: () => void }) { | |
| 399 | const me = useOwnPresence(); | |
| 400 | const now = Date.now(); | |
| 401 | const status = liveStatus(me?.status, now); | |
| 402 | const paused = dndOn(me, now); | |
| 403 | const away = me?.away_manual === true; | |
| 404 | return ( | |
| 405 | <> | |
| 406 | <SheetRow icon={status?.emoji ? <span className="text-lg leading-none">{status.emoji}</span> : <Smile />} onClick={onEdit}> | |
| 407 | {status ? status.text || "Status set" : "Set a status"} | |
| 408 | </SheetRow> | |
| 409 | <SheetRow icon={away ? <Sun /> : <Moon />} onClick={() => void setPresence({ away: !away }, { away_manual: !away, presence: away ? "active" : "away" })}> | |
| 410 | {away ? "Set yourself active" : "Set yourself away"} | |
| 411 | </SheetRow> | |
| 412 | {paused ? ( | |
| 413 | <SheetRow | |
| 414 | icon={<BellRing />} | |
| 415 | end={me?.dnd_until ? <span className="text-xs text-faint">{untilLabel(me.dnd_until, new Date(now))}</span> : null} | |
| 416 | onClick={() => void setPresence({ dnd_until: null }, { dnd_until: null })} | |
| 417 | > | |
| 418 | Resume notifications | |
| 419 | </SheetRow> | |
| 420 | ) : ( | |
| 421 | <SheetRow | |
| 422 | icon={<BellOff />} | |
| 423 | onClick={() => { | |
| 424 | const until = pauseUntil("1h", new Date()); | |
| 425 | void setPresence({ dnd_until: until }, { dnd_until: until }); | |
| 426 | }} | |
| 427 | > | |
| 428 | Pause notifications for 1 hour | |
| 429 | </SheetRow> | |
| 430 | )} | |
| 431 | </> | |
| 432 | ); | |
| 433 | } | |
| 434 | ||
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 435 | /** |
| 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. | 436 | * You and help, on a phone: your status, your profile and settings, the |
| 437 | * documentation, support, status and the keyboard's shortcuts, and signing | |
| 438 | * out, as the account menu has them on a computer. | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 439 | */ |
| 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. | 440 | export function AccountSheet({ open, onOpenChange, user }: { open: boolean; onOpenChange: (open: boolean) => void; user: User }) { |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 441 | const submit = useSubmit(); |
| One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers | 442 | const [editing, setEditing] = useState(false); |
| 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. | 443 | const [keys, setKeys] = useState(false); |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 444 | return ( |
| 445 | <> | |
| 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. | 446 | <BottomSheet open={open} onOpenChange={onOpenChange} title="You and help"> |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 447 | <div className="flex items-center gap-3 px-3 pt-1 pb-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. | 448 | <Avatar name={user.username} image={user.avatar} size={44} /> |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 449 | <div className="min-w-0"> |
| 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. | 450 | <p className="truncate text-base font-semibold">@{shownUsername(user)}</p> |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 451 | </div> |
| 452 | </div> | |
| 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. | 453 | <SheetGroup title="You"> |
| 454 | <OwnPresenceRows | |
| 455 | onEdit={() => { | |
| 456 | onOpenChange(false); | |
| 457 | setEditing(true); | |
| 458 | }} | |
| 459 | /> | |
| 460 | <SheetRow to={`/u/${user.username}`} icon={<CircleUserRound />}> | |
| 461 | Your profile | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 462 | </SheetRow> |
| 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. | 463 | <SheetRow to="/settings" icon={<Settings />}> |
| 464 | Your settings | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 465 | </SheetRow> |
| 466 | </SheetGroup> | |
| 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. | 467 | <SheetGroup title="Help"> |
| 468 | <SheetRow href="https://docs.g1t.sh/" icon={<BookOpen />}> | |
| 469 | Documentation | |
| 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 bar | 470 | </SheetRow> |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 471 | <SheetRow to="/support" icon={<LifeBuoy />}> |
| 472 | Support | |
| 473 | </SheetRow> | |
| 474 | <SheetRow href={STATUS_URL} icon={<Activity />}> | |
| 475 | Status | |
| 476 | </SheetRow> | |
| 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. | 477 | <SheetRow |
| 478 | icon={<Keyboard />} | |
| 479 | onClick={() => { | |
| 480 | onOpenChange(false); | |
| 481 | setKeys(true); | |
| One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers | 482 | }} |
| 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. | 483 | > |
| 484 | Keyboard shortcuts | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 485 | </SheetRow> |
| 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. | 486 | </SheetGroup> |
| 487 | <SheetGroup title="Account"> | |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 488 | <SheetRow icon={<LogOut />} onClick={() => submit(null, { method: "post", action: "/logout" })}> |
| 489 | Sign out | |
| 490 | </SheetRow> | |
| 491 | </SheetGroup> | |
| 492 | </BottomSheet> | |
| One kind of access token; presence and status; usernames keep their case; the tour is a miniature of the real app; icons for password managers | 493 | <StatusDialog open={editing} onOpenChange={setEditing} /> |
| 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. | 494 | <ShortcutsDialog open={keys} onOpenChange={setKeys} /> |
| Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002) | 495 | </> |
| 496 | ); | |
| 497 | } | |
| 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 bar | 498 | |
| 499 | /** Which project page a roadmap page (`soon/<key>`) sits under, by its section. */ | |
| 500 | const SOON_UNDER: Partial<Record<RoadmapItem["section"], ProjectPage>> = { | |
| 501 | Code: "code", | |
| 502 | Issues: "issues", | |
| 503 | Agents: "agents", | |
| 504 | Deployments: "deployments", | |
| 505 | Observability: "observability", | |
| 506 | Security: "security", | |
| 507 | Insights: "insights", | |
| 508 | }; | |
| 509 | const SOON_PAGES: Record<string, ProjectPage> = Object.fromEntries( | |
| 510 | ROADMAP.flatMap((item) => (SOON_UNDER[item.section] ? [[item.key, SOON_UNDER[item.section]!] as const] : [])), | |
| 511 | ); | |
| 512 | ||
| 513 | /** | |
| 514 | * A project's pages on a phone, under its name: the sidebar's own list | |
| 515 | * (lib/chrome.ts) as a row of tabs that scrolls sideways, the current one | |
| 516 | * kept in view, so Issues and Pull requests are one tap away. From 768px | |
| 517 | * the sidebar has them. | |
| 518 | */ | |
| 519 | export function ProjectStrip({ | |
| 520 | base, | |
| 521 | member, | |
| 522 | can, | |
| 523 | settings, | |
| 524 | counts, | |
| 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. | 525 | always = false, |
| 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 bar | 526 | }: { |
| 527 | /** `/<namespace>/<name>`. */ | |
| 528 | base: string; | |
| 529 | member: boolean; | |
| 530 | can?: Partial<Abilities>; | |
| 531 | /** Whether they see its settings (lib/access.ts `seesSettings`), as the sidebar decides. */ | |
| 532 | settings?: boolean; | |
| 533 | counts?: { issues: number; pulls: number }; | |
| 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. | 534 | /** On a computer too: for a visitor, who has no sidebar to list them. */ |
| 535 | always?: boolean; | |
| 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 bar | 536 | }) { |
| 537 | const { pathname } = useLocation(); | |
| 538 | const rest = pathname.toLowerCase().startsWith(base.toLowerCase()) ? pathname.slice(base.length) : ""; | |
| 539 | const at = projectPageAt(rest, SOON_PAGES); | |
| 540 | const pages: (ProjectPage | "overview")[] = ["overview", ...projectPages(member, can).filter((page) => page !== "observability" && page !== "settings"), | |
| 541 | ...(settings ?? member ? (["settings"] as const) : []), | |
| 542 | ]; | |
| 543 | const pill = (current: boolean) => | |
| 544 | `flex h-9 items-center gap-1.5 rounded-full px-3.5 text-sm transition-colors ${ | |
| 545 | current ? "bg-raised font-medium text-fg ring-1 ring-line-strong" : "text-muted active:bg-raised/60" | |
| 546 | }`; | |
| 547 | return ( | |
| 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. | 548 | <TabStrip label="Project" className={`-mx-4 gap-1 px-4 ${always ? "" : "md:hidden"}`}> |
| 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 bar | 549 | {pages.map((page) => { |
| 550 | const current = at === page; | |
| 551 | const link = page === "overview" ? { label: "Overview", path: "" } : PROJECT_PAGE_LINKS[page]; | |
| 552 | const count = page === "issues" ? counts?.issues : page === "pulls" ? counts?.pulls : undefined; | |
| 553 | return ( | |
| 554 | <Link | |
| 555 | key={page} | |
| 556 | to={link.path ? `${base}/${link.path}` : base} | |
| 557 | prefetch="intent" | |
| 558 | aria-current={current ? "page" : undefined} | |
| 559 | data-active={current || undefined} | |
| 560 | className={pill(current)} | |
| 561 | > | |
| 562 | {link.label} | |
| 563 | {count != null && count > 0 && <span className="rounded-full bg-line px-1.5 text-xs tabular-nums text-muted">{count}</span>} | |
| 564 | </Link> | |
| 565 | ); | |
| 566 | })} | |
| 567 | </TabStrip> | |
| 568 | ); | |
| 569 | } |
This file's history is long; its oldest lines are credited to the oldest commit read.