feat: cinema mode — full-width video, Up Next + Download beside comments

New Cinema toggle alongside Fresh/Reverse/Autoplay. When on, adds
.cinema-mode class to section.two-column, restructuring the grid:

  row 1: video (full viewport width, max 85vh, preserved aspect)
  row 2: description (full width)
  row 3: product-right (price + download + Up Next) | comments

Uses display:contents on .watch-left so watch-mode children bubble
up as direct grid items. Video sizing follows CLAUDE.md media rule
(width:auto + max-width:100% + max-height:85vh) to avoid letterbox
whitespace.

Preference persisted in localStorage (watchCinemaMode). Toggle
handler attached in rebindToggles; class applied on init and on
every toggle change.
This commit is contained in:
russell@unturf.com 2026-04-21 17:36:53 -04:00
parent 78378f78d6
commit ee1d642d92
3 changed files with 100 additions and 0 deletions

View file

@ -1792,6 +1792,45 @@ textarea.markup-editor-textarea {
align-self: start;
}
/* Cinema mode: video spans viewport, Up Next + Download row under
video, side-by-side with comments. Unwraps .watch-left via
display:contents so its children become direct grid items. */
section.two-column.cinema-mode {
max-width: 100%;
padding-left: 20px;
padding-right: 20px;
grid-template-columns: 1fr 1fr;
grid-template-areas:
"images images"
"description description"
"purchase comments";
gap: 24px;
align-items: start;
}
section.two-column.cinema-mode .watch-left {
display: contents;
}
section.two-column.cinema-mode .product-images { grid-area: images; }
section.two-column.cinema-mode .product-description { grid-area: description; }
section.two-column.cinema-mode .product-comments { grid-area: comments; }
section.two-column.cinema-mode section.product-right { grid-area: purchase; }
/* Cinema: video/cover fills horizontal space, preserves aspect ratio.
Follows CLAUDE.md media rule: auto width + max-width + max-height. */
section.two-column.cinema-mode .product-main,
section.two-column.cinema-mode .watch-video-container > video,
section.two-column.cinema-mode .watch-audio-container > .product-main {
max-width: 100%;
max-height: 85vh;
width: auto;
height: auto;
display: block;
margin-left: auto;
margin-right: auto;
}
/* if we have room, break markup-editor into 2 columns */
div.markup-editor {
display: grid;
@ -3011,6 +3050,28 @@ textarea {
color: var(--text-muted, #999);
}
.watch-cinema-label {
display: grid;
grid-template-columns: auto auto;
gap: 6px;
align-items: center;
font-size: 0.85em;
cursor: pointer;
}
.watch-cinema-label input {
display: none;
}
.watch-cinema-label input:checked + .autoplay-slider {
background: var(--blue-color, #98b6fa);
}
.watch-cinema-label input:checked + .autoplay-slider::after {
transform: translateX(16px);
}
.cinema-label-text {
font-size: 0.85em;
color: var(--text-muted, #999);
}
/* Media type filter bar */
.ring-filter-controls {
display: grid;

View file

@ -156,6 +156,24 @@
if (savedFresh !== null) freshMode = savedFresh === '1';
} catch (e) {}
// Cinema mode preference (full-width video, restructured page layout)
var cinemaMode = false;
try {
var savedCinema = localStorage.getItem('watchCinemaMode');
if (savedCinema !== null) cinemaMode = savedCinema === '1';
} catch (e) {}
function applyCinemaMode() {
var section = document.querySelector('section.two-column');
if (!section) return;
if (cinemaMode) {
section.classList.add('cinema-mode');
} else {
section.classList.remove('cinema-mode');
}
}
applyCinemaMode();
// Media type filter preferences (all on by default)
var FILTER_TYPES = ['video', 'audio', 'image', 'document', 'other'];
var FILTER_LABELS = {video: 'Video', audio: 'Audio', image: 'Image', document: 'Docs', other: 'Other'};
@ -943,6 +961,7 @@
var autoplayChecked = autoplayEnabled ? ' checked' : '';
var directionChecked = ringDirection === -1 ? ' checked' : '';
var freshChecked = freshMode ? ' checked' : '';
var cinemaChecked = cinemaMode ? ' checked' : '';
var modBadge = isMod ? '<span class="ring-mod-badge">mod</span>' : '';
var loopsBadge = ringLoops > 0
? '<span id="ring-loops-badge" class="ring-loops-badge">+' + ringLoops + '</span>'
@ -970,6 +989,11 @@
+ '<input type="checkbox" id="watch-autoplay-toggle"' + autoplayChecked + ' />'
+ '<span class="autoplay-slider"></span>'
+ '</label>'
+ '<label class="watch-cinema-label js-only">'
+ '<span class="cinema-label-text">Cinema</span>'
+ '<input type="checkbox" id="watch-cinema-toggle"' + cinemaChecked + ' />'
+ '<span class="autoplay-slider"></span>'
+ '</label>'
+ '<button id="karaoke-toggle-btn" class="ring-karaoke-btn mps-button js-only" style="display:' + (karaokeUrls.instrumentals || karaokeUrls.vocals || karaokeEligible ? '' : 'none') + '" title="Karaoke"></button>'
+ '<button id="skip-prev-btn" class="skip-next-btn mps-button js-only" title="Go to previous item">&#9664; Prev</button>'
+ '<button id="skip-random-btn" class="skip-next-btn mps-button js-only" title="Jump to random item">&#127922;</button>'
@ -1094,6 +1118,16 @@
});
}
var cinemaToggle = document.getElementById('watch-cinema-toggle');
if (cinemaToggle) {
cinemaToggle.checked = cinemaMode;
cinemaToggle.addEventListener('change', function() {
cinemaMode = this.checked;
try { localStorage.setItem('watchCinemaMode', cinemaMode ? '1' : '0'); } catch (e) {}
applyCinemaMode();
});
}
// Media type filter buttons
var filterBtns = document.querySelectorAll('.ring-filter-btn');
for (var fi = 0; fi < filterBtns.length; fi++) {

View file

@ -29,6 +29,11 @@
<input type="checkbox" id="watch-autoplay-toggle" checked />
<span class="autoplay-slider"></span>
</label>
<label class="watch-cinema-label js-only">
<span class="cinema-label-text">Cinema</span>
<input type="checkbox" id="watch-cinema-toggle" />
<span class="autoplay-slider"></span>
</label>
<button id="karaoke-toggle-btn" class="ring-karaoke-btn mps-button js-only" style="display:none" title="Karaoke: play instrumental version">&#127908;</button>
<button id="skip-prev-btn" class="skip-next-btn mps-button js-only" title="Go to previous item">&#9664; Prev</button>
<button id="skip-random-btn" class="skip-next-btn mps-button js-only" title="Jump to random item">&#127922;</button>