Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
466 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.

Integrations are a directory of connectors, for a workspace and for each person1import {
2 AlertTriangle,
3 BarChart3,
4 BookOpen,
5 Bot,
6 CalendarDays,
7 ChevronRight,
8 Cloud,
9 Code2,
10 FolderOpen,
11 Headset,
12 LayoutGrid,
13 ListChecks,
14 MessagesSquare,
15 Palette,
16 Search,
17 ShieldCheck,
18 Siren,
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.19 Webhook,
Integrations are a directory of connectors, for a workspace and for each person20} from "lucide-react";
21import { type ReactNode, useState } from "react";
22import { Link, useSearchParams } from "react-router";
23
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.24import { type Provider, PROVIDERS } from "@g1t/contracts";
Integrations are a directory of connectors, for a workspace and for each person25import { type ConnectorScope, type ConnectorView, connectorPath } from "@g1t/contracts/connectors";
26
The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens.27import { AI_PROVIDERS } from "../lib/ai-providers";
Integrations are a directory of connectors, for a workspace and for each person28import { CONTACT } from "../lib/legal";
29import {
30 type CategoryFilter,
31 type ConnectedState,
32 arrange,
33 askHref,
34 categoriesWith,
35 categoryCounts,
36 categoryFilter,
37 categoryTitle,
38 groupByCategory,
39 monogram,
40 tileColor,
41} from "../lib/connectors";
42import { cn } from "../lib/cn";
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.43import { integrationsSection } from "../lib/integration-sections";
Integrations are a directory of connectors, for a workspace and for each person44import { Badge } from "./ui/badge";
45import { Hint } from "./ui/hint";
46import { Input } from "./ui";
The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after.47import { Card } from "./ui/card";
Integrations are a directory of connectors, for a workspace and for each person48
49const CATEGORY_ICONS: Record<CategoryFilter, ReactNode> = {
50 all: <LayoutGrid size={15} />,
51 code: <Code2 size={15} />,
52 issues: <ListChecks size={15} />,
53 chat: <MessagesSquare size={15} />,
54 calendar: <CalendarDays size={15} />,
55 docs: <BookOpen size={15} />,
56 monitoring: <Siren size={15} />,
57 cloud: <Cloud size={15} />,
58 ai: <Bot size={15} />,
59 data: <BarChart3 size={15} />,
60 crm: <Headset size={15} />,
61 design: <Palette size={15} />,
62 security: <ShieldCheck size={15} />,
63 files: <FolderOpen size={15} />,
64};
65
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.66const PROVIDER_COLORS: Partial<Record<Provider, string>> = {
67 sentry: "#a78bfa",
68 datadog: "#a26ce6",
69 webhook: "#5eead4",
70 jira: "#5b8def",
71 linear: "#8b8ff0",
72};
73
The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens.74/** A provider's mark: its initials in its colour, or an icon for the generic ones. */
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.75export function ProviderMark({ provider, size = 32 }: { provider: Provider; size?: number }) {
The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens.76 const entry = AI_PROVIDERS[provider];
77 const color = entry?.color ?? PROVIDER_COLORS[provider] ?? "#a1a1aa";
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.78 const content =
79 provider === "webhook" ? (
80 <Webhook size={size * 0.5} />
81 ) : provider.endsWith("_endpoint") ? (
82 <Bot size={size * 0.5} />
83 ) : (
The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens.84 entry?.mark || PROVIDERS[provider].label[0]
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.85 );
86 return (
87 <span
88 aria-hidden="true"
89 className="inline-flex shrink-0 items-center justify-center font-semibold tracking-tight"
90 style={{
91 width: size,
92 height: size,
93 borderRadius: size * 0.28,
The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens.94 fontSize: size * (String(content).length > 1 ? 0.34 : 0.42),
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.95 background: `color-mix(in srgb, ${color} 16%, transparent)`,
The contracts no longer describe g1t's own agents, and workspaces can keep their AI providers. Every consumer had already stopped using them, so the TypeScript and Rust contracts drop the workspace-agent, ability, look, skill-library, context, runner-agent and mention modules, the chat and docs RPCs agents posted and read through, the folio agent methods, the agent_waiting notification, agent.asked and memory.changed events, AI credit, token and gateway billing types, model routes and sessions, team agents, agent tokens, plan, review, message and confidence types, command rules, cost caps and confidence rules. The agents:run and models:* scopes are no longer offered by any preset, OAuth default or permission form, but a stored token or grant that names them still reads; and the removed operations are gone from the scope table, so the API's check that every operation has exactly one scope is strict again. What stored data needs in order to parse (old run and credential kinds, an agent team lead, retired rules and webhook events, old guardrail and integration settings) still parses, and a run's token is still PrincipalKind::Agent, which now means a run's token and the @g1t rows written before. The customize, marketplace, skills and claude-plugin contracts stay only until the mobile app drops its Customize screens.96 // Toward the text colour, so the letters read on the tile in both themes.
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.97 color: `color-mix(in srgb, ${color} 60%, var(--color-fg))`,
98 boxShadow: `inset 0 0 0 1px color-mix(in srgb, ${color} 30%, transparent)`,
99 }}
100 >
101 {content}
102 </span>
103 );
104}
105
106/** Providers to choose from, as compact tiles that open their form. */
107export function ProviderTiles({
108 providers,
109 slug,
110 adding,
111 blurb,
112}: {
113 providers: Provider[];
114 slug: string;
115 adding: Provider | null;
116 blurb: (provider: Provider) => string;
117}) {
118 return (
119 <div className="grid gap-2 sm:grid-cols-2 lg:grid-cols-3">
120 {providers.map((provider) => (
121 <Link
122 key={provider}
123 to={`/${slug}/-/integrations/${integrationsSection(PROVIDERS[provider].kind)}?add=${provider}#add`}
124 preventScrollReset
125 className={`group flex items-center gap-3 rounded-xl border px-3 py-2.5 transition-colors ${
126 adding === provider ? "border-accent-dim bg-surface" : "border-line hover:border-line-strong hover:bg-surface"
127 }`}
128 >
129 <ProviderMark provider={provider} size={28} />
130 <span className="min-w-0 grow">
131 <span className="block truncate text-sm font-medium">{PROVIDERS[provider].label}</span>
132 <Hint label={blurb(provider)}>
133 <span className="block truncate text-xs text-muted">{blurb(provider)}</span>
134 </Hint>
135 </span>
136 <ChevronRight size={14} className="shrink-0 text-faint transition-transform group-hover:translate-x-0.5" />
137 </Link>
138 ))}
139 </div>
140 );
141}
142
Integrations are a directory of connectors, for a workspace and for each person143/** A connector's tile: its provider's mark, or its letters on a steady colour. No logos. */
144export function ConnectorMark({ view, size = 36 }: { view: Pick<ConnectorView, "id" | "name" | "provider">; size?: number }) {
145 if (view.provider) return <ProviderMark provider={view.provider} size={size} />;
146 const color = tileColor(view.id);
147 const letters = monogram(view.name);
148 return (
149 <span
150 aria-hidden="true"
151 className="inline-flex shrink-0 items-center justify-center font-semibold tracking-tight"
152 style={{
153 width: size,
154 height: size,
155 borderRadius: size * 0.28,
156 fontSize: size * (letters.length > 1 ? 0.34 : 0.42),
157 background: `color-mix(in srgb, ${color} 16%, transparent)`,
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.158 // Toward the text colour, so the letters read in both themes.
159 color: `color-mix(in srgb, ${color} 60%, var(--color-fg))`,
Integrations are a directory of connectors, for a workspace and for each person160 boxShadow: `inset 0 0 0 1px color-mix(in srgb, ${color} 30%, transparent)`,
161 }}
162 >
163 {letters}
164 </span>
165 );
166}
167
168export type ConnectorDirectoryProps = {
169 scope: ConnectorScope;
170 views: ConnectorView[];
171 /** What is connected here, by connector id. */
172 connected: Record<string, ConnectedState>;
173 /** The workspace's slug on its page; null on your own. */
174 workspace: string | null;
175 /** Whether the viewer can connect things here (an owner, or anyone for themselves). */
176 canManage: boolean;
177 /** Category navigation as a sidebar beside the cards (wide pages), or as chips above them. */
178 nav?: "sidebar" | "chips";
179};
180
181/**
182 * The integrations directory: categories to browse, a search, and every
183 * connector as a card, sorted into Connected, Available and Soon.
184 */
185export function ConnectorDirectory({ scope, views, connected, workspace, canManage, nav = "sidebar" }: ConnectorDirectoryProps) {
186 const [params, setParams] = useSearchParams();
187 const category = categoryFilter(params.get("category"));
188 const [query, setQuery] = useState("");
189 const counts = categoryCounts(views);
190 const categories = categoriesWith(views);
191 const found = arrange(views, { category, query, connected: new Set(Object.keys(connected)) });
192 const searching = query.trim().length > 0;
193 const grouped = category === "all" && !searching;
194 const empty = found.connected.length + found.available.length + found.soon.length === 0;
195
196 const choose = (next: CategoryFilter) => {
197 setParams(
198 (current) => {
199 const updated = new URLSearchParams(current);
200 if (next === "all") updated.delete("category");
201 else updated.set("category", next);
202 return updated;
203 },
204 { replace: true, preventScrollReset: true },
205 );
206 setQuery("");
207 };
208
209 const options: { id: CategoryFilter; title: string }[] = [{ id: "all", title: "All" }, ...categories];
210 const card = (view: ConnectorView) => (
211 <ConnectorCard
212 key={view.id}
213 view={view}
214 scope={scope}
215 state={connected[view.id] ?? null}
216 workspace={workspace}
217 canManage={canManage}
218 showCategory={category === "all" || searching}
219 />
220 );
221
222 return (
223 <div className={cn(nav === "sidebar" && "md:grid md:grid-cols-[12rem_minmax(0,1fr)] md:gap-8")}>
224 {/* Categories: a list beside the cards on a wide page, chips that scroll sideways otherwise. */}
225 <nav aria-label="Categories" className={cn(nav === "sidebar" && "md:sticky md:top-20 md:self-start")}>
226 <ul
227 className={cn(
228 "-mx-4 flex gap-1.5 overflow-x-auto px-4 pb-1 [scrollbar-width:none] sm:mx-0 sm:px-0",
229 nav === "sidebar" && "md:mx-0 md:flex-col md:gap-px md:overflow-visible md:px-0",
230 )}
231 >
232 {options.map((option) => {
233 const current = option.id === category;
234 return (
235 <li key={option.id} className="shrink-0">
236 <button
237 type="button"
238 onClick={() => choose(option.id)}
239 aria-current={current ? "page" : undefined}
240 className={cn(
241 "flex items-center gap-2 rounded-full border px-3 py-1.5 text-sm whitespace-nowrap transition-colors",
242 nav === "sidebar" && "md:w-full md:rounded-md md:border-transparent md:px-2.5",
243 current
244 ? "border-accent/40 bg-accent/10 text-fg md:bg-raised"
245 : "border-line text-muted hover:border-line-strong hover:text-fg md:hover:bg-surface",
246 )}
247 >
248 <span className={cn("hidden text-faint", nav === "sidebar" && "md:inline-flex", current && "text-accent")}>
249 {CATEGORY_ICONS[option.id]}
250 </span>
251 <span className="grow text-left">{option.title}</span>
252 <span className="text-xs text-faint tabular-nums">{counts[option.id]}</span>
253 </button>
254 </li>
255 );
256 })}
257 </ul>
258 </nav>
259
260 <div className="mt-5 min-w-0 md:mt-0">
261 <div className="relative">
262 <Search size={15} className="pointer-events-none absolute top-1/2 left-3 -translate-y-1/2 text-faint" />
263 <Input
264 type="search"
265 value={query}
266 onChange={(event) => setQuery(event.target.value)}
267 placeholder={category === "all" ? "Search integrations" : `Search all integrations, not just ${categoryTitle(category)}`}
268 aria-label="Search integrations"
269 className="pl-9"
270 />
271 </div>
272
273 {empty && (
The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after.274 <Card asChild tone="plain" className="mt-8 border-dashed px-4 py-8 text-center text-sm text-muted">
275 <p>
276 Nothing matches “{query.trim()}”.{" "}
277 <a
278 href={askHref({ connector: { id: "other", name: query.trim() || "something else" }, scope, workspace, address: CONTACT.support })}
279 className="text-accent hover:underline"
280 >
281 Ask for it
282 </a>
283 , and say what you would use it for.
284 </p>
285 </Card>
Integrations are a directory of connectors, for a workspace and for each person286 )}
287
288 {found.connected.length > 0 && (
289 <Group title="Connected" count={found.connected.length}>
290 <Cards>{found.connected.map(card)}</Cards>
291 </Group>
292 )}
293 {found.available.length > 0 && (
294 <Group
295 title="Available"
296 count={found.available.length}
297 about={
298 scope === "workspace"
299 ? canManage
300 ? "Ready to connect now."
301 : `Ready now. An owner of ${workspace} connects them.`
302 : "Ready to connect now."
303 }
304 >
305 <Cards>{found.available.map(card)}</Cards>
306 </Group>
307 )}
308 {found.soon.length > 0 && (
309 <Group title="Soon" count={found.soon.length} about="Being built. Ask for the ones you need, and they come sooner.">
310 {grouped ? (
311 <div className="space-y-6">
312 {groupByCategory(found.soon).map((group) => (
313 <div key={group.id}>
314 <p className="mb-2 flex items-center gap-2 text-xs font-medium tracking-wide text-faint uppercase">
315 {CATEGORY_ICONS[group.id]}
316 {group.title}
317 </p>
318 <Cards>{group.views.map((view) => <ConnectorCard key={view.id} view={view} scope={scope} state={null} workspace={workspace} canManage={canManage} showCategory={false} />)}</Cards>
319 </div>
320 ))}
321 </div>
322 ) : (
323 <Cards>{found.soon.map(card)}</Cards>
324 )}
325 </Group>
326 )}
327 </div>
328 </div>
329 );
330}
331
332function Group({ title, count, about, children }: { title: string; count: number; about?: string; children: ReactNode }) {
333 return (
334 <section className="mt-8">
335 <h2 className="flex items-baseline gap-2 text-sm font-medium">
336 {title}
337 <span className="text-xs font-normal text-faint tabular-nums">{count}</span>
338 </h2>
339 {about && <p className="mt-0.5 text-xs text-muted">{about}</p>}
340 <div className="mt-3">{children}</div>
341 </section>
342 );
343}
344
345function Cards({ children }: { children: ReactNode }) {
346 return <ul className="grid gap-3 sm:grid-cols-2">{children}</ul>;
347}
348
349function ConnectorCard({
350 view,
351 scope,
352 state,
353 workspace,
354 canManage,
355 showCategory,
356}: {
357 view: ConnectorView;
358 scope: ConnectorScope;
359 state: ConnectedState | null;
360 workspace: string | null;
361 canManage: boolean;
362 showCategory: boolean;
363}) {
364 const soon = view.status === "soon";
365 const setup = view.href ? connectorPath(view.href, workspace ?? "") : null;
366 const to = state?.manage ?? setup;
367 const also =
368 view.alsoIn === "personal"
369 ? "Each person can also connect their own, under their settings."
370 : view.alsoIn === "workspace"
371 ? "A workspace owner can also connect it for everyone."
372 : null;
373 return (
374 <li
375 className={cn(
376 "relative flex flex-col rounded-xl border p-4 transition-colors",
377 soon ? "border-line/70 bg-surface/40" : "border-line bg-surface",
378 !soon && to && "hover:border-line-strong",
379 )}
380 >
381 <div className="flex items-start gap-3">
382 <span className={cn(soon && "opacity-70 grayscale-[35%]")}>
383 <ConnectorMark view={view} />
384 </span>
385 <div className="min-w-0 grow">
386 <div className="flex items-center gap-2">
387 {!soon && to ? (
388 // The whole card opens it: the link stretches over the card.
389 <Link to={to} className="truncate text-sm font-medium after:absolute after:inset-0 after:rounded-xl focus-visible:outline-none">
390 {view.name}
391 </Link>
392 ) : (
393 <p className={cn("truncate text-sm font-medium", soon && "text-fg/80")}>{view.name}</p>
394 )}
395 {state ? (
396 state.problem ? (
397 <Hint label={state.problem}>
398 <Badge tone="warn" className="relative z-10" tabIndex={0}>
399 <AlertTriangle size={11} /> Needs attention
400 </Badge>
401 </Hint>
402 ) : (
403 <Badge tone="success">Connected</Badge>
404 )
405 ) : soon ? (
406 <Badge>Soon</Badge>
407 ) : null}
408 </div>
409 {showCategory && <p className="text-xs text-faint">{categoryTitle(view.category)}</p>}
410 </div>
411 </div>
412 <p className={cn("mt-2.5 text-sm", soon ? "text-muted/90" : "text-muted")}>{view.description}</p>
413 {state?.detail && <p className="mt-1.5 truncate text-xs text-faint">{state.detail}</p>}
414 {view.capabilities.length > 0 && (
415 <ul className="mt-3 flex flex-wrap gap-1.5">
416 {view.capabilities.map((capability) => (
417 <li key={capability} className="rounded-md bg-raised px-1.5 py-0.5 text-[0.6875rem] text-muted">
418 {capability}
419 </li>
420 ))}
421 </ul>
422 )}
423 <div className="mt-auto flex items-end justify-between gap-3 pt-3">
424 <p className="text-[0.6875rem] text-faint">{also}</p>
425 {soon ? (
426 <a
427 href={askHref({ connector: view, scope, workspace, address: CONTACT.support })}
428 className="relative z-10 shrink-0 rounded-md border border-line px-2.5 py-1 text-xs font-medium text-fg/80 hover:border-line-strong hover:bg-raised hover:text-fg"
429 >
430 Ask for this
431 </a>
432 ) : to ? (
433 <span className="flex shrink-0 items-center gap-0.5 text-xs font-medium text-accent">
434 {state ? (canManage ? "Manage" : "View") : canManage ? "Set up" : "View"}
435 <ChevronRight size={13} />
436 </span>
437 ) : null}
438 </div>
439 </li>
440 );
441}
442
443/** A short line saying who a page's connections are for, and where the other kind lives. */
444export function ScopeNote({ scope, workspace }: { scope: ConnectorScope; workspace: string | null }) {
445 return (
The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after.446 <Card asChild tone="plain" className="mb-6 bg-surface/60 px-4 py-3 text-sm text-muted">
447 <p>
448 {scope === "workspace" ? (
449 <>
The site no longer has g1t's own agents. g1t is where the coding agents you already use do their work, so the Agents mode is gone from the rail, with every page under /<workspace>/-/agents: personas, faces, skills, abilities, routines, computers, sessions and agent spend. Customize (skills, connectors, plugins, marketplaces), the context hub, model providers, the AI Gateway page, the Spend page and pill, Ask g1t, and the repository's Agents, Plans and agent settings pages go too, and their addresses now answer 404.450 Connected here, a tool works <span className="text-fg">for everyone in {workspace}</span>.
The site is built from its shared components: one Button with its variants and sizes, one Badge, Card for every panel, Alert for notices, Kbd for keys, Table for the security tables, and Avatar, Command, Progress, ScrollArea, ToggleGroup, Collapsible and Accordion in the kit; raw buttons, hand-drawn cards and pills and per-file button class lists are gone, outline buttons stand the same height as filled ones, and the phone's Ask g1t loses its tile. Every page was compared pixel by pixel before and after.451 Your own accounts, such as your calendar, are under{" "}
452 <Link to="/settings/integrations" className="text-accent hover:underline">
453 your integrations
454 </Link>
455 .
456 </>
457 ) : (
458 <>
459 Connected here, a tool works <span className="text-fg">just for you, in every workspace</span> you belong to. Tools
460 the whole team shares are connected by a workspace owner, under the workspace's Integrations.
461 </>
462 )}
463 </p>
464 </Card>
Integrations are a directory of connectors, for a workspace and for each person465 );
466}