← back to build-pages
ui: commit-detail prev/next history nav — walk forward/backward through a project's commits + 1 smoke
a633cb58a013f68fe456214a6cc267f6040d70af · 2026-05-13 17:50:55 -0700 · SteveStudio2
Files touched
M server.jsM test/smoke.js
Diff
commit a633cb58a013f68fe456214a6cc267f6040d70af
Author: SteveStudio2 <stevestudio2@SteveStacStudio.lan>
Date: Wed May 13 17:50:55 2026 -0700
ui: commit-detail prev/next history nav — walk forward/backward through a project's commits + 1 smoke
---
server.js | 15 +++++++++++++++
test/smoke.js | 1 +
2 files changed, 16 insertions(+)
diff --git a/server.js b/server.js
index 8cd00ba..5feeed3 100644
--- a/server.js
+++ b/server.js
@@ -621,6 +621,13 @@ app.get('/p/:slug/c/:hash', async (req, res, next) => {
if (!p) return res.status(404).send(shell('Not found', '<h1>404</h1>'));
try {
const c = await commitDetail(p.repo, req.params.hash);
+ // Walk-the-history nav — find this commit's position in the cached log and
+ // expose prev/next (chronologically). Falls through gracefully if the
+ // hash isn't in the cached list (shouldn't happen, but defensive).
+ const bundle = await getProjectBundle(req.params.slug, p);
+ const cIdx = bundle.commits.findIndex(x => c.hash.startsWith(x.shortHash) || x.hash === c.hash);
+ const prevC = cIdx >= 0 && cIdx < bundle.commits.length - 1 ? bundle.commits[cIdx + 1] : null; // older
+ const nextC = cIdx > 0 ? bundle.commits[cIdx - 1] : null; // newer
const crumbs = {
'@context': 'https://schema.org',
'@type': 'BreadcrumbList',
@@ -640,6 +647,14 @@ app.get('/p/:slug/c/:hash', async (req, res, next) => {
<ul class="bp-files">${c.files.map(f => `<li><code>${esc(f)}</code></li>`).join('')}</ul>
<h2>Diff</h2>
<pre class="bp-diff">${highlightDiff(esc(c.diff))}</pre>
+
+ <p class="bp-meta">
+ ${prevC ? `<a href="/p/${esc(req.params.slug)}/c/${esc(prevC.shortHash)}">← ${esc(prevC.shortHash)} ${esc(prevC.subject.slice(0, 60))}</a>` : '<span>(oldest)</span>'}
+ ·
+ <a href="/p/${esc(req.params.slug)}">back to ${esc(p.name)}</a>
+ ·
+ ${nextC ? `<a href="/p/${esc(req.params.slug)}/c/${esc(nextC.shortHash)}">${esc(nextC.subject.slice(0, 60))} ${esc(nextC.shortHash)} →</a>` : '<span>(newest)</span>'}
+ </p>
`, {
canonical: `https://projects.agentabrams.com/p/${req.params.slug}/c/${req.params.hash}`,
desc: `${c.subject} — commit in ${p.name}`,
diff --git a/test/smoke.js b/test/smoke.js
index bbb3dea..66890b7 100644
--- a/test/smoke.js
+++ b/test/smoke.js
@@ -131,6 +131,7 @@ function check(name, cond, hint = '') {
check('commit: 200', r.status === 200);
check('commit: BreadcrumbList', /"@type":"BreadcrumbList"/.test(r.body));
check('commit: diff +/- highlighting', /bp-d-add|bp-d-rm/.test(r.body));
+ check('commit: prev/next nav', /back to /.test(r.body));
}
// 6c. perf budget — warm render of /p/butlr (cache warmed by the calls above)
← 0c0d61c ui: prev/next project nav on /p/:slug — wraps at registry en
·
back to build-pages
·
api/health: include loadavg {1m,5m,15m} for host-overload mo bae5063 →