CommitDetail.tsx
Raw
1import { escapeHtml } from "@kitajs/html";
2import type { RepositoryRow } from "../../db/index.ts";
3import { formatDateTime } from "../../lib/formatDate.ts";
4import type { SessionUser } from "../../middleware/session.ts";
5import type { RenderedDiffFile } from "../../services/diffHighlight.ts";
6import type { CommitMeta } from "../../services/git.ts";
7import { DiffView } from "../DiffView.tsx";
8import { Layout } from "../layout.tsx";
9import { RepoHeader } from "../repos/RepoHeader.tsx";
10import { RepoNav } from "./RepoNav.tsx";
11
12interface CommitDetailProps {
13 user: SessionUser | null;
14 repo: RepositoryRow;
15 sha: string;
16 meta: CommitMeta;
17 files: RenderedDiffFile[];
18 tooLarge?: number;
19}
20
21export function CommitDetail({
22 user,
23 repo,
24 sha,
25 meta,
26 files,
27 tooLarge,
28}: CommitDetailProps) {
29 return (
30 <Layout user={user} title={`${sha.slice(0, 7)} — ${repo.name}`}>
31 <div class="container">
32 <RepoHeader repo={repo} />
33 <RepoNav repo={repo} active="commits" user={user} />
34
35 {/* Back link */}
36 <div class="commit-page-top">
37 <a
38 href={`/${repo.name}/commits/${repo.default_branch}`}
39 class="btn btn-sm"
40 >
41 ← Back to log
42 </a>
43 <a
44 href={`/${repo.name}/tree/${sha}`}
45 class="btn btn-secondary btn-sm"
46 title={`Browse tree at ${sha.slice(0, 7)}`}
47 >
48 @ {sha.slice(0, 7)}
49 </a>
50 </div>
51
52 {/* Commit metadata card */}
53 <div class="commit-card">
54 <h2
55 class={`commit-card-subject${meta.subject ? "" : " commit-card-subject-empty"}`}
56 >
57 {meta.subject || "No commit message"}
58 </h2>
59 {meta.body && (
60 <pre class="commit-card-body">{meta.body}</pre>
61 )}
62 <div class="commit-card-meta">
63 <div class="commit-card-meta-row">
64 <span class="commit-meta-label">Author</span>
65 <span class="commit-meta-value">
66 {escapeHtml(`${meta.author} <${meta.email}>`)}
67 </span>
68 </div>
69 <div class="commit-card-meta-row">
70 <span class="commit-meta-label">Date</span>
71 <time
72 class="commit-meta-value"
73 datetime={meta.date}
74 >
75 {formatDateTime(meta.date)}
76 </time>
77 </div>
78 {(meta.committer !== meta.author ||
79 meta.committerEmail !== meta.email) && (
80 <>
81 <div class="commit-card-meta-row">
82 <span class="commit-meta-label">
83 Committer
84 </span>
85 <span class="commit-meta-value">
86 {escapeHtml(
87 `${meta.committer} <${meta.committerEmail}>`,
88 )}
89 </span>
90 </div>
91 <div class="commit-card-meta-row">
92 <span class="commit-meta-label">
93 Commit date
94 </span>
95 <time
96 class="commit-meta-value"
97 datetime={meta.committerDate}
98 >
99 {formatDateTime(meta.committerDate)}
100 </time>
101 </div>
102 </>
103 )}
104 <div class="commit-card-meta-row">
105 <span class="commit-meta-label">Commit</span>
106 <code class="commit-meta-value commit-sha-full mono">
107 {meta.hash}
108 </code>
109 </div>
110 {meta.parents.length > 0 && (
111 <div class="commit-card-meta-row">
112 <span class="commit-meta-label">
113 Parent{meta.parents.length > 1 ? "s" : ""}
114 </span>
115 <span class="commit-meta-value">
116 {meta.parents.map((p) => (
117 <a
118 href={`/${repo.name}/commit/${p}`}
119 class="commit-hash mono"
120 >
121 {p.slice(0, 7)}
122 </a>
123 ))}
124 </span>
125 </div>
126 )}
127 {meta.sigStatus !== "none" && (
128 <div class="commit-card-meta-row">
129 <span class="commit-meta-label">Signature</span>
130 <span class="commit-meta-value">
131 {meta.sigStatus === "good" ? (
132 <span class="sig-badge verified">
133 verified
134 </span>
135 ) : (
136 <span class="sig-badge unverified">
137 unverified
138 </span>
139 )}
140 </span>
141 </div>
142 )}
143 </div>
144 </div>
145
146 {tooLarge !== undefined ? (
147 <div class="file-download-notice">
148 <p>
149 Diff is too large to render inline (
150 {(tooLarge / 1024 / 1024).toFixed(1)} MB). Browse
151 individual files at the tree below.
152 </p>
153 </div>
154 ) : (
155 <DiffView files={files} repo={repo} sha={sha} />
156 )}
157 </div>
158 </Layout>
159 );
160}
161