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

Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put1/**
2 * Which frame a page is drawn in, and what the sidebar offers someone who
3 * is not signed in.
4 *
Chat controls, public profiles, shadcn selects, and no Docs tab in a project5 * Someone signed in always gets the app's frame: the rail and its modes.
6 * A visitor gets it on projects, workspaces and not-found pages, where the
7 * sidebar is the project's own menu. g1t's public pages that belong to no
8 * workspace (a person's profile, Explore, Search) are drawn for a visitor
9 * in the public frame instead: the top bar with the mark, search and
10 * signing in, the page at full width, and the footer. So do the front page
11 * and the pages about signing in, paying and trust.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put12 */
13
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look14import type { Abilities, Capability } from "@g1t/contracts";
15
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put16/** Pages a visitor sees in the marketing frame. */
17const MARKETING = new Set([
18 "/",
19 "/pricing",
20 "/login",
21 "/register",
22 "/logout",
23 "/verify",
Merge email confirmation gate: a code and a link, nothing until confirmed (identity 0036)24 "/confirm-email",
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put25 "/forgot",
26 "/reset",
27 "/device",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look28 // Who makes g1t, and the promises it keeps: the policies, security,
29 // support and status.
30 "/policies",
31 "/security",
32 "/support",
33 "/status",
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put34 // These send a visitor to sign in; the frame matters only for a moment.
35 "/new",
36 "/settings",
37 "/workspaces/new",
38]);
39
Chat controls, public profiles, shadcn selects, and no Docs tab in a project40/** g1t's public pages that are no workspace's: a visitor reads them in the public frame. */
41const PUBLIC_PAGES = new Set(["/explore", "/search"]);
42
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put43/** Whether the page is drawn in the app's sidebar frame. */
44export function usesAppShell(pathname: string, signedIn: boolean): boolean {
45 if (signedIn) return true;
46 const path = pathname.length > 1 ? pathname.replace(/\/+$/, "") : pathname;
Chat controls, public profiles, shadcn selects, and no Docs tab in a project47 if (MARKETING.has(path) || PUBLIC_PAGES.has(path)) return false;
48 // A person's profile: theirs, not any workspace's.
49 if (/^\/u\/[^/]+$/.test(path)) return false;
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put50 if (path === "/oauth" || path.startsWith("/oauth/")) return false;
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look51 // An invite link is the front door: the marketing frame, like /register.
52 if (path.startsWith("/invite/")) return false;
53 if (path.startsWith("/policies/")) return false;
status.g1t.sh with incident management, invites that land you in the workspace, settings as pages, usage without quotas54 // Your settings pages, like /settings: a visitor is sent to sign in.
55 if (path.startsWith("/settings/")) return false;
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put56 return true;
57}
58
59export type SidebarItem = { label: string; to: string };
60
61/** The visitor's sidebar menu: where to browse from. */
62export const VISITOR_LINKS: SidebarItem[] = [
63 { label: "Explore", to: "/explore" },
64 { label: "Search", to: "/search" },
65];
66
67/** The project pages the sidebar lists, by key, for a member or not. */
68export type ProjectPage =
69 | "code"
70 | "issues"
71 | "pulls"
72 | "agents"
73 | "actions"
74 | "deployments"
75 | "observability"
76 | "security"
77 | "insights"
78 | "settings";
79
80/**
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look81 * What a page needs beyond a role on the repository, once what the viewer
82 * may do is known: Security's findings are for people who can push.
83 */
84export const PAGE_NEEDS: Partial<Record<ProjectPage, Capability>> = { security: "push" };
85
86/**
87 * A project's menu, in order. Security and Settings are for people with a
88 * role on its repository, and a page in `PAGE_NEEDS` only for those whose
89 * role has its capability (`can`, once the repository's page has loaded);
90 * everyone who can see the project sees the rest.
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put91 */
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look92export function projectPages(member: boolean, can?: Partial<Abilities>): ProjectPage[] {
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put93 const pages: (ProjectPage | false)[] = [
94 "code",
95 "issues",
96 "pulls",
97 "agents",
98 "actions",
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look99 // A public repository's deployments and build logs are for anyone, as its code is.
100 "deployments",
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put101 "observability",
102 member && "security",
103 "insights",
104 member && "settings",
105 ];
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look106 return pages.filter(
107 (page): page is ProjectPage => page !== false && (!can || !PAGE_NEEDS[page] || Boolean(can[PAGE_NEEDS[page]!])),
108 );
Team plan, an open-source pool, monthly trials and honest metering; the sidebar for everyone; a workspace that stays put109}
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 bar110
111/**
112 * A project's pages as the phone's strip of tabs under its name names them
113 * (routes/repo/layout.tsx): the sidebar's words, and the paths under the
114 * project at which each is the current one.
115 */
116export const PROJECT_PAGE_LINKS: Record<ProjectPage, { label: string; path: string; also: string[]; soon?: boolean }> = {
117 code: { label: "Code", path: "code", also: ["tree", "blob", "commits", "commit", "branches", "tags", "releases", "compare"] },
118 issues: { label: "Issues", path: "issues", also: ["plans", "milestones", "labels"] },
119 pulls: { label: "Pull requests", path: "pulls", also: ["pull", "queue"] },
120 agents: { label: "Agents", path: "agents", also: ["sessions", "memory"] },
121 actions: { label: "Workflows", path: "actions", also: [] },
122 deployments: { label: "Deployments", path: "deployments", also: [] },
123 observability: { label: "Observability", path: "soon/logs", also: [], soon: true },
124 security: { label: "Security", path: "security", also: [] },
125 insights: { label: "Insights", path: "contributors", also: ["activity", "stargazers"] },
126 settings: { label: "Settings", path: "settings", also: [] },
127};
128
129/**
130 * Which of a project's pages `rest` (the path under the project, no
131 * leading slash) is on: "overview" for the project itself. `soon` says
132 * which page a roadmap page (`soon/<key>`) sits under.
133 */
134export function projectPageAt(rest: string, soon: Record<string, ProjectPage> = {}): ProjectPage | "overview" | null {
135 const path = rest.replace(/^\/+|\/+$/g, "");
136 if (path === "") return "overview";
137 const soonKey = /^soon\/([^/]+)/.exec(path)?.[1];
138 if (soonKey && soon[soonKey]) return soon[soonKey]!;
139 for (const [page, link] of Object.entries(PROJECT_PAGE_LINKS) as [ProjectPage, (typeof PROJECT_PAGE_LINKS)[ProjectPage]][]) {
140 if ([link.path, ...link.also].some((prefix) => path === prefix || path.startsWith(`${prefix}/`))) return page;
141 }
142 return null;
143}

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