diff --git a/webui/index.html b/webui/index.html index d34431f1..3327f422 100644 --- a/webui/index.html +++ b/webui/index.html @@ -962,6 +962,16 @@ +
+ + +
diff --git a/webui/static/video/video-calendar.js b/webui/static/video/video-calendar.js index 4248a61c..a4a03d59 100644 --- a/webui/static/video/video-calendar.js +++ b/webui/static/video/video-calendar.js @@ -18,7 +18,7 @@ var MO = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec']; var MO_FULL = ['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December']; - var state = { loaded: false, eps: {}, data: null, offset: 0, filter: 'all' }; + var state = { loaded: false, eps: {}, data: null, offset: 0, filter: 'all', view: 'cards' }; function $(s) { return document.querySelector(s); } function esc(s) { @@ -88,7 +88,7 @@ : 'Anytime'; var flag = ep.has_file ? '' : ''; var meta = '' + se + '' + (epTitle ? '' + esc(epTitle) + '' : ''); - return '' + @@ -448,6 +448,10 @@ for (var i = 0; i < fbs.length; i++) (function (b) { b.addEventListener('click', function () { state.filter = b.getAttribute('data-video-cal-filter'); render(); }); })(fbs[i]); + var vbs = document.querySelectorAll('[data-video-cal-view]'); + for (var k = 0; k < vbs.length; k++) (function (b) { + b.addEventListener('click', function () { setView(b.getAttribute('data-video-cal-view')); }); + })(vbs[k]); var addBtn = $('[data-video-cal-addmissing]'); if (addBtn) addBtn.addEventListener('click', addMissing); @@ -587,6 +591,21 @@ .catch(function () { /* modal already shows payload data */ }); } + // View switcher (cards ↔ compact). Just toggles a class on the stable grid + // wrapper — no refetch/re-render needed — and remembers the choice. + function setView(v) { + state.view = v; + try { localStorage.setItem('vcalView', v); } catch (e) { /* private mode */ } + applyView(); + } + function applyView() { + var grid = $('[data-video-cal-grid]'); + if (grid) grid.classList.toggle('vcal-view--compact', state.view === 'compact'); + var vbs = document.querySelectorAll('[data-video-cal-view]'); + for (var i = 0; i < vbs.length; i++) + vbs[i].classList.toggle('vcal-view-btn--on', vbs[i].getAttribute('data-video-cal-view') === state.view); + } + function onPageShown(e) { if (!e || e.detail !== PAGE_ID) return; if (!state.loaded) { state.scrollToNow = true; load(); } @@ -595,6 +614,8 @@ function init() { wire(); + try { var sv = localStorage.getItem('vcalView'); if (sv) state.view = sv; } catch (e) { /* private mode */ } + applyView(); document.addEventListener('soulsync:video-page-shown', onPageShown); } diff --git a/webui/static/video/video-side.css b/webui/static/video/video-side.css index 05ff07f5..f829da88 100644 --- a/webui/static/video/video-side.css +++ b/webui/static/video/video-side.css @@ -1809,6 +1809,25 @@ body[data-side="video"] #soulsync-toggle { display: none; } padding: 7px 14px; border-radius: 8px; cursor: pointer; transition: all 0.15s ease; } .vcal-filter-btn:hover { color: #fff; background: rgba(255, 255, 255, 0.08); } .vcal-filter-btn--on { color: #fff; background: rgba(var(--vcal-accent), 0.9); box-shadow: 0 4px 14px rgba(var(--vcal-accent), 0.32); } +/* view switcher (cards ↔ compact) — mirrors the filter pill group, with icons */ +.vcal-views { display: inline-flex; gap: 3px; padding: 4px; border-radius: 12px; + background: rgba(255, 255, 255, 0.06); border: 1px solid rgba(255, 255, 255, 0.08); } +.vcal-view-btn { display: inline-flex; align-items: center; gap: 6px; border: 0; background: transparent; + color: rgba(255, 255, 255, 0.7); font-size: 12.5px; font-weight: 700; padding: 7px 13px; border-radius: 8px; + cursor: pointer; transition: all 0.15s ease; } +.vcal-view-btn svg { opacity: 0.85; } +.vcal-view-btn:hover { color: #fff; background: rgba(255, 255, 255, 0.08); } +.vcal-view-btn--on { color: #fff; background: rgba(var(--vcal-accent), 0.9); box-shadow: 0 4px 14px rgba(var(--vcal-accent), 0.32); } + +/* Compact view — drop the episode art to fit a lot more on screen */ +.vcal-grid.vcal-view--compact .vcal-art { display: none; } +.vcal-grid.vcal-view--compact .vcal-slot { gap: 8px; padding: 13px 10px; } +.vcal-grid.vcal-view--compact .vcal-card { border-radius: 10px; } +.vcal-grid.vcal-view--compact .vcal-info { padding: 10px 12px; } +.vcal-grid.vcal-view--compact .vcal-show { margin-top: 4px; } +/* the art carried the library ✓ — surface ownership as a left accent stripe instead */ +.vcal-grid.vcal-view--compact .vcal-cell--owned .vcal-card { box-shadow: inset 3px 0 0 #6cd391; } +.vcal-grid.vcal-view--compact .vcal-cell--owned .vcal-info { padding-left: 15px; } /* catch-up button: queue already-aired missing episodes to the wishlist */ .vcal-addmissing { display: inline-flex; align-items: center; gap: 7px; padding: 9px 16px; border-radius: 11px; border: 1px solid rgba(var(--accent-rgb, 88 101 242), 0.4); cursor: pointer; font-size: 12.5px; font-weight: 800;