thunderbird-patch-review

Easy-to-use patch review interface for Thunderbird

Contribute: ~marcc/thunderbird-review-plugin@lists.sr.ht

git clone git://mccd.space/thunderbird-patch-review

commit a23ea54eb255049183779f8e4bcf828a9a4103ba
parent 515034fc73b1427f171e18c30be1703088172a85
Author: Pi Agent <agent@pi.local>
Date:   Sat, 18 Jul 2026 22:23:56 +0200

review: put the patch selector (1/2 etc.) in the top bar, drop the title

Replace the top-left "series title" text with the #series-nav pill row:\nthe per-patch tabs (cover, 1/2, 2/2, badge counts) now live in the top\nbar instead of inside the patch box. Drop the in-box nav, so the patch\nbox is just the commit message + comments + file diffs; patch selection\nis global, across the whole series, and belongs with the toolbar.

Diffstat:
Mextension/review/review.css | 26++++++++------------------
Mextension/review/review.html | 2+-
Mextension/review/review.js | 18++++++------------
3 files changed, 15 insertions(+), 31 deletions(-)
diff --git a/extension/review/review.css b/extension/review/review.css
@@ -55,11 +55,11 @@ body {
   z-index: 2;
 }
 
-#series-title {
-  font-weight: 600;
-  overflow: hidden;
-  text-overflow: ellipsis;
-  white-space: nowrap;
+#series-nav {
+  display: flex;
+  align-items: center;
+  gap: 0.4rem;
+  flex-wrap: wrap;
 }
 
 #actions {
@@ -201,17 +201,7 @@ button.primary:hover { color: #fff; opacity: 0.9; }
   overflow: hidden;
 }
 
-.series-nav {
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  gap: 0.5rem;
-  flex-wrap: wrap;
-  padding: 0.4rem 0.6rem;
-  border-bottom: 1px solid var(--border);
-}
-
-.series-nav .pill {
+#series-nav .pill {
   padding: 0.2rem 0.7rem;
   border: 1px solid var(--border);
   border-radius: 999px;
@@ -219,13 +209,13 @@ button.primary:hover { color: #fff; opacity: 0.9; }
   color: var(--muted);
 }
 
-.series-nav .pill.active {
+#series-nav .pill.active {
   border-color: var(--accent);
   color: var(--accent);
   font-weight: 600;
 }
 
-.series-nav .pill .badge {
+#series-nav .pill .badge {
   display: inline-block;
   min-width: 1.2em;
   margin-left: 0.35em;
diff --git a/extension/review/review.html b/extension/review/review.html
@@ -7,7 +7,7 @@
 </head>
 <body>
   <header id="topbar">
-    <div id="series-title">Loading…</div>
+    <nav id="series-nav" aria-label="Patch series"></nav>
     <div id="actions">
       <div class="cluster" id="repo-cluster">
         <button id="repo-path" type="button" title="Click to choose the repository directory">no repository chosen</button>
diff --git a/extension/review/review.js b/extension/review/review.js
@@ -31,7 +31,6 @@ async function init() {
   const messageId = Number(new URLSearchParams(location.search).get("mid"));
   const response = await bg({ type: "get-series", messageId });
   if (!response.ok) {
-    $("#series-title").textContent = "Failed to load series";
     showStatus(response.error || "unknown error", { error: true });
     return;
   }
@@ -45,9 +44,9 @@ async function init() {
   state.current = Math.max(0, state.series.findIndex((p) => !p.isCover));
 
   const anchor = state.series.find((p) => !p.isCover) || state.series[0];
-  const m = anchor.m ? ` (${state.series.filter((p) => !p.isCover).length}/${anchor.m} patches)` : "";
-  $("#series-title").textContent = anchor.title + m;
   document.title = `Review: ${anchor.title}`;
+  state.navEl = $("#series-nav");
+  renderNav();
   refreshRepoDisplay(state.repo);
   state.slug =
     anchor.title.replace(/[^A-Za-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 60) ||
@@ -107,17 +106,11 @@ function renderPatch() {
   const patch = state.series[i];
   const parsed = state.parsed[i];
 
-  // The patch list and the commit message share one box so the series
-  // selector reads as a header to the message it selects.
+  // The patch box holds the commit message (and per-patch comment + file
+  // diffs); the patch selector itself lives in the top bar (#series-nav).
   const box = document.createElement("div");
   box.className = "patch-box";
 
-  const nav = document.createElement("nav");
-  nav.className = "series-nav";
-  box.appendChild(nav);
-  state.navEl = nav;
-  renderNav();
-
   // Commit message (or cover letter text) with a whole-patch comment. Never
   // fall back to the raw body for a patch — that would print the whole diff
   // above the interactive one (common case: single-line commit messages,
@@ -747,6 +740,7 @@ function hideStatus() {
 $("#btn-status-close").addEventListener("click", hideStatus);
 
 init().catch((e) => {
-  $("#series-title").textContent = "Error";
+  document.title = "Review: error";
+  if (state.navEl) state.navEl.textContent = "";
   showStatus(e.message || String(e), { error: true });
 });