Skip to content

g1t/apps/web/app/routes/explore.tsx

241 lines10,377 bytesCodeBlame

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.

Merge branch 'projects-kind-and-links'1import { ArrowUpRight, BookMarked, Search } from "lucide-react";
Search across all of g1t, Explore, and a command palette2import type { ReactNode } from "react";
3import { Form, Link } from "react-router";
4
5import type { ExploreRepo } from "@g1t/contracts";
API and MCP server, Rust identity service, registration, site redesign6
7import type { Route } from "./+types/explore";
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look8import { EmptyState, Pill, TimeAgo, notACredential } from "../components/ui";
Social cards for every page: og.g1t.sh9import { page } from "../lib/meta";
Merge branch 'projects-kind-and-links'10import { host } from "../components/deploy";
11import { projects, search } from "../lib/services.server";
API and MCP server, Rust identity service, registration, site redesign12import { getViewer } from "../lib/session.server";
13
Social cards for every page: og.g1t.sh14export function meta({ loaderData, ...args }: Route.MetaArgs) {
Search across all of g1t, Explore, and a command palette15 const filter = loaderData?.language ?? (loaderData?.topic ? `#${loaderData.topic}` : null);
Social cards for every page: og.g1t.sh16 return page(args, {
Search across all of g1t, Explore, and a command palette17 title: filter ? `${filter} · Explore · g1t` : "Explore · g1t",
18 description: filter
19 ? `Public projects on g1t in ${filter}.`
20 : "Public projects on g1t: recently active, newly created, and by language and topic.",
Social cards for every page: og.g1t.sh21 });
API and MCP server, Rust identity service, registration, site redesign22}
23
24export async function loader({ request, context }: Route.LoaderArgs) {
Search across all of g1t, Explore, and a command palette25 const url = new URL(request.url);
26 const sort = url.searchParams.get("sort") === "new" ? ("new" as const) : ("active" as const);
27 const language = url.searchParams.get("language")?.trim().toLowerCase() || null;
28 const topic = url.searchParams.get("topic")?.trim().toLowerCase() || null;
29 const pageNumber = Math.min(Math.max(Number(url.searchParams.get("page")) || 1, 1), 50);
30 const explore = await search
31 .explore(getViewer(context), { sort, language, topic, page: pageNumber })
32 .catch(() => null);
Merge branch 'projects-kind-and-links'33 // Each one's homepage or production, from its project; the page shows without them.
34 const links = explore
35 ? await projects.publicLinks(explore.repos.map((repo) => ({ namespace: repo.namespace, name: repo.name }))).catch(() => ({}))
36 : {};
37 return { sort, language, topic, explore, links: links as Record<string, string> };
Search across all of g1t, Explore, and a command palette38}
39
40/** The address of Explore with some of its filters changed. */
41function href(current: { sort: string; language: string | null; topic: string | null }, change: Record<string, string | null>) {
42 const next = { sort: current.sort, language: current.language, topic: current.topic, ...change };
43 const params = new URLSearchParams();
44 if (next.sort && next.sort !== "active") params.set("sort", next.sort);
45 if (next.language) params.set("language", next.language);
46 if (next.topic) params.set("topic", next.topic);
47 if (change.page) params.set("page", change.page);
48 const query = params.toString();
49 return query ? `/explore?${query}` : "/explore";
50}
51
Merge branch 'projects-kind-and-links'52function RepoCard({ repo, link }: { repo: ExploreRepo; link: string | null }) {
Search across all of g1t, Explore, and a command palette53 return (
Merge branch 'projects-kind-and-links'54 <div className="relative flex h-full flex-col rounded-xl border border-line bg-surface p-4 transition-colors hover:border-line-strong">
Search across all of g1t, Explore, and a command palette55 <span className="flex items-center gap-2">
56 <BookMarked size={15} className="shrink-0 text-faint" />
Merge branch 'projects-kind-and-links'57 <Link prefetch="intent" to={`/${repo.namespace}/${repo.name}`} className="truncate font-mono text-sm after:absolute after:inset-0">
Search across all of g1t, Explore, and a command palette58 <span className="text-muted">{repo.namespace}/</span>
59 <span className="font-medium">{repo.name}</span>
Merge branch 'projects-kind-and-links'60 </Link>
Invite-only launch: sign in with GitHub, repository access and lifecycle, many emails, a new look61 {repo.archived && (
62 <span className="ml-auto shrink-0">
63 <Pill>archived</Pill>
64 </span>
65 )}
Search across all of g1t, Explore, and a command palette66 </span>
67 <span className="mt-2 line-clamp-2 grow text-sm text-muted">{repo.description ?? "No description."}</span>
Merge branch 'projects-kind-and-links'68 {link && (
69 <a
70 href={link}
71 rel="noopener noreferrer nofollow"
72 className="relative z-10 mt-2 flex min-w-0 items-center gap-1 self-start font-mono text-xs text-muted hover:text-accent"
73 >
74 <span className="truncate">{host(link).replace(/\/$/, "")}</span>
75 <ArrowUpRight size={11} className="shrink-0" />
76 </a>
77 )}
Search across all of g1t, Explore, and a command palette78 {repo.topics.length > 0 && (
79 <span className="mt-3 flex flex-wrap gap-1.5">
80 {repo.topics.slice(0, 4).map((topic) => (
81 <span key={topic} className="rounded-full bg-raised px-2 py-px text-[0.6875rem] text-muted ring-1 ring-line">
82 {topic}
83 </span>
84 ))}
85 </span>
86 )}
87 <span className="mt-3 flex items-center gap-3 text-xs text-faint">
88 {repo.language && <span className="font-mono">{repo.language}</span>}
89 <span className="ml-auto">
90 {repo.pushedAt ? (
91 <>
92 Pushed <TimeAgo at={repo.pushedAt} />
93 </>
94 ) : (
95 <>
96 Created <TimeAgo at={repo.createdAt} />
97 </>
98 )}
99 </span>
100 </span>
Merge branch 'projects-kind-and-links'101 </div>
Search across all of g1t, Explore, and a command palette102 );
103}
104
105function Chip({ to, current, children }: { to: string; current: boolean; children: ReactNode }) {
106 return (
107 <Link
108 to={to}
109 aria-current={current ? "true" : undefined}
110 className={`rounded-full px-2.5 py-1 text-xs transition-colors ring-1 ${
111 current ? "bg-accent/15 text-fg ring-accent/50" : "text-muted ring-line hover:text-fg hover:ring-line-strong"
112 }`}
113 >
114 {children}
115 </Link>
116 );
API and MCP server, Rust identity service, registration, site redesign117}
118
Search across all of g1t, Explore, and a command palette119export default function ExplorePage({ loaderData }: Route.ComponentProps) {
120 const { sort, language, topic, explore } = loaderData;
121 const current = { sort, language, topic };
122 const filtered = language || topic;
API and MCP server, Rust identity service, registration, site redesign123 return (
Search across all of g1t, Explore, and a command palette124 <main className="mx-auto max-w-6xl px-4 py-10 sm:px-6">
125 <h1 className="text-2xl font-semibold tracking-tight">Explore</h1>
API and MCP server, Rust identity service, registration, site redesign126 <p className="mt-1 text-muted">
Search across all of g1t, Explore, and a command palette127 Public projects on g1t{language ? ` in ${language}` : ""}
128 {topic ? ` about ${topic}` : ""}.
API and MCP server, Rust identity service, registration, site redesign129 </p>
Search across all of g1t, Explore, and a command palette130 <Form action="/search" role="search" className="relative mt-6 max-w-2xl">
131 <Search size={16} className="pointer-events-none absolute top-1/2 left-3.5 -translate-y-1/2 text-faint" />
API and MCP server, Rust identity service, registration, site redesign132 <input
133 name="q"
Agents as a team: lifecycle, merge queue, billing and a new shell134 {...notACredential()}
Search across all of g1t, Explore, and a command palette135 placeholder="Search repositories, code, issues and people"
136 aria-label="Search g1t"
137 className="h-10 w-full rounded-lg border border-line bg-surface pr-4 pl-10 text-sm outline-none transition-colors placeholder:text-faint hover:border-line-strong focus:border-accent-dim"
API and MCP server, Rust identity service, registration, site redesign138 />
139 </Form>
Search across all of g1t, Explore, and a command palette140
141 <div className="mt-8 grid gap-8 lg:grid-cols-[minmax(0,1fr)_16rem]">
142 <section>
143 <div className="flex flex-wrap items-center gap-3">
144 <nav aria-label="Order" className="flex items-center gap-1 rounded-lg border border-line bg-bg p-1">
145 {[
146 { value: "active", label: "Recently active" },
147 { value: "new", label: "New" },
148 ].map((option) => (
149 <Link
150 key={option.value}
151 to={href(current, { sort: option.value })}
152 aria-current={sort === option.value ? "page" : undefined}
153 className={`rounded-md px-2.5 py-1 text-xs font-medium transition-colors ${
154 sort === option.value ? "bg-raised text-fg ring-1 ring-line" : "text-muted hover:text-fg"
155 }`}
156 >
157 {option.label}
158 </Link>
159 ))}
160 </nav>
161 {filtered && (
162 <Link to={href(current, { language: null, topic: null })} className="text-xs text-muted hover:text-fg">
163 Clear {language ? "language" : "topic"}
164 </Link>
165 )}
166 </div>
167 {!explore ? (
168 <div className="mt-4">
169 <EmptyState title="Explore cannot be reached right now">Try again in a moment.</EmptyState>
170 </div>
171 ) : explore.repos.length === 0 ? (
172 <div className="mt-4">
173 <EmptyState title="No public projects here yet">
174 {filtered ? "Try another language or topic." : "Public projects appear here as they are made."}
175 </EmptyState>
176 </div>
177 ) : (
178 <ul className="mt-4 grid gap-3 sm:grid-cols-2">
179 {explore.repos.map((repo) => (
180 <li key={`${repo.namespace}/${repo.name}`}>
Merge branch 'projects-kind-and-links'181 <RepoCard repo={repo} link={loaderData.links[`${repo.namespace}/${repo.name}`.toLowerCase()] ?? null} />
Search across all of g1t, Explore, and a command palette182 </li>
183 ))}
184 </ul>
185 )}
186 {explore && (explore.page > 1 || explore.more) && (
187 <nav aria-label="Pages" className="mt-6 flex justify-between text-sm">
188 {explore.page > 1 ? (
189 <Link to={href(current, { page: String(explore.page - 1) })} className="text-muted hover:text-fg">
190 Previous
191 </Link>
192 ) : (
193 <span />
194 )}
195 {explore.more && (
196 <Link to={href(current, { page: String(explore.page + 1) })} className="text-muted hover:text-fg">
197 Next
198 </Link>
199 )}
200 </nav>
201 )}
202 </section>
203
204 <aside className="space-y-6">
205 {explore && explore.languages.length > 0 && (
206 <section>
207 <h2 className="text-xs font-medium text-faint">Languages</h2>
208 <div className="mt-2 flex flex-wrap gap-1.5">
209 {explore.languages.map((facet) => (
210 <Chip
211 key={facet.name}
212 to={href(current, { language: language === facet.name ? null : facet.name })}
213 current={language === facet.name}
214 >
215 <span className="font-mono">{facet.name}</span> <span className="text-faint">{facet.count}</span>
216 </Chip>
217 ))}
218 </div>
219 </section>
220 )}
221 {explore && explore.topics.length > 0 && (
222 <section>
223 <h2 className="text-xs font-medium text-faint">Topics</h2>
224 <div className="mt-2 flex flex-wrap gap-1.5">
225 {explore.topics.map((facet) => (
226 <Chip
227 key={facet.name}
228 to={href(current, { topic: topic === facet.name ? null : facet.name })}
229 current={topic === facet.name}
230 >
231 {facet.name} <span className="text-faint">{facet.count}</span>
232 </Chip>
233 ))}
234 </div>
235 </section>
236 )}
237 </aside>
238 </div>
API and MCP server, Rust identity service, registration, site redesign239 </main>
240 );
241}

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