add committer info to commit detail view
Shows committer name, email, and date when they differ from the author (e.g. rebased or cherry-picked commits). Escapes angle brackets in author/committer identity strings via escapeHtml. Fixes audio controls attribute type mismatch in FileBlob. Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
Msrc/index.tsx
@@ -23,8 +23,7 @@ for (const cmd of ["git", "ssh-keygen"]) {
await syncStartup();
if (!SSH_DISABLED) await startSshServer();
const _app = new Elysia({ serve: { maxRequestBodySize: MAX_UPLOAD_BYTES } })
new Elysia({ serve: { maxRequestBodySize: MAX_UPLOAD_BYTES } })
.use(
staticPlugin({
assets: path.resolve("./public"),
@@ -40,5 +39,4 @@ const _app = new Elysia({ serve: { maxRequestBodySize: MAX_UPLOAD_BYTES } })
.use(releasesRoutes)
.use(avatarRoutes)
.listen(PORT);
console.log(`Hearthforge running at http://localhost:${PORT}`);
Msrc/services/git.ts
@@ -115,6 +115,9 @@ export interface CommitMeta {
author: string;
email: string;
date: string;
committer: string;
committerEmail: string;
committerDate: string;
parents: string[];
}
@@ -433,7 +436,7 @@ export const git = {
const p = repoPath(name);
try {
const [metaOut, msgOut] = await Promise.all([
$`git -C ${p} show --no-patch --format=%H%x1f%an%x1f%ae%x1f%ai%x1f%P ${sha}`.text(),
$`git -C ${p} show --no-patch --format=%H%x1f%an%x1f%ae%x1f%ai%x1f%cn%x1f%ce%x1f%ci%x1f%P ${sha}`.text(),
$`git -C ${p} log --format=%B -1 ${sha}`.text(),
]);
const parts = metaOut.trim().split("\x1f");
@@ -454,7 +457,10 @@ export const git = {
author: parts[1] ?? "",
email: parts[2] ?? "",
date: parts[3] ?? "",
parents: (parts[4] ?? "").trim().split(/\s+/).filter(Boolean),
committer: parts[4] ?? "",
committerEmail: parts[5] ?? "",
committerDate: parts[6] ?? "",
parents: (parts[7] ?? "").trim().split(/\s+/).filter(Boolean),
};
} catch {
return null;
Msrc/views/patches/PatchDetail.tsx
@@ -178,7 +178,11 @@ export function PatchDetail({
</div>
<div class="patch-author-meta">
<span class="patch-author-label">git author:</span>
<span class="patch-author-identity">{escapeHtml(`${patch.author_name} <${patch.author_email}>`)}</span>
<span class="patch-author-identity">
{escapeHtml(
`${patch.author_name} <${patch.author_email}>`,
)}
</span>
</div>
</div>
Msrc/views/repos/CommitDetail.tsx
@@ -1,3 +1,4 @@
import { escapeHtml } from "@kitajs/html";
import type { RepositoryRow } from "../../db/index.ts";
import type { SessionUser } from "../../middleware/session.ts";
import type { RenderedDiffFile } from "../../services/diffHighlight.ts";
@@ -66,7 +67,7 @@ export function CommitDetail({
<div class="commit-card-meta-row">
<span class="commit-meta-label">Author</span>
<span class="commit-meta-value">
{meta.author} <{meta.email}>
{escapeHtml(`${meta.author} <${meta.email}>`)}
</span>
</div>
<div class="commit-card-meta-row">
@@ -78,6 +79,32 @@ export function CommitDetail({
{formatDate(meta.date)}
</time>
</div>
{(meta.committer !== meta.author ||
meta.committerEmail !== meta.email) && (
<>
<div class="commit-card-meta-row">
<span class="commit-meta-label">
Committer
</span>
<span class="commit-meta-value">
{escapeHtml(
`${meta.committer} <${meta.committerEmail}>`,
)}
</span>
</div>
<div class="commit-card-meta-row">
<span class="commit-meta-label">
Commit date
</span>
<time
class="commit-meta-value"
datetime={meta.committerDate}
>
{formatDate(meta.committerDate)}
</time>
</div>
</>
)}
<div class="commit-card-meta-row">
<span class="commit-meta-label">Commit</span>
<code class="commit-meta-value commit-sha-full mono">
Msrc/views/repos/FileBlob.tsx
@@ -89,7 +89,7 @@ export function FileBlob({
) : view.mimeType.startsWith("audio/") ? (
// biome-ignore lint/a11y/useMediaCaption: captions unavailable for arbitrary repo files
<audio
controls
controls="true"
src={`/${repo.name}/raw/${blobRef}/${filePath}`}
class="file-media-audio"
/>