Commit

Get started on mission control

A checklist above everything else until a new workspace has handed off its first outcome: create a workspace, add a repository, add agent credit, hand off an outcome. Each step ticks itself off and links to where it is done.

syntaqxcommitted Parentd52353fBrowse files
1 file+90−10/1 viewed
+90−1
11 import { ArrowRight, Hand, Plus } from "lucide-react";
22 import { type ReactNode, useEffect } from "react";
3−import { Link, data, useRevalidator } from "react-router";
3+import { Link, data, useRevalidator, useRouteLoaderData } from "react-router";
44
55 import type { Lifecycle, Pull, Repo } from "@g1t/contracts";
66
77 import type { Route } from "./+types/home";
88 import { Landing } from "../components/landing";
9+import type { ShellData } from "../components/shell";
910 import { STAGE_LABEL, StageDots } from "../components/lifecycle";
1011 import { RepoList } from "../components/repo-list";
1112 import {
137138 return `${count} ${count === 1 ? one : many}`;
138139 }
139140
141+type Step = { done: boolean; title: string; about: string; to: string | null; action: string };
142+
143+/**
144+ * The first things to do, ticked off as they are done, until an outcome has
145+ * been handed to agents. Shown above everything else on mission control.
146+ */
147+function GetStarted({ steps }: { steps: Step[] }) {
148+ const left = steps.filter((step) => !step.done).length;
149+ return (
150+ <section className="rounded-2xl bg-surface p-5 ring-1 ring-line">
151+ <div className="flex items-baseline justify-between gap-3">
152+ <h2 className="font-semibold tracking-tight">Get started</h2>
153+ <span className="text-xs text-muted">
154+ {steps.length - left} of {steps.length} done
155+ </span>
156+ </div>
157+ <ol className="mt-4 space-y-2">
158+ {steps.map((step, index) => (
159+ <li
160+ key={step.title}
161+ className={`flex items-start gap-3 rounded-xl px-3 py-2.5 ${step.done ? "" : "bg-bg/50 ring-1 ring-line"}`}
162+ >
163+ <span
164+ className={`mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-full text-[0.6875rem] font-medium ${
165+ step.done ? "bg-accent text-bg" : "text-muted ring-1 ring-line-strong"
166+ }`}
167+ >
168+ {step.done ? "✓" : index + 1}
169+ </span>
170+ <span className="min-w-0 grow">
171+ <span className={`block text-sm font-medium ${step.done ? "text-muted line-through decoration-faint" : ""}`}>
172+ {step.title}
173+ </span>
174+ {!step.done && <span className="mt-0.5 block text-xs leading-5 text-muted">{step.about}</span>}
175+ </span>
176+ {!step.done && step.to && (
177+ <Link
178+ to={step.to}
179+ className="shrink-0 rounded-md bg-fg px-2.5 py-1 text-xs font-medium text-bg transition-colors hover:bg-white"
180+ >
181+ {step.action}
182+ </Link>
183+ )}
184+ </li>
185+ ))}
186+ </ol>
187+ </section>
188+ );
189+}
190+
140191 export default function Home({ loaderData }: Route.ComponentProps) {
141192 const { viewer, repos, active, assigned } = loaderData;
142193
156207 return () => clearInterval(timer);
157208 }, [changing, revalidator]);
158209
210+ const shell = useRouteLoaderData("root")?.shell as ShellData | null | undefined;
159211 if (!viewer) return <Landing repos={repos} />;
160212
213+ const workspace = shell?.workspace?.slug;
214+ const first = repos[0];
215+ const handedOff = active.length > 0 || (shell?.monthSpentMicros ?? 0) > 0;
216+ const steps: Step[] = [
217+ {
218+ done: Boolean(workspace),
219+ title: "Create a workspace",
220+ about: "Repositories, people and agent credit live in one.",
221+ to: "/workspaces/new",
222+ action: "Create",
223+ },
224+ {
225+ done: repos.length > 0,
226+ title: "Add a repository",
227+ about: "Create one, or import one from GitHub by its address. Push to it with git as usual.",
228+ to: workspace ? `/new?workspace=${workspace}` : "/new",
229+ action: "Add",
230+ },
231+ {
232+ done: shell?.creditMicros == null || shell.creditMicros > 0,
233+ title: "Add agent credit",
234+ about: "g1t's agents are paid for from the workspace's credit, at what the model costs plus 20%.",
235+ to: workspace ? `/${workspace}/-/billing` : null,
236+ action: "Add credit",
237+ },
238+ {
239+ done: handedOff,
240+ title: "Hand off an outcome",
241+ about: "Write what you want on a repository's Plan page. A planner splits it into issues, and agents take them and land them on main.",
242+ to: first ? `/${first.namespace}/${first.name}/plans` : null,
243+ action: "Write one",
244+ },
245+ ];
246+ const starting = steps.some((step) => !step.done);
247+
161248 const summary = [
162249 moving.length > 0 && `${moving.length} in progress`,
163250 ready.length > 0 && `${ready.length} ready to merge`,
183270 </div>
184271 </section>
185272
273+ {starting && <GetStarted steps={steps} />}
274+
186275 {needsYou.length > 0 && (
187276 <Section
188277 title={