thunderbird-patch-review
Simple email patch review tool for Thunderbird
git clone git://mccd.space/thunderbird-patch-reviewcommit d06b0c473b7b85908977d548d89ff6b5cd596d29
parent 9f8e985c2dbe55902916148a494d8b3a9c2d21db
Author: Pi Agent <agent@pi.local>
Date: Mon, 20 Jul 2026 11:08:46 +0200
Add Refresh: retry glyph on the Apply warning, Refresh… in the dropdown
When the apply probe reports a warning state (dirty tree or conflict),
the Apply button is now repurposed as a retry instead of sitting
disabled: the existing ✖/⚠ marker is followed by a 🗘 glyph and
clicking the button re-runs refreshApplyState() rather than applying,
so the user can re-check after committing/stashing or resolving the
conflict without re-picking the repository. Modify-and-apply still
applies, so it stays disabled in those states; the "applied" success
state stays disabled (not a warning). A new Refresh… item is added to
the apply dropdown and wired to the same probe, bypassing the
data-mode dispatch so it works in any state.
Diffstat:
2 files changed, 35 insertions(+), 10 deletions(-)
diff --git a/extension/review/review.html b/extension/review/review.html
@@ -17,6 +17,7 @@
<div class="menu" hidden>
<button data-mode="modify">Modify and apply</button>
<button data-mode="download">Download patchset…</button>
+ <button data-bypass="1" id="btn-refresh">Refresh…</button>
</div>
</div>
</div>
diff --git a/extension/review/review.js b/extension/review/review.js
@@ -566,6 +566,14 @@ function setupSplit(splitId, onPick) {
setupSplit("send-split", (mode) => openSendConfirm(mode));
setupSplit("apply-split", applySeries);
+// Refresh re-runs the apply-state probe without applying. It bypasses
+// setupSplit's data-mode dispatch (no data-mode) and is wired here
+// directly so it works in any state, including the disabled-button
+// warning states where the user has just fixed the cause.
+$("#btn-refresh").addEventListener("click", () => {
+ closeMenus();
+ refreshApplyState();
+});
$("#btn-send").addEventListener("click", () => openSendConfirm("send"));
$("#btn-send-cancel").addEventListener("click", closeSendConfirm);
@@ -665,30 +673,46 @@ async function refreshApplyState() {
if (modifyBtn) modifyBtn.disabled = true;
} else if (state.applyStatus === "conflict") {
apply.classList.add("state-conflict");
- apply.disabled = true;
- apply.textContent = "✖ Apply";
+ // Keep the button enabled: in a warning state its click re-runs the
+ // probe (see the btn-apply handler) instead of applying, so the user
+ // can retry after fixing the cause without re-picking the repo. The
+ // 🗘 glyph signals that. Modify still applies, so leave it disabled.
+ apply.disabled = false;
+ apply.textContent = "✖ Apply \u{1F5D9}";
apply.title =
"This series does not apply cleanly to the repository.\n\n" +
- (response.output || "(no detail)");
+ (response.output || "(no detail)") +
+ "\n\nClick to re-check after resolving the conflict.";
if (modifyBtn) modifyBtn.disabled = true;
} else if (state.applyStatus === "dirty") {
// The working tree has uncommitted tracked changes, so git am will
// refuse before even reading the patches. Warn in yellow — distinct
// from conflict (the patch content doesn't fit HEAD): this is a
- // local, fixable condition. Leave Apply and Modify disabled; the
- // tooltip tells the user to commit or stash and the next probe
- // (after Browse re-pick or a successful apply elsewhere) re-checks.
+ // local, fixable condition. Leave the button enabled as a retry
+ // (click re-checks); Modify still applies, so it stays disabled.
apply.classList.add("state-dirty");
- apply.disabled = true;
- apply.textContent = "⚠ Apply";
+ apply.disabled = false;
+ apply.textContent = "⚠ Apply \u{1F5D9}";
apply.title =
"The repository has uncommitted changes; git am will refuse until you commit or stash them.\n\n" +
- (response.output || "(no detail)");
+ (response.output || "(no detail)") +
+ "\n\nClick to re-check after committing or stashing.";
if (modifyBtn) modifyBtn.disabled = true;
}
}
-$("#btn-apply").addEventListener("click", () => applySeries("apply"));
+// In a warning state (dirty/conflict) the Apply button is repurposed as a
+// retry: clicking it re-runs the probe instead of applying, so the user can
+// refresh after fixing the cause (commit/stash, resolve the conflict) without
+// re-picking the repo. Otherwise it applies as normal. refreshApplyState()
+// is also reachable from the dropdown's "Refresh…" item.
+$("#btn-apply").addEventListener("click", () => {
+ if (state.applyStatus === "dirty" || state.applyStatus === "conflict") {
+ refreshApplyState();
+ return;
+ }
+ applySeries("apply");
+});
document.addEventListener("click", (e) => {
if (!e.target.closest(".split")) {