+ Wish: use the app-standard watchlist-button chrome (was bespoke)

The per-video wishlist toggle now reuses .library-artist-watchlist-btn (icon +
text, accent gradient) via a shared ytWishBtn() helper — compact in the dense
episode rows, icon-only in the tight playlist-section cards. 'Wishlist' ↔ 'In
Wishlist' with a green .watching state. Same behavior + data-vd-yt-wish hook;
just consistent chrome.
This commit is contained in:
BoulderBadgeDad 2026-06-17 21:02:08 -07:00
parent 113a5ded38
commit abb9d0e89e
2 changed files with 28 additions and 12 deletions

View file

@ -980,12 +980,23 @@
esc(ep.title || 'Untitled') + '</span>' +
(meta.length ? '<span class="vd-ep-rt">' + esc(meta.join(' · ')) + '</span>' : '') + '</div>' +
(ep.overview ? '<p class="vd-ep-desc">' + esc(ep.overview) + '</p>' : '') + '</div>' +
'<button class="vd-yt-wish' + (wished ? ' vd-yt-wish--on' : '') + '" type="button" data-vd-yt-wish="' +
esc(ep.youtube_id) + '">' + (wished ? '✓ Wished' : '+ Wish') + '</button>' +
ytWishBtn(ep.youtube_id, wished, false) +
'<span class="vd-ep-chev" aria-hidden="true">⌄</span></div>' +
'<div class="vd-ep-extra" data-vd-ep-panel="' + key + '" hidden></div>';
}
// The per-video wishlist toggle — the app-standard watchlist-button chrome
// (icon + text, accent gradient) so it stops looking bespoke. iconOnly trims
// the label for the tight playlist-section cards.
function ytWishBtn(id, wished, iconOnly) {
return '<button class="library-artist-watchlist-btn vd-yt-wishbtn' +
(iconOnly ? ' vd-yt-wishbtn--icon' : '') + (wished ? ' watching' : '') +
'" type="button" data-vd-yt-wish="' + esc(id) + '">' +
'<span class="watchlist-icon">' + (wished ? '✓' : '') + '</span>' +
(iconOnly ? '' : '<span class="watchlist-text">' + (wished ? 'In Wishlist' : 'Wishlist') + '</span>') +
'</button>';
}
function episodeRow(ep) {
if (data && data.source === 'youtube') return ytEpisodeRow(ep);
var owned = ep.owned ? 'vd-ep--owned' : 'vd-ep--missing';
@ -1611,8 +1622,9 @@
if (ytVideoMap[id]) ytVideoMap[id].wished = val;
var r0 = root(), btns = r0 ? r0.querySelectorAll('[data-vd-yt-wish="' + id + '"]') : [];
for (var i = 0; i < btns.length; i++) {
btns[i].classList.toggle('vd-yt-wish--on', val);
btns[i].textContent = val ? '✓ Wished' : '+ Wish';
btns[i].classList.toggle('watching', val);
var ic = btns[i].querySelector('.watchlist-icon'); if (ic) ic.textContent = val ? '✓' : '';
var tx = btns[i].querySelector('.watchlist-text'); if (tx) tx.textContent = val ? 'In Wishlist' : 'Wishlist';
}
var ep = ytFindEp(id); if (ep) { ep.owned = val; renderSeasonNav(); }
document.dispatchEvent(new CustomEvent('soulsync:video-wishlist-changed'));
@ -1713,8 +1725,7 @@
'<a class="vd-yt-plvid-thumb" href="https://www.youtube.com/watch?v=' + esc(v.youtube_id) +
'" target="_blank" rel="noopener" data-vd-ext>' + thumb + '<span class="vd-yt-plvid-play">▶</span></a>' +
'<div class="vd-yt-plvid-title" title="' + esc(v.title) + '">' + esc(v.title || 'Untitled') + '</div>' +
'<button class="vd-yt-wish' + (v.wished ? ' vd-yt-wish--on' : '') + '" type="button" data-vd-yt-wish="' +
esc(v.youtube_id) + '">' + (v.wished ? '✓' : '+') + '</button></div>';
ytWishBtn(v.youtube_id, v.wished, true) + '</div>';
}
function toggleYtPlaylist(el) {
var pid = el.getAttribute('data-vd-yt-pl-toggle');

View file

@ -2885,11 +2885,16 @@ body[data-side="video"] #soulsync-toggle { display: none; }
/* YouTube episode rows have no index/badge → 4 columns (thumb · info · wish · chev) */
.vd-ep--yt { grid-template-columns: 150px 1fr auto auto; }
/* per-video Wish button inside an episode row */
.vd-yt-wish { flex-shrink: 0; align-self: center; border: 1px solid rgba(255,255,255,0.14); cursor: pointer;
background: rgba(255,255,255,0.05); color: rgba(255,255,255,0.78); font-size: 11.5px; font-weight: 800;
padding: 6px 13px; border-radius: 999px; transition: all 0.15s ease; white-space: nowrap; }
.vd-yt-wish:hover { background: rgba(255,59,59,0.18); border-color: rgba(255,59,59,0.5); color: #fff; }
.vd-yt-wish--on { background: rgba(108,211,145,0.16); border-color: rgba(108,211,145,0.4); color: #6cd391; }
/* Per-video wishlist toggle = the standard watchlist button (.library-artist-
watchlist-btn), shrunk for the dense rows; .watching "on the wishlist" green. */
.vd-yt-wishbtn { flex-shrink: 0; align-self: center; padding: 5px 12px; font-size: 11.5px; white-space: nowrap; }
.vd-yt-wishbtn--icon { padding: 5px 9px; }
.vd-yt-wishbtn.watching { color: #6cd391;
background: linear-gradient(135deg, rgba(108,211,145,0.2) 0%, rgba(108,211,145,0.07) 100%);
border-color: rgba(108,211,145,0.4); }
.vd-yt-wishbtn.watching:hover:not(:disabled) {
background: linear-gradient(135deg, rgba(108,211,145,0.3) 0%, rgba(108,211,145,0.12) 100%);
box-shadow: 0 4px 16px rgba(108,211,145,0.2); }
/* playlist video chips */
.vd-yt-plvid { display: flex; flex-direction: column; }
.vd-yt-plvid-thumb { position: relative; display: block; aspect-ratio: 16/9; border-radius: 9px; overflow: hidden; background: #16161d; }
@ -2898,7 +2903,7 @@ body[data-side="video"] #soulsync-toggle { display: none; }
.vd-yt-plvid-thumb:hover .vd-yt-plvid-play { opacity: 0.9; }
.vd-yt-plvid-title { font-size: 12px; font-weight: 700; color: #fff; line-height: 1.3; margin-top: 7px;
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
.vd-yt-plvid .vd-yt-wish { align-self: flex-start; margin-top: 7px; }
.vd-yt-plvid .vd-yt-wishbtn { align-self: flex-start; margin-top: 7px; }
/* YouTube search-results loading state (skeleton cards + "searching…" label) */
.vsr-yt-loading { margin-left: 10px; font-size: 12px; font-weight: 600; color: #ff8a8a; opacity: 0.85; }