Skip to content
77 linesCodeBlameRaw
1import { Code2, MessagesSquare } from "lucide-react";
2import { Link, data } from "react-router";
3
4import type { Route } from "./+types/code-access";
5import { page } from "../../lib/meta";
6import { identity } from "../../lib/services.server";
7import { requireUser, roleIn } from "../../lib/session.server";
8
9export function meta({ params, ...args }: Route.MetaArgs) {
10 return page(args, { title: `Code access · ${params.owner} · g1t` });
11}
12
13/**
14 * What a member without Code access sees in place of a repository, an
15 * issue, a pull request or a project list: what it was, and who to ask.
16 */
17export async function loader({ params, context, request }: Route.LoaderArgs) {
18 const viewer = requireUser(context, request);
19 if (!roleIn(viewer, params.owner)) throw data(null, { status: 404 });
20 const members = await identity.listMembers(params.owner, viewer).catch(() => null);
21 const owners = members?.ok ? members.value.filter((m) => m.role === "owner").slice(0, 4).map((m) => m.username) : [];
22 const from = new URL(request.url).searchParams.get("from");
23 // Only a path on this site, shown as text.
24 return { owners, from: from && /^\/[^/]/.test(from) ? from.split("?")[0] : null };
25}
26
27export default function CodeAccess({ loaderData, params }: Route.ComponentProps) {
28 const { owners, from } = loaderData;
29 const what = from ? from.split("/").filter(Boolean).slice(1).join("/") : null;
30 return (
31 <div className="mx-auto max-w-lg py-10 text-center">
32 <span className="mx-auto flex size-12 items-center justify-center rounded-xl border border-line bg-surface text-muted">
33 <Code2 size={22} />
34 </span>
35 <h1 className="mt-5 text-2xl font-semibold tracking-tight">Ask an owner for Code access</h1>
36 <p className="mt-2 text-[0.9375rem] leading-relaxed text-muted">
37 {what ? (
38 <>
39 <span className="font-mono text-fg-soft">{what}</span> is part of Code in {params.owner}.
40 </>
41 ) : (
42 <>This is part of Code in {params.owner}.</>
43 )}{" "}
44 Your membership includes Chat, Artifacts, Agents and Notifications. An owner can turn on Code access for you.
45 </p>
46 {owners.length > 0 && (
47 <p className="mt-4 text-sm text-muted">
48 Owners:{" "}
49 {owners.map((owner, index) => (
50 <span key={owner}>
51 {index > 0 && ", "}
52 <Link to={`/u/${owner}`} className="font-medium text-fg hover:text-accent">
53 @{owner}
54 </Link>
55 </span>
56 ))}
57 </p>
58 )}
59 <div className="mt-8 flex justify-center gap-2">
60 <Link
61 to={`/${params.owner}/-/chat`}
62 className="inline-flex h-9 items-center gap-2 rounded-md bg-accent px-3.5 text-sm font-medium text-bg transition-colors hover:bg-accent-hover"
63 >
64 <MessagesSquare size={15} />
65 Back to Chat
66 </Link>
67 <Link
68 to={`/${params.owner}/-/today`}
69 className="inline-flex h-9 items-center rounded-md border border-line px-3.5 text-sm font-medium text-fg/90 transition-colors hover:border-line-strong hover:bg-surface"
70 >
71 Today
72 </Link>
73 </div>
74 <p className="mt-6 text-xs text-faint">Agents can still explain how things work and what changed; they never show source code.</p>
75 </div>
76 );
77}