From 3158460298cba505b84f86e10589b6796cafd3f8 Mon Sep 17 00:00:00 2001 From: Evan Jarrett Date: Wed, 2 Sep 2026 21:02:50 -0500 Subject: [PATCH] appview: hide the current tag from the repository Diff dropdown MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) Claude-Session: https://claude.ai/code/session_01PDqoCE1j3njokkZ9b1C5n9 --- pkg/appview/src/js/repository.js | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/pkg/appview/src/js/repository.js b/pkg/appview/src/js/repository.js index 23f9548..a2d9934 100644 --- a/pkg/appview/src/js/repository.js +++ b/pkg/appview/src/js/repository.js @@ -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')],