Skip to content
238 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.

Navigation: the workspace's own page with tabs, pins and recent in the sidebar1/**
2 * A workspace's Projects page, for a workspace with hundreds of them: what
3 * the address asks for (a search, filters, a sort, a view and a page), and
4 * the list that answers it. Read from and written to the address, so a
5 * filtered list can be shared, bookmarked and gone back to.
6 */
7
A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so.8import type { MirrorState, Project, ProjectEcosystem, ProjectKind } from "@g1t/contracts";
Navigation: the workspace's own page with tabs, pins and recent in the sidebar9
10export type ProjectSort = "updated" | "name" | "active" | "pushed";
11export type ProjectView = "list" | "grid";
12
A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so.13/**
14 * The Kind filter's choices: a kind of project, or where it is managed.
15 * `mirror` is a repository that mirrors a remote (whatever state the mirror
16 * is in), `g1t` one that is g1t's own.
17 */
18export type ProjectKindFilter = "all" | ProjectKind | "mirror" | "g1t";
19
Navigation: the workspace's own page with tabs, pins and recent in the sidebar20export type ProjectQuery = {
21 /** Words in the name, slug or description. */
22 q: string;
23 visibility: "all" | "public" | "private";
A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so.24 kind: ProjectKindFilter;
Navigation: the workspace's own page with tabs, pins and recent in the sidebar25 /** A language, from the manifests at the project's root. */
26 language: string | null;
27 /** Only projects with Deployments on. */
28 deployments: boolean;
29 /** Archived projects: left out (the default), shown too, or only them. */
30 archived: "hide" | "include" | "only";
31 sort: ProjectSort;
32 view: ProjectView;
33 /** 1 first. */
34 page: number;
35};
36
37/** Projects to a page. */
38export const PAGE_SIZE = 30;
39
40export const DEFAULT_QUERY: ProjectQuery = {
41 q: "",
42 visibility: "all",
43 kind: "all",
44 language: null,
45 deployments: false,
46 archived: "hide",
47 sort: "updated",
48 view: "list",
49 page: 1,
50};
51
52export const SORTS: { value: ProjectSort; label: string }[] = [
53 { value: "updated", label: "Recently updated" },
54 { value: "pushed", label: "Recently pushed" },
55 { value: "active", label: "Most active" },
56 { value: "name", label: "Name" },
57];
58
59/** The language a project's manifests say it is written in. */
60export const LANGUAGE_OF: Record<ProjectEcosystem, string> = {
61 npm: "JavaScript",
62 cargo: "Rust",
63 go: "Go",
64 composer: "PHP",
65 python: "Python",
66};
67
A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so.68/** The remote a project's repository mirrors, and where the mirror stands, for its row. */
69export type ListedMirror = { state: MirrorState; remote: string };
70
71/**
72 * What the list needs of a project, with whether its Deployments are on and
73 * the remote its repository mirrors (null for one managed on g1t).
74 */
Navigation: the workspace's own page with tabs, pins and recent in the sidebar75export type Listed = Pick<
76 Project,
77 "id" | "slug" | "name" | "description" | "private" | "archived" | "kind" | "ecosystem" | "updatedAt" | "pushedAt" | "activity"
A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so.78> & { deploying: boolean; mirror: ListedMirror | null };
Navigation: the workspace's own page with tabs, pins and recent in the sidebar79
80const one = <T extends string>(value: string | null, allowed: readonly T[], fallback: T): T =>
81 (allowed as readonly string[]).includes(value ?? "") ? (value as T) : fallback;
82
83/** What the address asks for; anything it does not say, or says wrong, is the default. */
84export function readProjectQuery(params: URLSearchParams): ProjectQuery {
85 const page = Number.parseInt(params.get("page") ?? "", 10);
86 return {
87 q: (params.get("q") ?? "").trim().slice(0, 100),
88 visibility: one(params.get("visibility"), ["all", "public", "private"] as const, "all"),
A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so.89 kind: one(params.get("kind"), ["all", "app", "library", "tool", "docs", "other", "mirror", "g1t"] as const, "all"),
Navigation: the workspace's own page with tabs, pins and recent in the sidebar90 language: params.get("language")?.trim() || null,
91 deployments: params.get("deployments") === "on",
92 archived: one(params.get("archived"), ["hide", "include", "only"] as const, "hide"),
93 sort: one(params.get("sort"), ["updated", "name", "active", "pushed"] as const, "updated"),
94 view: one(params.get("view"), ["list", "grid"] as const, "list"),
95 page: Number.isFinite(page) && page > 0 ? page : 1,
96 };
97}
98
99/**
100 * The address for `query` with `change` made, leaving out what is the
101 * default. Changing anything but the page goes back to the first page.
102 */
103export function projectQueryString(query: ProjectQuery, change: Partial<ProjectQuery> = {}): string {
104 const next = { ...query, ...change };
105 if (!("page" in change)) next.page = 1;
106 const params = new URLSearchParams();
107 if (next.q) params.set("q", next.q);
108 if (next.visibility !== "all") params.set("visibility", next.visibility);
109 if (next.kind !== "all") params.set("kind", next.kind);
110 if (next.language) params.set("language", next.language);
111 if (next.deployments) params.set("deployments", "on");
112 if (next.archived !== "hide") params.set("archived", next.archived);
113 if (next.sort !== "updated") params.set("sort", next.sort);
114 if (next.view !== "list") params.set("view", next.view);
115 if (next.page > 1) params.set("page", String(next.page));
116 const text = params.toString();
117 return text ? `?${text}` : "";
118}
119
120/** Whether anything narrows the list, beyond leaving archived projects out. */
121export function isFiltered(query: ProjectQuery): boolean {
122 return Boolean(
123 query.q || query.visibility !== "all" || query.kind !== "all" || query.language || query.deployments || query.archived !== "hide",
124 );
125}
126
127/** When it last changed: its own settings, or a push, whichever is later. */
128export function lastUpdated(project: Pick<Listed, "updatedAt" | "pushedAt">): string {
129 return project.pushedAt && project.pushedAt > project.updatedAt ? project.pushedAt : project.updatedAt;
130}
131
132function matches(project: Listed, words: string[]): boolean {
133 const text = `${project.name} ${project.slug} ${project.description ?? ""}`.toLowerCase();
134 return words.every((word) => text.includes(word));
135}
136
137/** The projects `query` asks for, in its order, before paging. */
138export function filterProjects(projects: Listed[], query: ProjectQuery): Listed[] {
139 const words = query.q.toLowerCase().split(/\s+/).filter(Boolean);
140 const shown = projects.filter(
141 (project) =>
142 (query.archived === "include" || (query.archived === "only") === project.archived) &&
143 (query.visibility === "all" || (query.visibility === "private") === project.private) &&
A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so.144 (query.kind === "all" ||
145 (query.kind === "mirror" ? project.mirror != null : query.kind === "g1t" ? project.mirror == null : project.kind === query.kind)) &&
Navigation: the workspace's own page with tabs, pins and recent in the sidebar146 (!query.language || (project.ecosystem != null && LANGUAGE_OF[project.ecosystem] === query.language)) &&
147 (!query.deployments || project.deploying) &&
148 (words.length === 0 || matches(project, words)),
149 );
150 return sortProjects(shown, query.sort, words);
151}
152
153const byName = (a: Listed, b: Listed) => a.name.localeCompare(b.name, undefined, { sensitivity: "base" }) || a.slug.localeCompare(b.slug);
154
155/**
156 * In `sort`'s order, ties by name. While searching, a name that starts
157 * with what was typed comes first.
158 */
159export function sortProjects(projects: Listed[], sort: ProjectSort, words: string[] = []): Listed[] {
160 const first = words[0];
161 const leads = (project: Listed) =>
162 first != null && (project.name.toLowerCase().startsWith(first) || project.slug.startsWith(first)) ? 0 : 1;
163 const order: Record<ProjectSort, (a: Listed, b: Listed) => number> = {
164 name: byName,
165 updated: (a, b) => lastUpdated(b).localeCompare(lastUpdated(a)),
166 pushed: (a, b) => (b.pushedAt ?? "").localeCompare(a.pushedAt ?? ""),
167 active: (a, b) => b.activity - a.activity,
168 };
169 return [...projects].sort((a, b) => leads(a) - leads(b) || order[sort](a, b) || byName(a, b));
170}
171
172/** One page of them, and where it sits among the rest. */
173export function pageOf<T>(list: T[], page: number, size = PAGE_SIZE): { items: T[]; page: number; pages: number; from: number; to: number } {
174 const pages = Math.max(1, Math.ceil(list.length / size));
175 const current = Math.min(Math.max(1, page), pages);
176 const start = (current - 1) * size;
177 const items = list.slice(start, start + size);
178 return { items, page: current, pages, from: items.length ? start + 1 : 0, to: start + items.length };
179}
180
181export type Facets = {
182 visibility: { public: number; private: number };
Merge branch 'main' into worktree-agent-a69aeabc4b0deeb97183 kind: Record<ProjectKind, number>;
Navigation: the workspace's own page with tabs, pins and recent in the sidebar184 languages: { name: string; count: number }[];
185 deploying: number;
186 archived: number;
A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so.187 /** Projects whose repository mirrors a remote; the rest are managed on g1t. */
188 mirrors: number;
Navigation: the workspace's own page with tabs, pins and recent in the sidebar189};
190
191/** How many of the workspace's projects each filter would show, before any is chosen. */
192export function facetsOf(projects: Listed[]): Facets {
193 const languages = new Map<string, number>();
A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so.194 const facets: Facets = {
195 visibility: { public: 0, private: 0 },
196 kind: { app: 0, library: 0, tool: 0, docs: 0, other: 0 },
197 languages: [],
198 deploying: 0,
199 archived: 0,
200 mirrors: 0,
201 };
Navigation: the workspace's own page with tabs, pins and recent in the sidebar202 for (const project of projects) {
203 facets.visibility[project.private ? "private" : "public"]++;
204 facets.kind[project.kind]++;
205 if (project.deploying) facets.deploying++;
A desktop release goes out with what can be built. Linux is built on g1t's machines in every release; Windows and macOS each wait on a repository variable: a machine registered for them, or for Windows a cross build on Linux once aka.ms and download.visualstudio.microsoft.com are workflow-only domains, which is where the first release stopped. A platform with no machine is skipped and the download page says it is coming, with a word for the visitor whose platform this release lacks. The guide and CONTRIBUTING say so.206 if (project.mirror) facets.mirrors++;
Navigation: the workspace's own page with tabs, pins and recent in the sidebar207 if (project.archived) facets.archived++;
208 if (project.ecosystem) {
209 const language = LANGUAGE_OF[project.ecosystem];
210 languages.set(language, (languages.get(language) ?? 0) + 1);
211 }
212 }
213 facets.languages = [...languages].map(([name, count]) => ({ name, count })).sort((a, b) => b.count - a.count || a.name.localeCompare(b.name));
214 return facets;
215}
216
217/**
218 * The index of the row to move to from `at` by key, in a list of `count`
219 * rows: arrows and j/k move one, Home and End to either end. Null for any
220 * other key.
221 */
222export function nextRow(key: string, at: number, count: number): number | null {
223 if (count === 0) return null;
224 switch (key) {
225 case "ArrowDown":
226 case "j":
227 return Math.min(count - 1, at + 1);
228 case "ArrowUp":
229 case "k":
230 return Math.max(0, at - 1);
231 case "Home":
232 return 0;
233 case "End":
234 return count - 1;
235 default:
236 return null;
237 }
238}

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