Fork Horse repository, listed by its owner. Publisher snapshot.
packages/forkhorse-dashboard/src/empty.tsx
Snapshot at 34e3175
1import { Button, ClipboardText, Empty } from "@cloudflare/kumo";
2import brand from "../../../brand.json" with { type: "json" };
3import type { ProjectState } from "./contracts.ts";
4
5type FirstRunKind = "projects" | "agents" | "packets" | "forks" | "reviews";
6const firstRun: Record<FirstRunKind, { title: string; description: string; command?: string }> = {
7 projects: {
8 title: "No projects yet",
9 description: "Create a project with the owner CLI. It sets up the canonical repo and enrolls your first agents.",
10 command: `${brand.slug} project create <project> --config "$CLI_CONFIG" --agents <agent> --output "$PRIVATE_TOKEN_FILE"`,
11 },
12 agents: {
13 title: "No agents yet",
14 description: "Enroll an agent. Each agent works in its own fork.",
15 command: `${brand.slug} agent enroll <agent> --config "$CLI_CONFIG" --output "$NEW_PRIVATE_TOKEN_FILE"`,
16 },
17 packets: {
18 title: "No packets yet",
19 description: "A packet gives agents a goal, allowed paths and checks. To race up to three agents on one packet, add mode: race to the packet file.",
20 command: `${brand.slug} packet create packet.yaml --config "$CLI_CONFIG"`,
21 },
22 forks: {
23 title: "No forks yet",
24 description: "Forks appear here when an agent claims a packet.",
25 },
26 reviews: {
27 title: "No reviews yet",
28 description: "When an agent requests review of a commit, it appears here for you to approve and promote.",
29 },
30};
31
32/** Retained forks, promotions and race entries are history, even without a current claim. */
33export function overviewFirstRun(state: ProjectState): FirstRunKind | undefined {
34 if (!state.agents.length) return "agents";
35 if (!state.packets.length && !state.ledger.length &&
36 !state.recentActivity?.some((push) => state.forks.some((fork) => fork.repoName === push.repoName)))
37 return "packets";
38 if (!state.claims.some((claim) => claim.active) && !state.forks.length &&
39 !state.ledger.length && !state.races?.some((race) => race.entrants.length)) return "forks";
40 return undefined;
41}
42
43/** Read-only owner guidance. Copying a placeholder command never performs an owner write. */
44export function FirstRunEmpty({ kind, owner, goToWork }: {
45 kind: FirstRunKind;
46 owner: boolean;
47 goToWork?(): void;
48}) {
49 const hint = firstRun[kind];
50 const description = owner || kind === "forks" ? hint.description
51 : kind === "reviews"
52 ? "When an agent requests review of a commit, it appears here for the owner to approve and promote."
53 : `${hint.description} ${kind === "packets" ? "The owner creates packets." : "The owner enrolls agents."}`;
54 return (
55 <div data-first-run={kind}>
56 <Empty
57 size="sm"
58 title={hint.title}
59 description={description}
60 contents={owner && (hint.command ? (
61 <div className="first-run-command">
62 <code>{hint.command}</code>
63 <ClipboardText text="" textToCopy={hint.command} size="sm" className="first-run-copy" />
64 </div>
65 ) : kind === "reviews" && goToWork ? (
66 <Button variant="ghost" size="sm" onClick={goToWork}>Go to Work</Button>
67 ) : undefined)}
68 />
69 </div>
70 );
71}
72