Skip to content

Where people and agents ship software together. The open-source git platform for the whole job: issues, agents, checks and deploys to the edge.

g1t.sh
74 linesCodeBlameRaw
1/**
2 * Forking a repository: a button in the project's header beside Star, with
3 * how many forks it has, which leads to them. The button opens the fork
4 * page (routes/repo/fork.tsx); someone signed out is sent to sign in first.
5 * A private repository whose admins have not allowed forking shows it to
6 * its admins only, turned off, saying where to allow it.
7 */
8import { GitFork } from "lucide-react";
9import { Link } from "react-router";
10
11import { cn } from "../lib/cn";
12import { Hint } from "./ui/hint";
13
14const BOX = "inline-flex h-8 shrink-0 items-center overflow-hidden rounded-md border border-line text-[0.8125rem]";
15const FACE = "inline-flex h-full items-center gap-1.5 px-2.5 text-fg/80 outline-none hover:bg-surface hover:text-fg focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-accent";
16
17/** What the header knows about its forks: how many (at most 100 counted) and whether it can be forked. */
18export type Forking = {
19 /** Forks the viewer can see, up to 100. */
20 count: number;
21 /** Whether there are more than `count`. */
22 more: boolean;
23 /** Whether it may be forked: public, or private with forking allowed. */
24 allowed: boolean;
25};
26
27export function ForkButton({ base, name, forking, signedIn }: { base: string; name: string; forking: Forking; signedIn: boolean }) {
28 const shown = forking.more ? `${forking.count}+` : String(forking.count);
29 const words = `${shown} ${forking.count === 1 && !forking.more ? "fork" : "forks"}`;
30 const face = (
31 <>
32 <GitFork size={14} />
33 <span className="hidden sm:inline">Fork</span>
34 </>
35 );
36 const counter = (
37 <Hint label={words}>
38 <Link
39 to={`${base}/forks`}
40 aria-label={`${words}: who forked it`}
41 className="inline-flex h-full items-center border-l border-line px-2 font-medium tabular-nums text-fg/80 outline-none hover:bg-surface hover:text-accent focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-accent"
42 >
43 {shown}
44 </Link>
45 </Hint>
46 );
47 if (!forking.allowed) {
48 return (
49 <span className={BOX}>
50 <Hint label="Forking is off for this private repository. Allow it under Settings, Repository.">
51 <Link
52 to={`${base}/settings/repository#forking`}
53 aria-label={`Forking ${name} is off: allow it in Settings`}
54 className={cn(FACE, "text-faint hover:text-muted")}
55 >
56 {face}
57 </Link>
58 </Hint>
59 {counter}
60 </span>
61 );
62 }
63 const to = signedIn ? `${base}/fork` : `/login?next=${encodeURIComponent(`${base}/fork`)}`;
64 return (
65 <span className={BOX}>
66 <Hint label={`Fork ${name} into a workspace of yours`}>
67 <Link to={to} aria-label={signedIn ? `Fork ${name}` : `Sign in to fork ${name}`} className={FACE}>
68 {face}
69 </Link>
70 </Hint>
71 {counter}
72 </span>
73 );
74}