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. | 24 | import 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. | 25 | import { 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. | 26 | import { 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. */ | |
| 29 | export 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. | 31 | export 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 | ||
| 40 | const 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. */ |
| 43 | export 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`. */ |
| 55 | export 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`. */ | |
| 60 | export type PinnableApp = string; | |
| 61 | ||
| 62 | /** An app installed from the Marketplace. */ | |
| 63 | export 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. */ | |
| 85 | const SECTION_BY_CATEGORY: Partial<Record<Connector["category"], string>> = { ai: "models", monitoring: "alerts", issues: "trackers" }; | |
| 86 | ||
| 87 | function 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). */ | |
| 95 | const 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`. */ |
| 98 | export 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 | */ | |
| 107 | export 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. | 142 | export 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 | */ | |
| 151 | export 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. */ |
| 166 | export type InstalledAppData = Omit<InstalledApp, "path"> & { href: string }; | |
| 167 | ||
| 168 | export 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. | 174 | export 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. | 180 | const MAX_WORKSPACES = 20; |
| 181 | export 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 | */ | |
| 188 | export 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 | */ | |
| 197 | export 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`. */ |
| 202 | export 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. */ | |
| 214 | export 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. */ | |
| 219 | export 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. */ | |
| 228 | export 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. */ | |
| 236 | export 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. */ | |
| 242 | export 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. */ | |
| 247 | export const SIDEBAR_COOKIE = "g1t_sidebar"; | |
| 248 | ||
| 249 | /** Whether the sidebar is folded away, from the cookie's value. */ | |
| 250 | export 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. */ | |
| 255 | export function sidebarCookie(closed: boolean, secure: boolean): string { | |
| 256 | return `${SIDEBAR_COOKIE}=${closed ? "closed" : "open"}; Path=/; Max-Age=31536000; SameSite=Lax${secure ? "; Secure" : ""}`; | |
| 257 | } |