Skip to content

Commit ba198fb

Browse files
committed
Flesh out/prettify source info
1 parent 5c9a438 commit ba198fb

File tree

9 files changed

+309
-123
lines changed

9 files changed

+309
-123
lines changed

src/app/[...parts]/_page/DiffIntro/Halfs.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ import { forwardRef, type HTMLAttributes, type ReactNode } from "react";
22
import { cx } from "^/lib/cva";
33

44
export interface HalfsProps extends HTMLAttributes<HTMLElement> {
5-
left: ReactNode;
5+
left?: ReactNode;
66
center?: ReactNode;
7-
right: ReactNode;
7+
right?: ReactNode;
88
}
99

1010
const Halfs = forwardRef<HTMLElement, HalfsProps>(

src/app/[...parts]/_page/Sources.tsx

Lines changed: 0 additions & 117 deletions
This file was deleted.
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
import {
2+
BadgeCheck,
3+
ExternalLink as ExternalLinkIcon,
4+
FileCode,
5+
ScrollText,
6+
} from "lucide-react";
7+
import ExternalLink from "^/components/ExternalLink";
8+
import Button from "^/components/ui/Button";
9+
import Tooltip from "^/components/ui/Tooltip";
10+
import { type SourceInformation } from "^/lib/api/npm/sourceInformation/sourceInformation";
11+
12+
export interface ProvenanceCardProps {
13+
sourceInformation: SourceInformation;
14+
}
15+
16+
export default function ProvenanceCard({
17+
sourceInformation,
18+
}: ProvenanceCardProps) {
19+
return (
20+
<div className="space-y-2 rounded-lg border border-border bg-muted p-3">
21+
<div className="flex items-start gap-3">
22+
<BadgeCheck className="mt-0.5 size-5 shrink-0 text-green-600" />
23+
<div className="flex-1 space-y-2">
24+
<div>
25+
<div className="text-sm font-medium">
26+
Built and signed on
27+
</div>
28+
<div className="text-sm text-muted-foreground">
29+
{sourceInformation.buildPlatform}
30+
</div>
31+
</div>
32+
<div className="flex gap-2">
33+
<Tooltip
34+
label={
35+
<div className="space-y-1">
36+
<div className="text-xs font-medium">
37+
Build File
38+
</div>
39+
<code className="rounded-sm bg-muted px-1.5 py-0.5 text-xs">
40+
{sourceInformation.buildFile}
41+
</code>
42+
</div>
43+
}
44+
>
45+
<Button asChild variant="default" size="xs">
46+
<ExternalLink
47+
href={`${sourceInformation.repository}/blob/${sourceInformation.commitHash}/${sourceInformation.buildFile}`}
48+
>
49+
<FileCode className="size-4" />
50+
</ExternalLink>
51+
</Button>
52+
</Tooltip>
53+
<Tooltip label="Transparency log entry">
54+
<Button asChild variant="default" size="xs">
55+
<ExternalLink
56+
href={sourceInformation.publicLedger}
57+
>
58+
<ScrollText className="size-4" />
59+
</ExternalLink>
60+
</Button>
61+
</Tooltip>
62+
<Tooltip label="View build summary">
63+
<Button asChild variant="default" size="xs">
64+
<ExternalLink
65+
href={sourceInformation.buildSummaryUrl}
66+
>
67+
<ExternalLinkIcon className="size-4" />
68+
</ExternalLink>
69+
</Button>
70+
</Tooltip>
71+
</div>
72+
</div>
73+
</div>
74+
</div>
75+
);
76+
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
import { GitCommit, Github } from "lucide-react";
2+
import ExternalLink from "^/components/ExternalLink";
3+
import Heading from "^/components/ui/Heading";
4+
import { type SourceInformation } from "^/lib/api/npm/sourceInformation/sourceInformation";
5+
import { cx } from "^/lib/cva";
6+
import ProvenanceCard from "./ProvenanceCard";
7+
8+
export interface SourceCardProps extends React.HTMLProps<HTMLDivElement> {
9+
sourceInformation: SourceInformation;
10+
}
11+
12+
export default function SourceCard({
13+
sourceInformation,
14+
className,
15+
...props
16+
}: SourceCardProps) {
17+
// Extract owner/repo from repository URL (e.g., https://github.com/owner/repo)
18+
const repoPath = sourceInformation.repository
19+
.replace(/^https?:\/\/(www\.)?github\.com\//, "")
20+
.replace(/\.git$/, "");
21+
22+
return (
23+
<div
24+
className={cx("rounded-xl border border-border", className)}
25+
{...props}
26+
>
27+
<div className="w-full border-b p-2">
28+
<Heading h={4} className="text-base">
29+
Source
30+
</Heading>
31+
</div>
32+
<div className="p-3">
33+
<div className="space-y-3">
34+
<ExternalLink
35+
href={sourceInformation.repository}
36+
className="flex items-center justify-between gap-2 rounded p-2 hover:bg-muted/50"
37+
>
38+
<span className="text-sm">Repo:</span>
39+
<div className="flex items-center gap-1.5">
40+
{sourceInformation.repository.includes(
41+
"github.com",
42+
) && <Github className="size-3.5 shrink-0" />}
43+
<span className="text-sm font-medium">
44+
{repoPath}
45+
</span>
46+
</div>
47+
</ExternalLink>
48+
<ExternalLink
49+
href={`${sourceInformation.repository}/tree/${sourceInformation.commitHash}`}
50+
className="flex items-center justify-between gap-2 rounded p-2 hover:bg-muted/50"
51+
>
52+
<span className="text-sm">Commit:</span>
53+
<div className="flex items-center gap-1.5">
54+
<GitCommit className="size-3.5 shrink-0" />
55+
<code className="rounded-sm bg-muted px-1.5 py-0.5 font-mono text-sm">
56+
{sourceInformation.commitHash.substring(0, 8)}
57+
</code>
58+
</div>
59+
</ExternalLink>
60+
<ProvenanceCard sourceInformation={sourceInformation} />
61+
</div>
62+
</div>
63+
</div>
64+
);
65+
}
Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
import { GitCompare } from "lucide-react";
2+
import { cacheLife } from "next/cache";
3+
import ExternalLink from "^/components/ExternalLink";
4+
import Button from "^/components/ui/Button";
5+
import Heading from "^/components/ui/Heading";
6+
import Skeleton from "^/components/ui/Skeleton";
7+
import Tooltip from "^/components/ui/Tooltip";
8+
import { getSourceInformation } from "^/lib/api/npm/sourceInformation";
9+
import type SimplePackageSpec from "^/lib/SimplePackageSpec";
10+
import suspense from "^/lib/suspense";
11+
import Halfs from "../DiffIntro/Halfs";
12+
import SourceCard from "./SourceCard";
13+
14+
export interface SourcesProps {
15+
a: SimplePackageSpec;
16+
b: SimplePackageSpec;
17+
}
18+
19+
function createGithubCompareHref(
20+
repository: string,
21+
commitA: string,
22+
commitB: string,
23+
) {
24+
return `${repository}/compare/${commitA}...${commitB}`;
25+
}
26+
27+
async function Sources({ a, b }: SourcesProps) {
28+
"use cache";
29+
30+
cacheLife("hours");
31+
32+
const [sourceA, sourceB] = await Promise.all([
33+
getSourceInformation(a),
34+
getSourceInformation(b),
35+
]);
36+
37+
if (!sourceA && !sourceB) {
38+
return null;
39+
}
40+
41+
return (
42+
<div>
43+
<Halfs
44+
className="items-center"
45+
left={
46+
sourceA ? <SourceCard sourceInformation={sourceA} /> : <></>
47+
}
48+
center={<span className="w-4"></span>}
49+
right={
50+
sourceB ? <SourceCard sourceInformation={sourceB} /> : <></>
51+
}
52+
/>
53+
<Halfs
54+
className="items-center"
55+
left={<></>}
56+
center={
57+
<span className="p-4">
58+
{sourceA &&
59+
sourceB &&
60+
sourceA.repository === sourceB.repository ? (
61+
<Tooltip
62+
label={
63+
<span className="flex items-center gap-1.5">
64+
<span>Compare</span>
65+
<code className="rounded-sm bg-muted px-1.5 py-0.5 font-mono text-xs">
66+
{sourceA.commitHash.substring(0, 8)}
67+
</code>
68+
<span>with</span>
69+
<code className="rounded-sm bg-muted px-1.5 py-0.5 font-mono text-xs">
70+
{sourceB.commitHash.substring(0, 8)}
71+
</code>{" "}
72+
on Github.com
73+
</span>
74+
}
75+
>
76+
<Button asChild variant="secondary">
77+
<ExternalLink
78+
href={createGithubCompareHref(
79+
sourceA.repository,
80+
sourceA.commitHash,
81+
sourceB.commitHash,
82+
)}
83+
>
84+
<GitCompare className="mr-2 size-4" />
85+
Compare Sources
86+
</ExternalLink>
87+
</Button>
88+
</Tooltip>
89+
) : (
90+
<></>
91+
)}
92+
</span>
93+
}
94+
right={<></>}
95+
/>
96+
</div>
97+
);
98+
}
99+
100+
function SourcesFallback() {
101+
return (
102+
<>
103+
<Heading h={3} className="text-lg">
104+
Sources
105+
</Heading>
106+
<Halfs
107+
className="items-center"
108+
left={<Skeleton className="h-5 w-16" />}
109+
center={
110+
<span className="p-4">
111+
<Skeleton className="h-8 w-20" />
112+
</span>
113+
}
114+
right={<Skeleton className="h-5 w-16" />}
115+
/>
116+
</>
117+
);
118+
}
119+
120+
const SuspendedSources = suspense(Sources, SourcesFallback);
121+
122+
export default SuspendedSources;
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
import Sources from "./Sources";
2+
3+
export default Sources;

0 commit comments

Comments
 (0)