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.

97 linesCodeBlameRaw
1/**
2 * Extensions in the Marketplace: every listed extension, from its
3 * manifest (@g1t/contracts marketplace.ts), by who builds it (Official,
4 * Verified, Community, Internal), each tier a section even while it has
5 * nothing, so it is plain what is whose. Filters by tier and availability
6 * live in the address. Then how extensions are shared, and the legend.
7 */
8import { useSearchParams } from "react-router";
9
10import type { ListingTier } from "@g1t/contracts";
11import { FIRST_PARTY_EXTENSIONS } from "@g1t/contracts/marketplace";
12
13import { ExtensionCard, ListingFilterBar, ListingLegend, SectionHead, TierBadge, TierEmpty, filterCounts } from "../../../components/marketplace";
14import { type ExtensionListing, extensionListings, isConnectedSystem, passes, readFilters, tiersShown } from "../../../lib/marketplace";
15import { useMarketplace } from "./layout";
16
17/** Each tier's section heading on the Extensions tab. */
18const HEADINGS: Record<ListingTier, string> = {
19 official: "From g1t",
20 verified: "From verified publishers",
21 community: "From the community",
22 internal: "Built in this workspace",
23};
24
25export default function MarketplaceExtensions() {
26 const { slug, owner, username, requests, installs } = useMarketplace();
27 const [params] = useSearchParams();
28 const filters = readFilters(params);
29 // A search or an availability filter can empty a tier; the tier filter alone only picks one.
30 const filtered = filters.availability !== "all";
31 const listings = extensionListings(FIRST_PARTY_EXTENSIONS, installs, requests?.requests ?? [], username);
32 const shown = listings.filter((listing) => passes(listing, filters));
33 const grid = (items: ExtensionListing[]) => (
34 <div className="grid gap-3 sm:grid-cols-2 lg:grid-cols-3">
35 {items.map((listing) => (
36 <ExtensionCard key={listing.ref} listing={listing} slug={slug} owner={owner} />
37 ))}
38 </div>
39 );
40 return (
41 <div className="space-y-10">
42 <ListingFilterBar kind="extension" filters={filters} counts={filterCounts(listings)} />
43 {tiersShown(filters).map((tier) => {
44 const mine = shown.filter((listing) => listing.tier === tier);
45 const apps = mine.filter((listing) => !isConnectedSystem(listing.manifest));
46 const systems = mine.filter((listing) => isConnectedSystem(listing.manifest));
47 return (
48 <section key={tier} aria-labelledby={`tier-${tier}`}>
49 <SectionHead
50 id={`tier-${tier}`}
51 title={
52 <>
53 <TierBadge tier={tier} />
54 {HEADINGS[tier]}
55 </>
56 }
57 aside={`${mine.length}`}
58 >
59 {tier === "official" && mine.length > 0
60 ? "g1t's own, listed before their first release so you can see what each adds and may do. Each can be installed once it is published."
61 : null}
62 </SectionHead>
63 {mine.length === 0 ? (
64 <TierEmpty tier={tier} kind="extension" filtered={filtered} />
65 ) : (
66 <div className="space-y-6">
67 {apps.length > 0 && grid(apps)}
68 {systems.length > 0 && (
69 <div>
70 <h3 className="text-sm font-semibold">Connected systems</h3>
71 <p className="mt-0.5 mb-3 max-w-2xl text-sm text-muted">
72 Extensions that bridge a system your team already runs, so agents work across it and g1t together. Each page names where its data goes.
73 </p>
74 {grid(systems)}
75 </div>
76 )}
77 </div>
78 )}
79 </section>
80 );
81 })}
82 <section aria-labelledby="how" className="rounded-2xl border border-line bg-surface p-5 sm:p-6">
83 <h2 id="how" className="text-base font-semibold tracking-tight">
84 How extensions are shared
85 </h2>
86 <ul className="mt-3 max-w-3xl space-y-2 text-sm text-muted">
87 <li>An extension is a public repository on g1t with a manifest. Tagging a release publishes a version.</li>
88 <li>An install keeps the version it was installed at. Updates are offered to owners, never forced.</li>
89 <li>Before anything is added, the install screen lists every permission, and says which domains outside g1t its data goes to.</li>
90 <li>Its pages load in a sandboxed frame from g1t's user-content domain, and reach g1t only through what it was allowed.</li>
91 <li>Every call it makes is in the audit log. Owners can cap what it spends, and switch it off at once.</li>
92 </ul>
93 </section>
94 <ListingLegend kind="extension" />
95 </div>
96 );
97}