From 69a3a88b85ed0a56bf442af4fec420c871d337fa Mon Sep 17 00:00:00 2001 From: Paul Buetow Date: Tue, 5 May 2026 23:07:35 +0300 Subject: Add fullscreen crop mode toggle (hotkey c) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - In fullscreen, press 'c' to toggle object-fit between contain (default) and fill (stretches to fill, no aspect ratio preservation). - Adds .player.crop-mode video { object-fit: fill; } in player.css. - Adds toggleCrop() in player.js that toggles a crop-mode class and a small UI indicator (✂️) in the controls, visible only in fullscreen. - Resets crop mode back to contain when exiting fullscreen (via Escape, minimize, or fullscreenchange event). - Wires the 'c' hotkey in keyboard.js and registers toggleCrop in app.js. - Adds crop-indicator to index.html and detach.html controls, and documents the shortcut in the help modal. --- web/js/player.js | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) (limited to 'web/js/player.js') diff --git a/web/js/player.js b/web/js/player.js index 6da8954..fea4a19 100644 --- a/web/js/player.js +++ b/web/js/player.js @@ -21,6 +21,7 @@ let isPanning = false; let panStart = { x: 0, y: 0 }; let slideshowTimer = null; let slideshowPausedUntil = 0; +let cropMode = false; const els = () => ({ video: document.getElementById('media-video'), @@ -47,6 +48,7 @@ const els = () => ({ volume: document.getElementById('volume-slider'), timeElapsed: document.getElementById('time-elapsed'), timeTotal: document.getElementById('time-total'), + cropIndicator: document.getElementById('crop-indicator'), }); export function initPlayer(options = {}) { @@ -134,6 +136,16 @@ export function initPlayer(options = {}) { e.video.addEventListener('loadedmetadata', updateFloatingSize); + document.addEventListener('fullscreenchange', () => { + const p = e.player; + if (!p) return; + if (!document.fullscreenElement) { + p.classList.remove('crop-mode'); + cropMode = false; + if (e.cropIndicator) e.cropIndicator.classList.add('hidden'); + } + }); + function setupVideoDebug(m) { const events = ['loadstart','loadeddata','loadedmetadata','canplay','canplaythrough','playing','waiting','stalled','suspend','error','abort','emptied','ended']; events.forEach(event => { @@ -521,10 +533,23 @@ function updateMinimizedTitle() { export function exitFullscreenIfNeeded() { if (document.fullscreenElement) { document.exitFullscreen().catch(() => {}); - els().player?.classList.remove('is-fullscreen'); + const p = els().player; + if (p) p.classList.remove('is-fullscreen', 'crop-mode'); + cropMode = false; + const ind = els().cropIndicator; + if (ind) ind.classList.add('hidden'); } } +export function toggleCrop() { + const e = els(); + if (!e.player) return; + if (!document.fullscreenElement) return; + cropMode = !cropMode; + e.player.classList.toggle('crop-mode', cropMode); + if (e.cropIndicator) e.cropIndicator.classList.toggle('hidden', !cropMode); +} + function startProgressTimer() { stopProgressTimer(); if (!reportProgress) return; -- cgit v1.2.3