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

Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)1/**
2 * The landing page's product tour: one story told as a timeline. Every
3 * frame is a pure function of the time into the loop, so the tour can be
4 * paused, resumed, jumped to a step, or shown still, and the server and
5 * the first client render agree (t = 0).
6 *
The home page leads with Code: the tour opens on the pull request, and the forge comes before Chat7 * The story opens on the code: Otto's pull request #431 runs its checks,
8 * is approved and is merged by the queue. Then where it came from: the
9 * ask in #web, handed to @otto by g1t, now merged, and Izzy telling
10 * #support. Then Otto's desk, the record of what he did and spent, and
11 * last Inky's docs page picking up the change.
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)12 */
13
14export type Scene = "chat" | "agents" | "code" | "docs";
15
16/** Where the soft cursor points: an element marked `data-tour="<target>"`. */
17export type CursorTarget =
18 | "composer"
19 | "send"
20 | "rail-agents"
21 | "rail-code"
22 | "rail-chat"
23 | "rail-docs"
24 | "task-pull"
25 | "idle";
26
27export type Frame = {
28 scene: Scene;
29 /** Characters of the person's message typed into the composer. */
30 typed: number;
31 /** The person's message has been sent. */
32 sent: boolean;
33 /** g1t shows as typing. */
34 g1tTyping: boolean;
35 /** g1t's handoff reply, with the task card. */
36 handoff: boolean;
37 /** Builder's steps done, 0 to STEPS.length. */
38 steps: number;
39 /** Checks passed on the pull request, 0 to CHECKS.length. */
40 checks: number;
41 /** The diff is shown on the pull request. */
42 diff: boolean;
43 /** @reviewer approved. */
44 approved: boolean;
45 /** The merge queue merged it. */
46 merged: boolean;
47 /** The task card in #web says merged and deploying. */
48 cardMerged: boolean;
49 /** g1t posted that it shipped. */
50 shipped: boolean;
51 /** Izzy says she told #support. */
52 izzy: boolean;
53 /** Otto consulted Margo, shown as a collapsed line on the task. */
54 consult: boolean;
55 /** The docs page shows the changed line. */
56 docUpdated: boolean;
57 cursor: CursorTarget;
58 /** The cursor is pressing. */
59 click: boolean;
60 /** The cursor is shown at all. */
61 cursorShown: boolean;
62};
63
64/** What the person types into #web. */
65export const ASK = "@g1t the CSV export times out for big accounts. Can we get it fixed before Thursday?";
66
67export const STEPS: { text: string; cost: number }[] = [
68 { text: "Read the export job and the two reports in #web", cost: 0.04 },
69 { text: "Found it: every row loads before the first byte is sent", cost: 0.11 },
70 { text: "Streamed rows in batches of 1,000", cost: 0.19 },
71 { text: "Tested a 200,000-row account: 3.1 s, was 61 s", cost: 0.27 },
72 { text: "Opened pull request #431", cost: 0.31 },
73];
74
75export const CHECKS = ["build", "test", "lint", "preview"];
76
77/** The beats of the story, in ms from the start of the loop. */
78export const BEAT = {
The home page leads with Code: the tour opens on the pull request, and the forge comes before Chat79 code: 0,
80 check0: 900,
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)81 checkGap: 650,
The home page leads with Code: the tour opens on the pull request, and the forge comes before Chat82 diff: 1300,
83 approved: 3900,
84 merged: 5000,
85 railChatClick: 6300,
86 chat: 6450,
87 cardMerged: 7100,
88 shipped: 8400,
89 izzy: 9600,
90 railAgentsClick: 11000,
91 agents: 11150,
92 railDocsClick: 15000,
93 docs: 15150,
94 docUpdated: 16300,
95 end: 21000,
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)96} as const;
97
98/** The whole loop, in ms. */
99export const LOOP_MS = BEAT.end;
100
101/** The step pills under the frame: where each starts, for jumping to it. */
102export const PILLS: { scene: Scene; label: string; start: number; soon: boolean }[] = [
The home page leads with Code: the tour opens on the pull request, and the forge comes before Chat103 { scene: "code", label: "Code", start: 0, soon: false },
104 { scene: "chat", label: "Chat", start: BEAT.chat, soon: false },
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)105 { scene: "agents", label: "Agents", start: BEAT.agents, soon: true },
106 { scene: "docs", label: "Docs", start: BEAT.docs, soon: true },
107];
108
The home page leads with Code: the tour opens on the pull request, and the forge comes before Chat109/** Which pill a time falls under. */
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)110export function pillAt(t: number): number {
111 let index = 0;
112 PILLS.forEach((pill, i) => {
113 if (t >= pill.start) index = i;
114 });
115 return index;
116}
117
118/** How far through its pill a time is, 0 to 1. */
119export function pillProgress(t: number): number {
120 const index = pillAt(t);
121 const start = PILLS[index].start;
122 const end = PILLS[index + 1]?.start ?? LOOP_MS;
123 return Math.min(1, Math.max(0, (t - start) / (end - start)));
124}
125
126function count(t: number, first: number, gap: number, max: number): number {
127 if (t < first) return 0;
128 return Math.min(max, Math.floor((t - first) / gap) + 1);
129}
130
131/** Where the cursor is headed, and whether it is pressing, at time t. */
132function cursorAt(t: number): { cursor: CursorTarget; click: boolean } {
133 const press = (at: number) => t >= at && t < at + 180;
134 const moves: [number, CursorTarget][] = [
135 [BEAT.merged + 300, "rail-chat"],
The home page leads with Code: the tour opens on the pull request, and the forge comes before Chat136 [BEAT.izzy + 500, "rail-agents"],
137 [BEAT.agents + 2600, "rail-docs"],
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)138 [BEAT.docs + 500, "idle"],
139 ];
140 let cursor: CursorTarget = "idle";
141 for (const [at, target] of moves) if (t >= at) cursor = target;
The home page leads with Code: the tour opens on the pull request, and the forge comes before Chat142 const click = press(BEAT.railChatClick) || press(BEAT.railAgentsClick) || press(BEAT.railDocsClick);
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)143 return { cursor, click };
144}
145
The home page leads with Code: the tour opens on the pull request, and the forge comes before Chat146/**
147 * The frame at time t (ms into the loop). The ask, g1t's handoff and
148 * Otto's work all happened before the pull request the story opens on, so
149 * they are shown done throughout.
150 */
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)151export function frameAt(time: number): Frame {
152 const t = ((time % LOOP_MS) + LOOP_MS) % LOOP_MS;
The home page leads with Code: the tour opens on the pull request, and the forge comes before Chat153 const scene: Scene = t >= BEAT.docs ? "docs" : t >= BEAT.agents ? "agents" : t >= BEAT.chat ? "chat" : "code";
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)154 const { cursor, click } = cursorAt(t);
155 return {
156 scene,
The home page leads with Code: the tour opens on the pull request, and the forge comes before Chat157 typed: 0,
158 sent: true,
159 g1tTyping: false,
160 handoff: true,
161 steps: STEPS.length,
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)162 checks: count(t, BEAT.check0, BEAT.checkGap, CHECKS.length),
163 diff: t >= BEAT.diff,
164 approved: t >= BEAT.approved,
165 merged: t >= BEAT.merged,
166 cardMerged: t >= BEAT.cardMerged,
167 shipped: t >= BEAT.shipped,
168 izzy: t >= BEAT.izzy,
The home page leads with Code: the tour opens on the pull request, and the forge comes before Chat169 consult: true,
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)170 docUpdated: t >= BEAT.docUpdated,
171 cursor,
172 click,
The home page leads with Code: the tour opens on the pull request, and the forge comes before Chat173 cursorShown: t >= BEAT.merged && t < BEAT.end - 600,
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)174 };
175}
176
177/** A still frame for each pill, for reduced motion: the moment the step makes its point. */
178export const STILLS: Record<Scene, number> = {
179 code: BEAT.approved + 200,
The home page leads with Code: the tour opens on the pull request, and the forge comes before Chat180 chat: BEAT.izzy + 400,
181 agents: BEAT.agents + 400,
Merge the workspace shell: navigation and phone shell, g1t as orchestrator, agents in roles with audience-checked reads, reactions and custom emoji, live notifications and browser push, the homepage tour (agents 0002, chat 0002)182 docs: BEAT.docUpdated + 400,
183};
184
185/** Whether two frames would draw the same. */
186export function sameFrame(a: Frame, b: Frame): boolean {
187 for (const key of Object.keys(a) as (keyof Frame)[]) if (a[key] !== b[key]) return false;
188 return true;
189}

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