Skip to content
257 linesCodeBlameRaw

Pick any line to see why it is the way it is: the commit, the pull request and issue it came from, and what the agent was thinking.

g1t is described as the product it is in alpha, and its shell takes the new shape: the README, the docs home and docs/PLAN.md label every feature Live, Preview or Coming; a floating dock with the g1t mark, Today, Chat, Notifications, Agents, Code, Artifacts, your pinned apps and the Apps launcher, with People, Workspace and the account menu (now holding help) at its foot; the workspace's logo and switcher heading a flat sidebar that folds away with Ctrl B, or leading the header's breadcrumbs where there is none; the page in a rounded panel with a full-width header; Today as the front page, with first-time acceptance of agents' pull requests, what needs you and what's at stake, spend today and one item to start with, every number from a service; Notifications in place of the Inbox at /notifications; a bottom bar and More sheet on phones; and sign-in, sign-up, two-factor, reset, invites and choosing a workspace on standalone pages with no app around them.1/**
The Marketplace is where a workspace finds what to add, reached from Apps: the agent catalog of roles with who already does each job and Add to workspace; every integration with whether it's connected; g1t's own extensions, listed as Soon; and requests, where members ask and owners add or turn down, each told by a notification. Underneath, an extension is a manifest (publisher and tier, source and version from a tag, scopes, the domains its data goes to, its page on g1tusercontent.com, no price) and an install pins its version with an off switch, a budget and the audit log. Apps lists only what was added from the Marketplace, with pins; g1t's own pages stay in their sidebars, and AI Gateway joins Workspace's. The marketplace guide says how.2 * The dock's built-in apps, the apps a workspace installed from the
3 * Marketplace, and the ones each person pins to their dock.
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.4 *
5 * Built-in apps (Today, Chat, Notifications, Agents, Code, Artifacts,
The Marketplace is where a workspace finds what to add, reached from Apps: the agent catalog of roles with who already does each job and Add to workspace; every integration with whether it's connected; g1t's own extensions, listed as Soon; and requests, where members ask and owners add or turn down, each told by a notification. Underneath, an extension is a manifest (publisher and tier, source and version from a tag, scopes, the domains its data goes to, its page on g1tusercontent.com, no price) and an install pins its version with an off switch, a budget and the audit log. Apps lists only what was added from the Marketplace, with pins; g1t's own pages stay in their sidebars, and AI Gateway joins Workspace's. The marketplace guide says how.6 * People and Workspace) are always in the dock; their own pages (Projects,
7 * Usage, Teams and the rest) are in their sidebars. Apps are what the
8 * workspace added from the Marketplace: today, the integrations it
9 * connected, each opening its page. Each person pins the ones they want.
10 *
11 * An installed app's key says what it is: `int-<connector>` for an
12 * integration (@g1t/contracts/connectors), and later `ext-<extension>`.
13 * A key that names nothing installable (a built-in page pinned before
14 * Apps were the Marketplace's) is dropped wherever pins are read.
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 *
The apps you pin to your dock are kept with your account, per workspace and in your order, so the dock is the same on every device: identity keeps them in dock_pins and answers dock_pins and set_dock_pins, the dock reads them with the rest of the page, pins kept only on this device carry over with your first change, this device's copy still draws the dock when identity can't be reached, a pin that can't be saved says so, and they go when you or the workspace do; the workspaces guide says how.16 * Pins are each person's own, per workspace, in the order they set, and
17 * kept with their account by the identity service (`dock_pins`), so the
18 * dock is the same on every device (lib/dock.server.ts). A cookie
19 * (`g1t_dock`) keeps a copy on each device: the dock still draws from it
20 * when identity cannot be reached, and pins from before they were kept
21 * with the account carry over from it until the first change.
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.22 * No Workers or React imports, so it can be tested under Node.
23 */
Every Marketplace listing says who builds it and whether you can add it: a tier (Official, Verified, Community, Internal) with what it means, and an availability (Available, Connected, Soon, or Not available here with why) that never looks like a button that doesn't work; Extensions and Integrations are grouped and filtered by both, each integration has a page with what it lets agents do today and what's Soon, and the marketplace guide says how.24import type { ExtensionInstall, ListingTier } from "@g1t/contracts";
The Marketplace is where a workspace finds what to add, reached from Apps: the agent catalog of roles with who already does each job and Add to workspace; every integration with whether it's connected; g1t's own extensions, listed as Soon; and requests, where members ask and owners add or turn down, each told by a notification. Underneath, an extension is a manifest (publisher and tier, source and version from a tag, scopes, the domains its data goes to, its page on g1tusercontent.com, no price) and an install pins its version with an off switch, a budget and the audit log. Apps lists only what was added from the Marketplace, with pins; g1t's own pages stay in their sidebars, and AI Gateway joins Workspace's. The marketplace guide says how.25import { CONNECTORS, type Connector, connectorPath } from "@g1t/contracts/connectors";
Every Marketplace listing says who builds it and whether you can add it: a tier (Official, Verified, Community, Internal) with what it means, and an availability (Available, Connected, Soon, or Not available here with why) that never looks like a button that doesn't work; Extensions and Integrations are grouped and filtered by both, each integration has a page with what it lets agents do today and what's Soon, and the marketplace guide says how.26import { CONNECTOR_PUBLISHER, extensionById } from "@g1t/contracts/marketplace";
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.27
28/** The apps that are always in the dock. */
29export type BuiltinApp = "today" | "chat" | "notifications" | "agents" | "code" | "artifacts" | "people" | "workspace";
30
The Marketplace is where a workspace finds what to add, reached from Apps: the agent catalog of roles with who already does each job and Add to workspace; every integration with whether it's connected; g1t's own extensions, listed as Soon; and requests, where members ask and owners add or turn down, each told by a notification. Underneath, an extension is a manifest (publisher and tier, source and version from a tag, scopes, the domains its data goes to, its page on g1tusercontent.com, no price) and an install pins its version with an off switch, a budget and the audit log. Apps lists only what was added from the Marketplace, with pins; g1t's own pages stay in their sidebars, and AI Gateway joins Workspace's. The marketplace guide says how.31export type BuiltinInfo = {
32 key: BuiltinApp;
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.33 name: string;
34 /** Its page in the workspace `slug`. */
35 path: (slug: string) => string;
36 /** Part of Code: only for members with Code access. */
37 code?: boolean;
38};
39
40const under = (page: string) => (slug: string) => `/${slug}/-/${page}`;
41
The Marketplace is where a workspace finds what to add, reached from Apps: the agent catalog of roles with who already does each job and Add to workspace; every integration with whether it's connected; g1t's own extensions, listed as Soon; and requests, where members ask and owners add or turn down, each told by a notification. Underneath, an extension is a manifest (publisher and tier, source and version from a tag, scopes, the domains its data goes to, its page on g1tusercontent.com, no price) and an install pins its version with an off switch, a budget and the audit log. Apps lists only what was added from the Marketplace, with pins; g1t's own pages stay in their sidebars, and AI Gateway joins Workspace's. The marketplace guide says how.42/** The dock's built-in apps, in its order. */
43export const BUILTINS: BuiltinInfo[] = [
44 { key: "today", name: "Today", path: under("today") },
45 { key: "chat", name: "Chat", path: under("chat") },
46 { key: "notifications", name: "Notifications", path: () => "/notifications" },
47 { key: "agents", name: "Agents", path: under("agents") },
48 { key: "code", name: "Code", path: under("overview"), code: true },
49 { key: "artifacts", name: "Artifacts", path: under("artifacts") },
50 { key: "people", name: "People", path: under("people") },
51 { key: "workspace", name: "Workspace", path: under("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.52];
53
The Marketplace is where a workspace finds what to add, reached from Apps: the agent catalog of roles with who already does each job and Add to workspace; every integration with whether it's connected; g1t's own extensions, listed as Soon; and requests, where members ask and owners add or turn down, each told by a notification. Underneath, an extension is a manifest (publisher and tier, source and version from a tag, scopes, the domains its data goes to, its page on g1tusercontent.com, no price) and an install pins its version with an off switch, a budget and the audit log. Apps lists only what was added from the Marketplace, with pins; g1t's own pages stay in their sidebars, and AI Gateway joins Workspace's. The marketplace guide says how.54/** The built-in app `key`. */
55export function appOf(key: BuiltinApp): BuiltinInfo {
56 return BUILTINS.find((app) => app.key === key)!;
57}
58
59/** An installed app's key, as pins and forms carry it: `int-sentry`. */
60export type PinnableApp = string;
61
62/** An app installed from the Marketplace. */
63export type InstalledApp = {
64 key: PinnableApp;
65 name: string;
66 /** What it is, in a few words. */
67 about: string;
68 /** The Marketplace listing it was installed from: `integration:sentry`. */
69 listing: string;
Every Marketplace listing says who builds it and whether you can add it: a tier (Official, Verified, Community, Internal) with what it means, and an availability (Available, Connected, Soon, or Not available here with why) that never looks like a button that doesn't work; Extensions and Integrations are grouped and filtered by both, each integration has a page with what it lets agents do today and what's Soon, and the marketplace guide says how.70 /** Who stands behind it, as its Marketplace listing says: its publisher and tier. */
71 publisher: string;
72 tier: ListingTier;
The Marketplace is where a workspace finds what to add, reached from Apps: the agent catalog of roles with who already does each job and Add to workspace; every integration with whether it's connected; g1t's own extensions, listed as Soon; and requests, where members ask and owners add or turn down, each told by a notification. Underneath, an extension is a manifest (publisher and tier, source and version from a tag, scopes, the domains its data goes to, its page on g1tusercontent.com, no price) and an install pins its version with an off switch, a budget and the audit log. Apps lists only what was added from the Marketplace, with pins; g1t's own pages stay in their sidebars, and AI Gateway joins Workspace's. The marketplace guide says how.73 /** The connector behind it, for its mark. */
74 connector: Pick<Connector, "id" | "name" | "provider">;
75 /** Its page in the workspace `slug`. */
76 path: (slug: string) => string;
77 /**
78 * Whether the person can open it. Every member can open an integration's
79 * page; an app limited to some people shows Request access to the rest.
80 */
81 usable: boolean;
82};
83
84/** Where an integration is looked after: its setup page, by kind, or its own page. */
85const SECTION_BY_CATEGORY: Partial<Record<Connector["category"], string>> = { ai: "models", monitoring: "alerts", issues: "trackers" };
86
87function integrationPath(connector: Connector): (slug: string) => string {
88 const section = connector.provider ? SECTION_BY_CATEGORY[connector.category] : undefined;
89 if (section) return under(`integrations/${section}`);
90 const href = connector.href?.workspace;
91 return href ? (slug) => connectorPath(href, slug) : under("integrations");
92}
93
94/** Identity keeps keys of at most this many characters (MAX_DOCK_APP_KEY). */
95const MAX_KEY = 32;
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.96
The Marketplace is where a workspace finds what to add, reached from Apps: the agent catalog of roles with who already does each job and Add to workspace; every integration with whether it's connected; g1t's own extensions, listed as Soon; and requests, where members ask and owners add or turn down, each told by a notification. Underneath, an extension is a manifest (publisher and tier, source and version from a tag, scopes, the domains its data goes to, its page on g1tusercontent.com, no price) and an install pins its version with an off switch, a budget and the audit log. Apps lists only what was added from the Marketplace, with pins; g1t's own pages stay in their sidebars, and AI Gateway joins Workspace's. The marketplace guide says how.97/** The installed-app key for an integration: `int-sentry`. */
98export function integrationAppKey(connectorId: string): PinnableApp {
99 return `int-${connectorId}`;
100}
101
102/**
103 * The app a key names, from the catalog alone (no service is asked), or
104 * null when it names nothing that can be installed: a retired key, a
105 * built-in page, or a connector that is not available for a workspace.
106 */
107export function installedAppOf(key: string): InstalledApp | null {
108 if (key.length > MAX_KEY) return null;
109 if (/^ext-[a-z][a-z0-9-]*$/.test(key)) {
110 const extension = extensionById(key.slice(4));
111 if (!extension || extension.status !== "available") return null;
112 return {
113 key,
114 name: extension.name,
115 about: extension.tagline,
116 listing: `extension:${extension.id}`,
Every Marketplace listing says who builds it and whether you can add it: a tier (Official, Verified, Community, Internal) with what it means, and an availability (Available, Connected, Soon, or Not available here with why) that never looks like a button that doesn't work; Extensions and Integrations are grouped and filtered by both, each integration has a page with what it lets agents do today and what's Soon, and the marketplace guide says how.117 publisher: extension.publisher.name,
118 tier: extension.publisher.tier,
The Marketplace is where a workspace finds what to add, reached from Apps: the agent catalog of roles with who already does each job and Add to workspace; every integration with whether it's connected; g1t's own extensions, listed as Soon; and requests, where members ask and owners add or turn down, each told by a notification. Underneath, an extension is a manifest (publisher and tier, source and version from a tag, scopes, the domains its data goes to, its page on g1tusercontent.com, no price) and an install pins its version with an off switch, a budget and the audit log. Apps lists only what was added from the Marketplace, with pins; g1t's own pages stay in their sidebars, and AI Gateway joins Workspace's. The marketplace guide says how.119 connector: { id: extension.id, name: extension.name, provider: undefined },
120 // Its own page, in a sandboxed frame, is the extension host's to serve; until then, its listing.
121 path: (slug) => `/${slug}/-/marketplace/extensions/${extension.id}`,
122 usable: true,
123 };
124 }
125 if (!/^int-[a-z0-9][a-z0-9-]*$/.test(key)) return null;
126 const connector = CONNECTORS.find((c) => c.id === key.slice(4));
127 if (!connector || connector.status !== "available" || !connector.scopes.includes("workspace")) return null;
128 return {
129 key,
130 name: connector.name,
131 about: connector.description,
132 listing: `integration:${connector.id}`,
Every Marketplace listing says who builds it and whether you can add it: a tier (Official, Verified, Community, Internal) with what it means, and an availability (Available, Connected, Soon, or Not available here with why) that never looks like a button that doesn't work; Extensions and Integrations are grouped and filtered by both, each integration has a page with what it lets agents do today and what's Soon, and the marketplace guide says how.133 publisher: CONNECTOR_PUBLISHER.name,
134 tier: CONNECTOR_PUBLISHER.tier,
The Marketplace is where a workspace finds what to add, reached from Apps: the agent catalog of roles with who already does each job and Add to workspace; every integration with whether it's connected; g1t's own extensions, listed as Soon; and requests, where members ask and owners add or turn down, each told by a notification. Underneath, an extension is a manifest (publisher and tier, source and version from a tag, scopes, the domains its data goes to, its page on g1tusercontent.com, no price) and an install pins its version with an off switch, a budget and the audit log. Apps lists only what was added from the Marketplace, with pins; g1t's own pages stay in their sidebars, and AI Gateway joins Workspace's. The marketplace guide says how.135 connector: { id: connector.id, name: connector.name, provider: connector.provider },
136 path: integrationPath(connector),
137 usable: true,
138 };
139}
140
141/** Whether `key` names an app that can be pinned. */
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.142export function isPinnable(key: string): key is PinnableApp {
The Marketplace is where a workspace finds what to add, reached from Apps: the agent catalog of roles with who already does each job and Add to workspace; every integration with whether it's connected; g1t's own extensions, listed as Soon; and requests, where members ask and owners add or turn down, each told by a notification. Underneath, an extension is a manifest (publisher and tier, source and version from a tag, scopes, the domains its data goes to, its page on g1tusercontent.com, no price) and an install pins its version with an off switch, a budget and the audit log. Apps lists only what was added from the Marketplace, with pins; g1t's own pages stay in their sidebars, and AI Gateway joins Workspace's. The marketplace guide says how.143 return installedAppOf(key) != null;
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.144}
145
The Marketplace is where a workspace finds what to add, reached from Apps: the agent catalog of roles with who already does each job and Add to workspace; every integration with whether it's connected; g1t's own extensions, listed as Soon; and requests, where members ask and owners add or turn down, each told by a notification. Underneath, an extension is a manifest (publisher and tier, source and version from a tag, scopes, the domains its data goes to, its page on g1tusercontent.com, no price) and an install pins its version with an off switch, a budget and the audit log. Apps lists only what was added from the Marketplace, with pins; g1t's own pages stay in their sidebars, and AI Gateway joins Workspace's. The marketplace guide says how.146/**
147 * The apps the workspace installed, from what it has connected (by
148 * connector id, lib/connected.server.ts), in catalog order. `manage`, when
149 * a connection has one, is where its app opens.
150 */
151export function installedApps(
152 connected: Record<string, { manage: string | null }>,
153 installs: readonly Pick<ExtensionInstall, "listing" | "enabled">[] = [],
154): InstalledApp[] {
155 const integrations = CONNECTORS.filter((connector) => connected[connector.id]).map((connector) => {
156 const app = installedAppOf(integrationAppKey(connector.id));
157 const manage = connected[connector.id]?.manage;
158 return app && manage ? { ...app, path: () => manage } : app;
159 });
160 // Extensions switched off (the kill switch) aren't apps until they're on again.
161 const extensions = installs.filter((install) => install.enabled).map((install) => installedAppOf(`ext-${install.listing.replace(/^extension:/, "")}`));
162 return [...extensions, ...integrations].filter((app): app is InstalledApp => app != null);
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.163}
164
The Marketplace is where a workspace finds what to add, reached from Apps: the agent catalog of roles with who already does each job and Add to workspace; every integration with whether it's connected; g1t's own extensions, listed as Soon; and requests, where members ask and owners add or turn down, each told by a notification. Underneath, an extension is a manifest (publisher and tier, source and version from a tag, scopes, the domains its data goes to, its page on g1tusercontent.com, no price) and an install pins its version with an off switch, a budget and the audit log. Apps lists only what was added from the Marketplace, with pins; g1t's own pages stay in their sidebars, and AI Gateway joins Workspace's. The marketplace guide says how.165/** An app as a loader sends it: everything but its path, which is worked out for the workspace. */
166export type InstalledAppData = Omit<InstalledApp, "path"> & { href: string };
167
168export function appData(app: InstalledApp, slug: string): InstalledAppData {
169 const { path, ...rest } = app;
170 return { ...rest, href: path(slug) };
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.171}
172
The apps you pin to your dock are kept with your account, per workspace and in your order, so the dock is the same on every device: identity keeps them in dock_pins and answers dock_pins and set_dock_pins, the dock reads them with the rest of the page, pins kept only on this device carry over with your first change, this device's copy still draws the dock when identity can't be reached, a pin that can't be saved says so, and they go when you or the workspace do; the workspaces guide says how.173/** The cookie that keeps this device's copy of each person's pins, per 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.174export const DOCK_COOKIE = "g1t_dock";
175
The apps you pin to your dock are kept with your account, per workspace and in your order, so the dock is the same on every device: identity keeps them in dock_pins and answers dock_pins and set_dock_pins, the dock reads them with the rest of the page, pins kept only on this device carry over with your first change, this device's copy still draws the dock when identity can't be reached, a pin that can't be saved says so, and they go when you or the workspace do; the workspaces guide says how.176/**
177 * Most workspaces the cookie remembers pins for, and most pins in each.
178 * Identity keeps up to 24 (`MAX_DOCK_PINS`); this stays under it.
179 */
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.180const MAX_WORKSPACES = 20;
181export const MAX_APP_PINS = 12;
182
The apps you pin to your dock are kept with your account, per workspace and in your order, so the dock is the same on every device: identity keeps them in dock_pins and answers dock_pins and set_dock_pins, the dock reads them with the rest of the page, pins kept only on this device carry over with your first change, this device's copy still draws the dock when identity can't be reached, a pin that can't be saved says so, and they go when you or the workspace do; the workspaces guide says how.183/**
184 * Pins as identity keeps them, read against the apps there are: keys that
185 * are not an app people pin (one since retired, say) are dropped, repeats
186 * too, in the order they were set.
187 */
188export function pinsFrom(keys: readonly string[]): PinnableApp[] {
189 return [...new Set(keys.filter(isPinnable))].slice(0, MAX_APP_PINS);
190}
191
192/**
193 * The pins to draw in the workspace `slug`: the account's when identity
194 * has them (`stored`), else this device's cookie, for pins never saved to
195 * the account or when identity could not be asked.
196 */
197export function pinsToShow(stored: readonly string[] | null | undefined, cookie: string | null | undefined, slug: string): PinnableApp[] {
198 return stored ? pinsFrom(stored) : pinsIn(cookie, slug);
199}
200
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.201/** Every workspace's pins, from the cookie's value: `acme:projects.usage,beta:teams`. */
202export function readDock(value: string | null | undefined): Record<string, PinnableApp[]> {
203 const dock: Record<string, PinnableApp[]> = {};
204 for (const entry of (value ?? "").split(",")) {
205 const [slug, keys] = entry.split(":");
206 if (!slug || !/^[a-z0-9-]+$/.test(slug) || keys === undefined) continue;
207 const pins = [...new Set(keys.split(".").filter(isPinnable))].slice(0, MAX_APP_PINS);
208 dock[slug] = pins;
209 }
210 return dock;
211}
212
213/** The pins in the workspace `slug`, in the order they were pinned. */
214export function pinsIn(value: string | null | undefined, slug: string): PinnableApp[] {
215 return readDock(value)[slug.toLowerCase()] ?? [];
216}
217
218/** The cookie's value with the workspace `slug`'s pins set to `pins`: the latest workspace first. */
219export function writeDock(value: string | null | undefined, slug: string, pins: PinnableApp[]): string {
220 const dock = readDock(value);
221 const key = slug.toLowerCase();
222 delete dock[key];
223 const entries = [[key, [...new Set(pins.filter(isPinnable))].slice(0, MAX_APP_PINS)] as const, ...Object.entries(dock)].slice(0, MAX_WORKSPACES);
224 return entries.map(([name, keys]) => `${name}:${keys.join(".")}`).join(",");
225}
226
227/** One change to someone's pins, as the pin buttons post it; null when it asks for nothing that makes sense. */
228export function appPinFromForm(form: FormData): { app: PinnableApp; pinned: boolean } | null {
229 const app = String(form.get("app") ?? "");
230 const intent = form.get("intent");
231 if (!isPinnable(app) || (intent !== "pin" && intent !== "unpin")) return null;
232 return { app, pinned: intent === "pin" };
233}
234
235/** The pins after pinning or unpinning `app`: a new pin goes at the end. */
236export function withPin(pins: PinnableApp[], app: PinnableApp, pinned: boolean): PinnableApp[] {
237 const rest = pins.filter((key) => key !== app);
238 return pinned ? [...rest, app] : rest;
239}
240
241/** The Set-Cookie header that keeps the dock, for a year. */
242export function dockCookie(value: string, secure: boolean): string {
243 return `${DOCK_COOKIE}=${encodeURIComponent(value)}; Path=/; Max-Age=31536000; SameSite=Lax${secure ? "; Secure" : ""}`;
244}
245
246/** The cookie that remembers whether the in-context sidebar is folded away on a computer. */
247export const SIDEBAR_COOKIE = "g1t_sidebar";
248
249/** Whether the sidebar is folded away, from the cookie's value. */
250export function sidebarClosed(value: string | null | undefined): boolean {
251 return value === "closed";
252}
253
254/** The cookie, as the page sets it when the sidebar is folded away or brought back. */
255export function sidebarCookie(closed: boolean, secure: boolean): string {
256 return `${SIDEBAR_COOKIE}=${closed ? "closed" : "open"}; Path=/; Max-Age=31536000; SameSite=Lax${secure ? "; Secure" : ""}`;
257}