appview: hide the current tag from the repository Diff dropdown

The Diff menu listed every tag including the one being viewed, and clicking
that entry did nothing: diffToTag returns early on to === currentTag, with no
navigation, no toast, no feedback.

Fixed in JS rather than in the template, which is the part that is easy to
get wrong. #diff-dropdown sits outside #tag-content, and the tag selector
block is marked "stays in DOM, never swapped" — so a {{ range }} filter would
be correct on first paint and stale after the first htmx tag swap, omitting
the originally loaded tag and re-including the newly current one. Same dead
entry, harder to see.

syncDiffMenu() reads the live value from #tag-selector and is called from
initTabs(), which already runs on load and again from the htmx:afterSettle
handler for #tag-content, so it stays correct across swaps.

Hidden rather than disabled: a disabled row still takes space and still reads
as an item to a screen reader, and "diff against the tag you are already on"
is meaningless rather than temporarily unavailable. Uses style.display to
match filterTags() in the same file, since daisyUI's .menu li rules outrank
Tailwind's .hidden.

The template guards the dropdown with {{ if gt (len .AllTags) 1 }} and tag
names are unique per repo (tags PK is did+repository+tag), so exactly one
entry is ever hidden and the menu can never end up empty. The early return in
diffToTag stays as a backstop.

Pre-existing, not a deploy regression. The baseline missed it because the
test repo had one tag, so the check skipped.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PDqoCE1j3njokkZ9b1C5n9
This commit is contained in:
Evan Jarrett
2026-09-02 21:02:50 -05:00
co-authored by Claude Opus 5
parent 1631898005
commit 3158460298
+29
View File
@@ -299,11 +299,38 @@ function initTabController() {
const selector = document.getElementById('tag-selector');
if (!content || !selector || !to) return;
const currentTag = selector.value;
// Backstop: syncDiffMenu() hides the current tag's entry, but keep the
// guard in case it's reached some other way (keyboard, stale DOM).
if (!currentTag || to === currentTag) return;
window.location.href = '/diff/' + content.dataset.owner + '/' + content.dataset.repo +
'?from=' + encodeURIComponent(currentTag) + '&to=' + encodeURIComponent(to);
};
// Hide the currently-selected tag from the "Diff" menu — diffing a tag
// against itself is a no-op (see the guard in diffToTag above), so the
// entry is dead weight in the menu.
//
// This has to live in JS, not the template: #diff-dropdown sits OUTSIDE
// #tag-content and is never re-rendered, so a template-side filter would
// be right on first paint and stale after the first htmx tag swap. Called
// from initTabs(), which runs on load and again on every tag swap.
//
// The template guards the whole dropdown with {{ if gt (len .AllTags) 1 }}
// and tags are unique per repo (tags PK is did+repository+tag), so hiding
// exactly one entry always leaves at least one behind — never an empty menu.
function syncDiffMenu() {
const selector = document.getElementById('tag-selector');
const menu = document.getElementById('diff-dropdown');
if (!selector || !menu) return;
const currentTag = selector.value;
menu.querySelectorAll('[data-action="diff-to"]').forEach(btn => {
const item = btn.closest('li') || btn;
// Inline style, matching filterTags() above: beats the daisyUI
// menu's own display rules without a specificity fight.
item.style.display = (btn.dataset.diffTo === currentTag) ? 'none' : '';
});
}
window.switchRepoTab = function(tabId) {
window._activeRepoTab = tabId;
const section = document.getElementById('tag-content');
@@ -338,6 +365,8 @@ function initTabController() {
function initTabs() {
loaded = {};
syncDiffMenu();
const prefetch = [
['artifacts-tab-btn', 'artifacts-content', tagsUrl],
['layers-tab-btn', 'layers-content', () => contentUrl('layers')],