Wishlist TV: roomier episode cards + full-width episode area + polish
From the expanded-show screenshot: episodes were crammed into a 320px tile and
titles truncated. Reworked (all still scoped under .vwsh-nebula):
- Season tiles are now SELECTORS. Picking one drops its episodes full-width below
the fan as 2-line cards: a bigger 16:9 still, the title wrapping to two lines,
and a meta line (status dot · S/E · air date). Single-select; click again to
close. Responsive grid, scrolls past ~360px.
- Softer count badge ('59 ep' muted pill instead of the loud number).
- Selected tile glows in the show hue; square bubbles bumped up so posters breathe.
Music wishlist untouched (verified: no bare .wl-* rules in video CSS).
This commit is contained in:
parent
e5cd5cf08c
commit
4d62df5c33
2 changed files with 84 additions and 35 deletions
|
|
@ -2513,15 +2513,14 @@ body[data-side="video"] #soulsync-toggle { display: none; }
|
|||
color: #fff; padding: 2px 7px; border-radius: 6px; letter-spacing: 0.02em; backdrop-filter: blur(4px);
|
||||
background: rgba(0, 0, 0, 0.58); border: 1px solid rgba(255, 255, 255, 0.18); text-shadow: 0 1px 3px rgba(0, 0, 0, 0.7); }
|
||||
|
||||
/* #3 richer episode tracks — status dot + air date */
|
||||
.vwsh-nebula .wl-tile-track { gap: 8px; }
|
||||
.vwsh-nebula .vwsh-ep-dot { flex-shrink: 0; width: 7px; height: 7px; border-radius: 50%;
|
||||
/* episode status dot (used in the episode-card meta line) */
|
||||
.vwsh-nebula .vwsh-ep-dot { display: inline-block; vertical-align: middle; margin-right: 6px;
|
||||
width: 7px; height: 7px; border-radius: 50%;
|
||||
background: rgb(var(--accent-rgb, 29, 185, 84)); box-shadow: 0 0 6px rgba(var(--accent-rgb, 29, 185, 84), 0.6); }
|
||||
.vwsh-nebula .vwsh-ep-dot--searching { background: #f59e0b; box-shadow: 0 0 6px rgba(245, 158, 11, 0.6); }
|
||||
.vwsh-nebula .vwsh-ep-dot--downloading { background: #8ab0ff; box-shadow: 0 0 6px rgba(100, 149, 237, 0.6); }
|
||||
.vwsh-nebula .vwsh-ep-dot--downloaded { background: #4ade80; box-shadow: 0 0 6px rgba(34, 197, 94, 0.6); }
|
||||
.vwsh-nebula .vwsh-ep-dot--failed { background: #f87171; box-shadow: 0 0 6px rgba(248, 113, 113, 0.6); }
|
||||
.vwsh-nebula .vwsh-ep-date { flex-shrink: 0; font-size: 9.5px; font-weight: 600; color: rgba(255, 255, 255, 0.35); }
|
||||
|
||||
/* #5 sort select (toolbar) */
|
||||
.vwsh-sort { padding: 11px 14px; border-radius: 12px; background: rgba(255, 255, 255, 0.05);
|
||||
|
|
@ -2541,15 +2540,43 @@ body[data-side="video"] #soulsync-toggle { display: none; }
|
|||
rgba(255, 255, 255, 0.16) calc(var(--vwsh-prog, 0) * 100%));
|
||||
box-shadow: 0 1px 4px rgba(0, 0, 0, 0.5); }
|
||||
|
||||
/* richer episode rows: still thumbnail + roomier expanded tile */
|
||||
.vwsh-nebula .wl-album-tile.tile-expanded { width: 320px; }
|
||||
.vwsh-nebula .wl-tile-track { gap: 8px; padding: 5px 8px; }
|
||||
.vwsh-nebula .vwsh-ep-thumb { flex-shrink: 0; width: 52px; height: 30px; border-radius: 4px; overflow: hidden;
|
||||
/* bigger square bubbles so the posters breathe */
|
||||
.vwsh-nebula .wl-orb.orb-sm { width: 84px; height: 84px; }
|
||||
.vwsh-nebula .wl-orb.orb-md { width: 104px; height: 104px; }
|
||||
.vwsh-nebula .wl-orb.orb-lg { width: 124px; height: 124px; }
|
||||
|
||||
/* softer count badge (the big number read like a rating) */
|
||||
.vwsh-nebula .wl-album-tile-badge { font-size: 9px; font-weight: 800; letter-spacing: 0.02em; padding: 2px 6px;
|
||||
background: rgba(0, 0, 0, 0.55); color: rgba(255, 255, 255, 0.85); border: 1px solid rgba(255, 255, 255, 0.14);
|
||||
backdrop-filter: blur(4px); }
|
||||
/* selected season tile glows in the show hue */
|
||||
.vwsh-nebula .wl-album-tile.vwsh-tile--sel { border: 1px solid hsla(var(--orb-hue, 230), 70%, 60%, 0.6);
|
||||
box-shadow: 0 0 0 1px hsla(var(--orb-hue, 230), 70%, 55%, 0.4), 0 8px 22px -10px hsla(var(--orb-hue, 230), 70%, 45%, 0.6); }
|
||||
|
||||
/* #1/#2 — the picked season drops its episodes here, full-width, as 2-line cards */
|
||||
.vwsh-nebula .vwsh-ep-area:empty { display: none; }
|
||||
.vwsh-nebula .vwsh-ep-area { margin-top: 14px; }
|
||||
.vwsh-nebula .vwsh-ep-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 10px;
|
||||
max-height: 360px; overflow-y: auto; padding: 2px; scrollbar-width: thin; }
|
||||
.vwsh-nebula .vwsh-epc { position: relative; display: flex; align-items: center; gap: 11px; padding: 8px;
|
||||
border-radius: 10px; background: rgba(255, 255, 255, 0.04); border: 1px solid rgba(255, 255, 255, 0.06);
|
||||
transition: background 0.15s ease, border-color 0.15s ease; }
|
||||
.vwsh-nebula .vwsh-epc:hover { background: rgba(255, 255, 255, 0.07); border-color: rgba(255, 255, 255, 0.12); }
|
||||
.vwsh-nebula .vwsh-epc-thumb { flex-shrink: 0; width: 84px; height: 47px; border-radius: 6px; overflow: hidden;
|
||||
background: #16161d; border: 1px solid rgba(255, 255, 255, 0.08); }
|
||||
.vwsh-nebula .vwsh-ep-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||||
.vwsh-nebula .vwsh-ep-thumb--none { background: repeating-linear-gradient(135deg,
|
||||
.vwsh-nebula .vwsh-epc-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||||
.vwsh-nebula .vwsh-epc-thumb--none { background: repeating-linear-gradient(135deg,
|
||||
rgba(255, 255, 255, 0.04) 0 6px, rgba(255, 255, 255, 0.07) 6px 12px); }
|
||||
.vwsh-nebula .vwsh-ep-thumb--none img { display: none; }
|
||||
.vwsh-nebula .vwsh-epc-thumb--none img { display: none; }
|
||||
.vwsh-nebula .vwsh-epc-body { flex: 1; min-width: 0; }
|
||||
.vwsh-nebula .vwsh-epc-title { font-size: 12.5px; font-weight: 700; color: #fff; line-height: 1.3;
|
||||
display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden; }
|
||||
.vwsh-nebula .vwsh-epc-meta { margin-top: 3px; font-size: 11px; font-weight: 600; color: rgba(255, 255, 255, 0.42); }
|
||||
.vwsh-nebula .vwsh-epc-rm { flex-shrink: 0; width: 22px; height: 22px; border-radius: 50%; border: none;
|
||||
background: rgba(0, 0, 0, 0.35); color: rgba(255, 255, 255, 0.45); font-size: 12px; cursor: pointer;
|
||||
display: flex; align-items: center; justify-content: center; opacity: 0; transition: all 0.15s ease; }
|
||||
.vwsh-nebula .vwsh-epc:hover .vwsh-epc-rm { opacity: 1; }
|
||||
.vwsh-nebula .vwsh-epc-rm:hover { background: rgba(239, 68, 68, 0.85); color: #fff; }
|
||||
|
||||
/* status pills + remove button */
|
||||
.vwsh-st { font-size: 10px; font-weight: 800; text-transform: uppercase; letter-spacing: 0.03em; padding: 3px 8px; border-radius: 6px; flex-shrink: 0; }
|
||||
|
|
|
|||
|
|
@ -77,38 +77,21 @@
|
|||
? '<img class="wl-orb-img" src="' + esc(sh.poster_url) + '" alt="" ' +
|
||||
'onerror="this.outerHTML=\'<div class="wl-orb-initials">' + esc(initials(sh.title)) + '</div>\'">'
|
||||
: '<div class="wl-orb-initials">' + esc(initials(sh.title)) + '</div>';
|
||||
// Season tiles are SELECTORS now — picking one renders its episodes full-
|
||||
// width below (so episode cards get room), instead of cramming inside.
|
||||
var tiles = (sh.seasons || []).map(function (se) {
|
||||
var n = se.episodes.length;
|
||||
// real season poster (falls back to the show poster, then a placeholder)
|
||||
var posterUrl = se.poster_url || sh.poster_url || null;
|
||||
var inner = posterUrl ? '<img src="' + esc(posterUrl) + '" alt="">' : '<div class="wl-album-tile-fallback">📺</div>';
|
||||
var art = '<div class="wl-album-tile-art">' + inner + '<span class="vwsh-season-tag">S' + se.season_number + '</span></div>';
|
||||
var tracks = (se.episodes || []).map(function (e) {
|
||||
var t = e.title || ('Episode ' + e.episode_number);
|
||||
var st = STATUS[e.status] ? e.status : 'wanted';
|
||||
var date = fmtDate(e.air_date);
|
||||
// richer episode row: still thumbnail + status dot + air date
|
||||
var thumb = e.still_url
|
||||
? '<span class="vwsh-ep-thumb"><img src="' + esc(e.still_url) + '" alt="" loading="lazy" ' +
|
||||
'onerror="this.parentNode.classList.add(\'vwsh-ep-thumb--none\')"></span>'
|
||||
: '<span class="vwsh-ep-thumb vwsh-ep-thumb--none"></span>';
|
||||
return '<div class="wl-tile-track">' + thumb +
|
||||
'<span class="vwsh-ep-dot vwsh-ep-dot--' + st + '" title="' + STATUS[st][0] + '"></span>' +
|
||||
'<span class="wl-tile-track-name">E' + e.episode_number + ' · ' + esc(t) + '</span>' +
|
||||
(date ? '<span class="vwsh-ep-date">' + esc(date) + '</span>' : '') +
|
||||
'<button class="wl-tile-track-remove" type="button" data-vwsh-rm="episode" ' +
|
||||
'data-tmdb="' + esc(sh.tmdb_id) + '" data-s="' + se.season_number + '" data-e="' + e.episode_number + '" title="Remove">✕</button>' +
|
||||
'</div>';
|
||||
}).join('');
|
||||
return '<div class="wl-album-tile" data-vwsh-tile>' + art +
|
||||
return '<div class="wl-album-tile" data-vwsh-tile data-tmdb="' + esc(sh.tmdb_id) + '" data-vwsh-season="' + se.season_number + '">' +
|
||||
'<div class="wl-album-tile-art">' + inner + '<span class="vwsh-season-tag">S' + se.season_number + '</span></div>' +
|
||||
'<div class="wl-album-tile-info">' +
|
||||
'<div class="wl-album-tile-name">Season ' + se.season_number + '</div>' +
|
||||
'<div class="wl-album-tile-count">' + n + ' episode' + (n === 1 ? '' : 's') + '</div>' +
|
||||
'</div>' +
|
||||
'<span class="wl-album-tile-badge">' + n + '</span>' +
|
||||
'<span class="wl-album-tile-badge">' + n + ' ep</span>' +
|
||||
'<button class="wl-album-tile-remove" type="button" data-vwsh-rm="season" ' +
|
||||
'data-tmdb="' + esc(sh.tmdb_id) + '" data-s="' + se.season_number + '" title="Remove season">✕</button>' +
|
||||
'<div class="wl-tile-tracks">' + tracks + '</div>' +
|
||||
'</div>';
|
||||
}).join('');
|
||||
var eps = total + ' episode' + (total === 1 ? '' : 's');
|
||||
|
|
@ -126,16 +109,47 @@
|
|||
'</div>' +
|
||||
'<div class="wl-orb-label" data-vwsh-open-show data-vwsh-src="' + src + '" data-vwsh-id="' + esc(openId) + '" title="' + esc(sh.title) + '">' + esc(sh.title) + '</div>' +
|
||||
'<div class="wl-orb-meta">' + eps + (sh.done ? ' · ' + sh.done + ' done' : '') + '</div>' +
|
||||
'<div class="wl-orb-expanded"><div class="wl-album-fan">' + tiles + '</div></div>' +
|
||||
'<div class="wl-orb-expanded"><div class="wl-album-fan">' + tiles + '</div>' +
|
||||
'<div class="vwsh-ep-area" data-vwsh-ep-area></div></div>' +
|
||||
'</div>';
|
||||
}
|
||||
|
||||
// A single episode as a 2-line card (still + title that can wrap + meta line).
|
||||
function epCard(tmdb, se, e) {
|
||||
var t = e.title || ('Episode ' + e.episode_number);
|
||||
var st = STATUS[e.status] ? e.status : 'wanted';
|
||||
var date = fmtDate(e.air_date);
|
||||
var thumb = e.still_url
|
||||
? '<span class="vwsh-epc-thumb"><img src="' + esc(e.still_url) + '" alt="" loading="lazy" ' +
|
||||
'onerror="this.parentNode.classList.add(\'vwsh-epc-thumb--none\')"></span>'
|
||||
: '<span class="vwsh-epc-thumb vwsh-epc-thumb--none"></span>';
|
||||
return '<div class="vwsh-epc">' + thumb +
|
||||
'<div class="vwsh-epc-body">' +
|
||||
'<div class="vwsh-epc-title" title="' + esc(t) + '">' + esc(t) + '</div>' +
|
||||
'<div class="vwsh-epc-meta"><span class="vwsh-ep-dot vwsh-ep-dot--' + st + '"></span>' +
|
||||
'S' + se.season_number + '·E' + e.episode_number + (date ? ' · ' + esc(date) : '') + '</div>' +
|
||||
'</div>' +
|
||||
'<button class="vwsh-epc-rm" type="button" data-vwsh-rm="episode" ' +
|
||||
'data-tmdb="' + esc(tmdb) + '" data-s="' + se.season_number + '" data-e="' + e.episode_number + '" title="Remove">✕</button>' +
|
||||
'</div>';
|
||||
}
|
||||
function renderEpisodeArea(group, tmdb, seasonNum) {
|
||||
var area = group && group.querySelector('[data-vwsh-ep-area]'); if (!area) return;
|
||||
var sh = state.showData[tmdb]; var se = null;
|
||||
if (sh) (sh.seasons || []).forEach(function (x) { if (x.season_number === seasonNum) se = x; });
|
||||
if (!se) { area.innerHTML = ''; return; }
|
||||
area.innerHTML = '<div class="vwsh-ep-grid">' +
|
||||
(se.episodes || []).map(function (e) { return epCard(tmdb, se, e); }).join('') + '</div>';
|
||||
}
|
||||
|
||||
function render(items) {
|
||||
var grid = $('[data-vwsh-grid]'); if (!grid) return;
|
||||
var shows = state.tab === 'show';
|
||||
grid.classList.toggle('wl-nebula-field', shows);
|
||||
grid.classList.toggle('vwsh-nebula', shows); // video-only scope so music wl-* is untouched
|
||||
grid.classList.toggle('vwsh-grid--movies', !shows);
|
||||
state.showData = {};
|
||||
if (shows) items.forEach(function (sh) { state.showData[sh.tmdb_id] = sh; }); // for the episode area
|
||||
grid.innerHTML = shows
|
||||
? items.map(function (sh, i) { return nebulaOrb(sh, i); }).join('')
|
||||
: items.map(movieCard).join('');
|
||||
|
|
@ -270,7 +284,15 @@
|
|||
return;
|
||||
}
|
||||
var tile = e.target.closest('[data-vwsh-tile]');
|
||||
if (tile) { tile.classList.toggle('tile-expanded'); return; } // season → episodes
|
||||
if (tile) { // season → render its episodes full-width below (single-select)
|
||||
var group = tile.closest('.wl-orb-group');
|
||||
var wasSel = tile.classList.contains('vwsh-tile--sel');
|
||||
if (group) { var all = group.querySelectorAll('.wl-album-tile'); for (var i = 0; i < all.length; i++) all[i].classList.remove('vwsh-tile--sel'); }
|
||||
if (wasSel) { var a = group && group.querySelector('[data-vwsh-ep-area]'); if (a) a.innerHTML = ''; return; }
|
||||
tile.classList.add('vwsh-tile--sel');
|
||||
renderEpisodeArea(group, parseInt(tile.getAttribute('data-tmdb'), 10), parseInt(tile.getAttribute('data-vwsh-season'), 10));
|
||||
return;
|
||||
}
|
||||
var orb = e.target.closest('[data-vwsh-orb]');
|
||||
if (orb) { var g = orb.closest('.wl-orb-group'); if (g) g.classList.toggle('expanded'); } // show → seasons
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue