feat: Follow-up: add portrait orientation design for mobile that only triggers in portrait pwa mode

Task #43 completed by ClaudeQueue

ClaudeQueue
This commit is contained in:
Claude Worker
2026-06-30 08:01:54 +00:00
parent 02153d877d
commit 136e21f1fa
2 changed files with 71 additions and 0 deletions

View File

@@ -495,6 +495,9 @@ const Player = {
navigator.mediaSession.setActionHandler('seekbackward', (d) => Player.seek(Math.max(0, Player.master.currentTime - (d.seekOffset || 10))));
navigator.mediaSession.setActionHandler('seekforward', (d) => Player.seek(Player.master.currentTime + (d.seekOffset || 10)));
}
// In portrait PWA mode, scroll the player into view so it's immediately
// visible without the user having to swipe up manually.
scrollPlayerIntoViewPortrait();
// Restore A-B markers and load related (non-blocking)
restoreAbMarkers();
loadRelated();
@@ -1976,6 +1979,52 @@ function showModal(title, bodyNode, actions) {
}
function closeModal() { $('modal').classList.add('hidden'); }
// ============================================================================
// Portrait PWA detection & behavioral layer
// Mirrors the CSS media query: (display-mode: standalone) and (orientation: portrait)
// The JS layer adds:
// • A `portrait-pwa` class on .app for any JS-driven conditional logic.
// • Auto-close the sidebar when rotating into portrait-standalone mode.
// • Auto-scroll the player into view when a video starts in portrait mode.
// ============================================================================
const _portraitPwaMQ = window.matchMedia
? window.matchMedia('(display-mode: standalone) and (orientation: portrait)')
: { matches: false, addEventListener: () => {} };
function isPortraitPWA() {
return _portraitPwaMQ.matches;
}
function applyPortraitPwaClass() {
const app = document.querySelector('.app');
if (!app) return;
if (isPortraitPWA()) {
app.classList.add('portrait-pwa');
} else {
app.classList.remove('portrait-pwa');
}
}
// Scroll the player pane to the top of the scrollable .body so it's visible.
// Only fires when in portrait-standalone mode where .body is the scroll root.
function scrollPlayerIntoViewPortrait() {
if (!isPortraitPWA()) return;
const body = document.querySelector('.body');
if (body) {
body.scrollTo({ top: 0, behavior: 'smooth' });
}
}
function setupPortraitPwaWatcher() {
applyPortraitPwaClass();
_portraitPwaMQ.addEventListener('change', () => {
applyPortraitPwaClass();
// Rotating into portrait-standalone: close any open sidebar drawer so the
// full-width layout isn't obscured.
if (isPortraitPWA()) closeSidebar();
});
}
// ============================================================================
// Events
// ============================================================================
@@ -2623,6 +2672,7 @@ async function boot() {
wirePlayerEvents();
wireUI();
wireShortcutHelp();
setupPortraitPwaWatcher();
try {
const loaded = await API.loadData();
if (loaded && typeof loaded === 'object') {