From 6a044e1d09a4fc1f00c83d3e8424ea83f5349aa8 Mon Sep 17 00:00:00 2001 From: ChrisBeWithYou Date: Sat, 4 Jul 2026 21:54:55 -0500 Subject: [PATCH] =?UTF-8?q?feat(library):=20Match=E2=86=92Details=20"use?= =?UTF-8?q?=20these=20values"=20bridge?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Connects the popup's two tabs. A match only improves the underlying canon+art; by design it never silently re-titles the grid. This adds the explicit opt-in path: each Match candidate (search or Identify-by-audio) gets a "Use these values →" action that copies its title/artist/album/year into the Details tab as pending (unsaved) inputs and lands you there for review — pinning the match too so the art/canon follow. You then Save (overlay) or Write to file. Queue- review candidates are unchanged (they still accept/pin on click). Co-Authored-By: Claude Opus 4.8 (1M context) --- static/v3/match-review.js | 35 +++++++++++++++++++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/static/v3/match-review.js b/static/v3/match-review.js index edbc8e05..3a361666 100644 --- a/static/v3/match-review.js +++ b/static/v3/match-review.js @@ -303,6 +303,7 @@ '' + esc(pct) + '' + '' + esc(meta) + '' + diffChips(song, c) + + (_single ? 'Use these values →' : '') + ''; } @@ -483,7 +484,39 @@ }; } song._detailsState = st; + // Match→Details bridge: a candidate picked with "Use these values" lands + // its fields here as the pending (unsaved) input values, shown pre-filled + // for review — the grid never adopts a match silently, so the user still + // Saves (or Writes to file). + const adopted = song._pendingDetails; + if (adopted) { + for (const [f] of DETAIL_FIELDS) { + if (f in adopted) st[f].value = String(adopted[f] || ''); + } + song._pendingDetails = null; + } paintDetails(body, song); + if (adopted) { + const s = body.querySelector('[data-df-status]'); + if (s) { s.className = 'text-xs leading-relaxed text-fb-textDim'; s.textContent = 'Filled from the match — review, then Save or Write to file.'; } + } + } + + // Match→Details bridge: adopt a candidate's display fields into the Details + // tab (opt-in — never silent). Pin the match too so the art/canon follow, + // then land on Details pre-filled for review. + async function useTheseValues(song, cand) { + if (!cand) return; + song._pendingDetails = { + title: String(cand.title || ''), artist: String(cand.artist || ''), + album: String(cand.album || ''), year: String(cand.year || ''), + }; + try { + await post('/api/enrichment/review/' + enc(song.filename) + '/pick', { candidate: cand }); + } catch (_) { /* pin is best-effort; the values still populate Details */ } + try { window.feedBack?.emit('library:changed', { reason: 'match' }); } catch (_) { } + _tab = 'details'; + renderTabbed(); } function paintDetails(body, song) { @@ -705,6 +738,7 @@ btn.addEventListener('click', async () => { const cand = cands[Number(btn.getAttribute('data-mr-cand'))]; if (!cand) return; + if (_single) { useTheseValues(song, cand); return; } // popup → adopt into Details await post('/api/enrichment/review/' + enc(song.filename) + '/pick', { candidate: cand }); settle(song); @@ -757,6 +791,7 @@ btn.addEventListener('click', async () => { const cand = cands[Number(btn.getAttribute('data-mr-cand'))]; if (!cand) return; + if (_single) { useTheseValues(song, cand); return; } // popup → adopt into Details await post('/api/enrichment/review/' + enc(song.filename) + '/pick', { candidate: cand }); settle(song);