Skip to content

Commit 5b2d50a

Browse files
authored
feat: linkify full commit SHAs in markdown (better-auth#244)
Converts full 40-character commit SHAs found in markdown content to clickable links pointing to the commit page (`/{owner}/{repo}/commit/{sha}`).
1 parent 6c73dc5 commit 5b2d50a

2 files changed

Lines changed: 40 additions & 0 deletions

File tree

apps/web/src/app/globals.css

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -488,6 +488,14 @@ tr.diff-search-match-active > td {
488488
white-space: nowrap;
489489
}
490490

491+
/* commit hashes */
492+
.ghmd a.ghmd-commit-ref {
493+
text-decoration: none;
494+
color: var(--primary);
495+
font-weight: 500;
496+
white-space: nowrap;
497+
}
498+
491499
/* Bold / italic */
492500
.ghmd strong {
493501
font-weight: 600;

apps/web/src/components/shared/markdown-renderer.tsx

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,33 @@ function linkifyIssueReferences(html: string, owner: string, repo: string): stri
248248
return parts.join("");
249249
}
250250

251+
/** Convert full commit SHAs (40 hex chars) to commit links */
252+
function linkifyCommits(html: string, owner: string, repo: string): string {
253+
const parts = html.split(/(<[^>]+>)/);
254+
let inCode = 0;
255+
let inLink = 0;
256+
257+
for (let i = 0; i < parts.length; i++) {
258+
const part = parts[i];
259+
if (part.startsWith("<")) {
260+
const lower = part.toLowerCase();
261+
if (lower.startsWith("<code") || lower.startsWith("<pre")) inCode++;
262+
else if (lower.startsWith("</code") || lower.startsWith("</pre")) inCode--;
263+
else if (lower.startsWith("<a ") || lower.startsWith("<a>")) inLink++;
264+
else if (lower.startsWith("</a")) inLink--;
265+
continue;
266+
}
267+
if (inCode > 0 || inLink > 0) continue;
268+
// Match 40-character commit SHA
269+
parts[i] = part.replace(
270+
/\b([a-f0-9]{40})\b/g,
271+
(sha) =>
272+
`<a href="/${owner}/${repo}/commit/${sha}" class="ghmd-commit-ref">${sha.slice(0, 7)}</a>`,
273+
);
274+
}
275+
return parts.join("");
276+
}
277+
251278
/** Convert @username mentions (outside of code/links) to profile links */
252279
function linkifyMentions(html: string): string {
253280
// Split on tags to avoid replacing inside <a>, <code>, <pre> content
@@ -508,6 +535,11 @@ export async function renderMarkdownToHtml(
508535
html = linkifyIssueReferences(html, refCtx.owner, refCtx.repo);
509536
}
510537

538+
// Linkify commit hashes when repo context is available
539+
if (repoContext) {
540+
html = linkifyCommits(html, repoContext.owner, repoContext.repo);
541+
}
542+
511543
return html;
512544
}
513545

0 commit comments

Comments
 (0)