[object Object]

← 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

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>'}
+        &nbsp;·&nbsp;
+        <a href="/p/${esc(req.params.slug)}">back to ${esc(p.name)}</a>
+        &nbsp;·&nbsp;
+        ${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 →