Fork Horse repository, listed by its owner. Publisher snapshot.

packages/forkhorse-dashboard/src/empty.tsx

Snapshot at 34e3175

Containing directory

3,153 bytes · textView raw
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