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

Commit

Every page's script loads again (#51): since Deploy #356, pages threw "Cannot read properties of undefined (reading 'filter')" as they loaded, so nothing on them answered a click and every Select showed empty. A project's tabs (lib/project-nav.ts) and the phone's soon pages (components/mobile.tsx) read the roadmap as their module loaded, and the bundle had put the roadmap in a chunk that imports theirs back, so it was read before it was there. Both are now built on first use, which no order of chunks can break. Explain moves into components/home.tsx, so Home's parts no longer import each other. Checked: a production build loaded route by route in Chrome (root, then each route's module): 0 of 257 fail, where the build before this fails all 257 with the same error and stack as production; the site's typecheck and tests (955).

G1t-Session:
ses_01m4ygex20ezftekecszgw2gv5
syntaqxcommitted Parent10520d1Browse files
4 files+102−840/4 viewed
+5−15
22 * Home's way into Ask (components/ask-composer): the box at the top of the
33 * page, What changed (g1t's plain-English summary of the span, streamed
44 * onto the page by components/home-summary), and Explain on each change
5− * that landed. The box and Explain hand their question to the top bar's Ask
6− * panel (lib/ask.ts askG1t). Every question goes to the viewer's direct
7− * message with @g1t, so the answers stay in Chat and Ask carries on from them.
5+ * that landed (components/home.tsx). The box and Explain hand their
6+ * question to the top bar's Ask panel (lib/ask.ts askG1t). Every question
7+ * goes to the viewer's direct message with @g1t, so the answers stay in
8+ * Chat and Ask carries on from them.
89 */
910 import { ArrowUp, MessageSquare, RotateCcw, Sparkles } from "lucide-react";
1011 import { type FormEvent, type KeyboardEvent, Suspense, lazy, useCallback, useEffect, useRef, useState } from "react";
1112 import { Link } from "react-router";
1213
1314 import { askG1t, openAsk } from "../lib/ask";
14−import { HOME_STARTERS, type LandedChange, type Span, explainQuestion, summarizeQuestion } from "../lib/home";
15+import { HOME_STARTERS, type Span, summarizeQuestion } from "../lib/home";
1516 import { Suggestion, Suggestions } from "./ai-elements/suggestion";
1617 import { Card } from "./home";
1718 import { MarkGlyph } from "./logo";
172173 </div>
173174 </Card>
174175 </div>
175− );
176−}
177−
178−/** Explain one change that landed: what it did and why, in plain English. */
179−export function ExplainButton({ change }: { change: Pick<LandedChange, "kind" | "title" | "to"> }) {
180− return (
181− <Hint label="Explain this change in plain English">
182− <Button type="button" variant="ghost" size="icon-xs" aria-label={`Explain ${change.title}`} onClick={() => askG1t(explainQuestion(change))} className="shrink-0 text-muted hover:text-accent">
183− <Sparkles size={13} />
184− </Button>
185− </Hint>
186176 );
187177 }
+16−1
1818 Package,
1919 Rocket,
2020 Server,
21+ Sparkles,
2122 Tag,
2223 User,
2324 Users,
4344 type Stake,
4445 WINDOWS,
4546 agentLabel,
47+ explainQuestion,
4648 landedByDay,
4749 listOf,
4850 shippedLabel,
5052 waited,
5153 whenShort,
5254 } from "../lib/home";
55+import { askG1t } from "../lib/ask";
5356 import { cn } from "../lib/cn";
5457 import { money } from "../lib/money";
5558 import { CommitText } from "./commit-message";
56−import { ExplainButton } from "./home-ask";
59+import { Button } from "./ui/button";
60+import { Hint } from "./ui/hint";
5761 import { ButtonLink } from "./ui";
5862 import { AgentMarker, Avatar } from "./ui/avatar";
5963 import { Badge, type BadgeTone } from "./ui/badge";
544548 );
545549 }
546550
551+/** Explain one change that landed: what it did and why, in plain English, in the top bar's Ask (components/home-ask). */
552+function ExplainButton({ change }: { change: LandedChange }) {
553+ return (
554+ <Hint label="Explain this change in plain English">
555+ <Button type="button" variant="ghost" size="icon-xs" aria-label={`Explain ${change.title}`} onClick={() => askG1t(explainQuestion(change))} className="shrink-0 text-muted hover:text-accent">
556+ <Sparkles size={13} />
557+ </Button>
558+ </Hint>
559+ );
560+}
561+
547562 function LandedItem({ change, now, tz, timeOnly }: { change: LandedChange; now: number; tz: string | null; timeOnly?: boolean }) {
548563 return (
549564 <li className={cn(rowClass, "items-start")}>
+8−4
545545 Security: "security",
546546 Insights: "insights",
547547 };
548−const SOON_PAGES: Record<string, ProjectPage> = Object.fromEntries(
549− ROADMAP.flatMap((item) => (SOON_UNDER[item.section] ? [[item.key, SOON_UNDER[item.section]!] as const] : [])),
550−);
548+// Read from the roadmap on first use, not as the module loads: a bundle can
549+// load this before the roadmap's chunk (lib/project-nav.ts says more).
550+let soonPages: Record<string, ProjectPage> | null = null;
551+const soonPagesOf = (): Record<string, ProjectPage> =>
552+ (soonPages ??= Object.fromEntries(
553+ ROADMAP.flatMap((item) => (SOON_UNDER[item.section] ? [[item.key, SOON_UNDER[item.section]!] as const] : [])),
554+ ));
551555
552556 /**
553557 * A project's pages along the bottom of its header: the sidebar's own list
579583 }) {
580584 const { pathname } = useLocation();
581585 const rest = pathname.toLowerCase().startsWith(base.toLowerCase()) ? pathname.slice(base.length) : "";
582− const at = projectPageAt(rest, SOON_PAGES);
586+ const at = projectPageAt(rest, soonPagesOf());
583587 const pages: (ProjectPage | "overview")[] = ["overview", ...projectPages(member, can).filter((page) => page !== "observability" && page !== "settings" && (issues || page !== "issues")),
584588 ...(settings ?? member ? (["settings"] as const) : []),
585589 ];
+73−64
3333 return roadmapIn(section).map((item) => ({ label: item.title, path: `soon/${item.key}`, soon: true, about: item.summary }));
3434 }
3535
36−export const SECTIONS: Section[] = [
37− {
38− key: "Code",
39− tabs: [
40− { label: "Files", path: "code", also: ["tree", "blob"] },
41− { label: "Commits", path: "commits", also: ["commit"] },
42− { label: "Branches", path: "branches" },
43− { label: "Tags", path: "tags" },
44− { label: "Releases", path: "releases" },
45− { label: "Compare", path: "compare" },
46− // No Docs tab: code is not docs. Docs is a workspace mode, filtered by project there.
47− ],
48− },
49− {
50− key: "Issues",
51− tabs: [
52− { label: "Issues", path: "issues" },
53− { label: "Milestones", path: "milestones" },
54− ...soon("Issues"),
55− ],
56− },
57− {
58− key: "Deployments",
59− tabs: [{ label: "Deployments", path: "deployments" }, ...soon("Deployments")],
60− },
61− {
62− key: "Observability",
63− tabs: [
64− { label: "Overview", path: "observability", exact: true, members: true },
65− { label: "Requests", path: "observability/requests", members: true },
66− { label: "Logs", path: "observability/logs", members: true },
67− { label: "Traces", path: "observability/traces", members: true },
68− { label: "Errors", path: "observability/errors", members: true },
69− { label: "Metrics", path: "observability/metrics", members: true },
70− { label: "Settings", path: "observability/settings", members: true, needs: "manage_integrations" },
71− ...soon("Observability"),
72− ],
73− },
74− {
75− key: "Security",
76− tabs: [
77− { label: "Overview", path: "security", members: true, needs: "security_alerts", exact: true },
78− { label: "Secret scanning", path: "security/secret-scanning", members: true, needs: "security_alerts" },
79− { label: "Code scanning", path: "security/code-scanning", members: true, needs: "security_alerts", also: ["security/pulls"] },
80− { label: "Vulnerabilities", path: "security/vulnerabilities", members: true, needs: "security_alerts" },
81− { label: "Dependency graph", path: "security/dependency-graph", members: true, needs: "security_alerts" },
82− { label: "Dependency updates", path: "security/dependency-updates", members: true, needs: "security_alerts" },
83− { label: "Advisories", path: "security/advisories", members: true, needs: "security_alerts", also: ["security/reports"] },
84− { label: "Settings", path: "security/settings", members: true, needs: "security_alerts" },
85− ...soon("Security"),
86− ],
87− },
88− {
89− key: "Insights",
90− tabs: [
91− { label: "Contributors", path: "contributors" },
92− { label: "Activity", path: "activity" },
93− { label: "Stargazers", path: "stargazers" },
94− ...soon("Insights"),
95− ],
96− },
97−];
36+/**
37+ * The project's pages, built on first use rather than when the module
38+ * loads: the soon tabs read the roadmap, and a bundle that splits the two
39+ * into chunks that import each other could otherwise read it before it is
40+ * there (a page that throws as it loads, so nothing on it works).
41+ */
42+let built: Section[] | null = null;
43+export function sections(): Section[] {
44+ return (built ??= [
45+ {
46+ key: "Code",
47+ tabs: [
48+ { label: "Files", path: "code", also: ["tree", "blob"] },
49+ { label: "Commits", path: "commits", also: ["commit"] },
50+ { label: "Branches", path: "branches" },
51+ { label: "Tags", path: "tags" },
52+ { label: "Releases", path: "releases" },
53+ { label: "Compare", path: "compare" },
54+ // No Docs tab: code is not docs. Docs is a workspace mode, filtered by project there.
55+ ],
56+ },
57+ {
58+ key: "Issues",
59+ tabs: [
60+ { label: "Issues", path: "issues" },
61+ { label: "Milestones", path: "milestones" },
62+ ...soon("Issues"),
63+ ],
64+ },
65+ {
66+ key: "Deployments",
67+ tabs: [{ label: "Deployments", path: "deployments" }, ...soon("Deployments")],
68+ },
69+ {
70+ key: "Observability",
71+ tabs: [
72+ { label: "Overview", path: "observability", exact: true, members: true },
73+ { label: "Requests", path: "observability/requests", members: true },
74+ { label: "Logs", path: "observability/logs", members: true },
75+ { label: "Traces", path: "observability/traces", members: true },
76+ { label: "Errors", path: "observability/errors", members: true },
77+ { label: "Metrics", path: "observability/metrics", members: true },
78+ { label: "Settings", path: "observability/settings", members: true, needs: "manage_integrations" },
79+ ...soon("Observability"),
80+ ],
81+ },
82+ {
83+ key: "Security",
84+ tabs: [
85+ { label: "Overview", path: "security", members: true, needs: "security_alerts", exact: true },
86+ { label: "Secret scanning", path: "security/secret-scanning", members: true, needs: "security_alerts" },
87+ { label: "Code scanning", path: "security/code-scanning", members: true, needs: "security_alerts", also: ["security/pulls"] },
88+ { label: "Vulnerabilities", path: "security/vulnerabilities", members: true, needs: "security_alerts" },
89+ { label: "Dependency graph", path: "security/dependency-graph", members: true, needs: "security_alerts" },
90+ { label: "Dependency updates", path: "security/dependency-updates", members: true, needs: "security_alerts" },
91+ { label: "Advisories", path: "security/advisories", members: true, needs: "security_alerts", also: ["security/reports"] },
92+ { label: "Settings", path: "security/settings", members: true, needs: "security_alerts" },
93+ ...soon("Security"),
94+ ],
95+ },
96+ {
97+ key: "Insights",
98+ tabs: [
99+ { label: "Contributors", path: "contributors" },
100+ { label: "Activity", path: "activity" },
101+ { label: "Stargazers", path: "stargazers" },
102+ ...soon("Insights"),
103+ ],
104+ },
105+ ]);
106+}
98107
99108 /** Pull requests, the work on them, the merge queue and sessions share a page's tabs too. */
100109 export const PULLS: Section = {
116125 * belongs to a page with more than one view.
117126 */
118127 export function tabsFor(rest: string, member: boolean, can?: Partial<Abilities>, features?: { issues?: boolean }): Tab[] | null {
119− for (const section of [...SECTIONS, PULLS]) {
128+ for (const section of [...sections(), PULLS]) {
120129 // Issues turned off (a fork's default): no Issues tabs, though what was opened stays readable.
121130 if (section.key === "Issues" && features?.issues === false) continue;
122131 const visible = section.tabs.filter(
129138
130139 /** The first page of a section, for its sidebar link: a built one if any. */
131140 export function entryOf(key: Section["key"]): Tab | undefined {
132− return SECTIONS.find((section) => section.key === key)?.tabs[0];
141+ return sections().find((section) => section.key === key)?.tabs[0];
133142 }

Comments

Sign in to comment.