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

packages/forkhorse-dashboard/src/lane.tsx

Snapshot at 34e3175

Containing directory

3,898 bytes ยท textView raw
1import { Tooltip } from "@cloudflare/kumo";
2import type { CSSProperties } from "react";
3import type { ProjectState } from "./contracts.ts";
4
5export interface AgentIdentity {
6  number: number;
7  silk: 1 | 2 | 3;
8}
9/** Enrollment rank, never response/claim/race order. Legacy agents precede dated agents. */
10export function agentIdentities(agents: ProjectState["agents"]): Map<string, AgentIdentity> {
11  const sorted = agents.toSorted((a, b) => {
12    if (!a.createdAt && b.createdAt) return -1;
13    if (a.createdAt && !b.createdAt) return 1;
14    return (
15      (Date.parse(a.createdAt ?? "") || 0) - (Date.parse(b.createdAt ?? "") || 0) ||
16      a.name.localeCompare(b.name)
17    );
18  });
19  return new Map(
20    sorted.map((agent, index) => [
21      agent.name,
22      {
23        number: index + 1,
24        silk: ((index % 3) + 1) as 1 | 2 | 3,
25      },
26    ]),
27  );
28}
29export function silkStyle(identity?: AgentIdentity): CSSProperties {
30  return {
31    "--lane-silk": identity ? `var(--race-silk-${identity.silk})` : "var(--text-color-kumo-subtle)",
32  } as CSSProperties;
33}
34export function Saddlecloth({ identity }: { identity?: AgentIdentity }) {
35  // Unknown agents get no invented rank. Numbers always accompany a known silk.
36  return (
37    <span
38      className="race-saddlecloth race-cloth"
39      style={silkStyle(identity)}
40    >
41      <span aria-hidden="true">{identity?.number ?? "โ€”"}</span>
42      <span className="sr-only">{identity ? `Agent number ${identity.number}` : "Agent number unavailable"}</span>
43    </span>
44  );
45}
46export type LaneMarkKind =
47  | "claim"
48  | "push"
49  | "review"
50  | "passed"
51  | "failed"
52  | "interrupted"
53  | "approved"
54  | "promoted";
55export const markGlyph: Record<LaneMarkKind, string> = {
56  claim: "โ—",
57  push: "โ—",
58  review: "โ—‹",
59  passed: "โœ“",
60  failed: "โœ—",
61  interrupted: "!",
62  approved: "โ—†",
63  promoted: "โ—",
64};
65export function LaneMarker({ kind }: { kind: LaneMarkKind | "unreached" | "reached" }) {
66  const hurdle = kind === "passed" || kind === "failed" || kind === "interrupted";
67  const reached = kind !== "unreached" && kind !== "failed" && kind !== "interrupted";
68  return (
69    <span className={`lane-marker lane-marker-${kind} race-marker ${reached ? "reached" : ""} ${kind === "promoted" ? "promoted" : ""} ${hurdle ? `hurdle ${kind}` : ""}`} aria-hidden="true">
70      {kind === "unreached" ? "โ—‹" : kind === "reached" ? "โ—" : markGlyph[kind]}
71    </span>
72  );
73}
74
75/** FH-055-compatible API, shared with the fork graph. */
76export function saddlecloth(state: ProjectState, agent: string): number | undefined {
77  return agentIdentities(state.agents).get(agent)?.number;
78}
79export function laneSilkStyle(state: ProjectState, agent: string): CSSProperties {
80  const style = silkStyle(agentIdentities(state.agents).get(agent));
81  return {
82    ...style,
83    "--entrant-silk": style["--lane-silk" as keyof CSSProperties],
84  } as CSSProperties;
85}
86export function LaneRunner({
87  entrant,
88  state,
89}: {
90  entrant: { agent: string; repoName: string };
91  state: ProjectState;
92}) {
93  return (
94    <span className="race-runner" style={laneSilkStyle(state, entrant.agent)}>
95      <Saddlecloth identity={agentIdentities(state.agents).get(entrant.agent)} />
96      <span className="race-runner-copy">
97        <strong>{entrant.agent}</strong>
98        <Tooltip content={entrant.repoName} render={<code className="caption race-fork" />}>
99          {entrant.repoName}
100        </Tooltip>
101      </span>
102    </span>
103  );
104}
105
106/** Decorative SVG lane path; callers supply only proven endpoints. */
107export function LaneLine({ path }: { path: string }) {
108  return <path className="lane-line" d={path} aria-hidden="true" />;
109}
110/** Table-based race connector: only consecutive recorded milestones may connect. */
111export function LaneConnector({ reached }: { reached: boolean }) {
112  return reached ? <span className="lane-connector" aria-hidden="true" /> : null;
113}
114