fix(plex): fill the screen in native fullscreen

Bug 5: in browser fullscreen the video stayed small and centred instead
of filling the screen. Two causes:

1. The wrapper carries 80vw/80vh + transform:scale(1.25) for the windowed
   HTPC look; in native FS that scaled layer never filled. Size the
   wrapper to 100vw/100vh with NO transform while fs is on.
2. The real culprit: the <video> was `w-auto h-auto max-h-full max-w-full`,
   which caps at the stream's intrinsic resolution (720p -> 1280x720) and
   never scales UP. The windowed scale(1.25) hid this. Switch to
   `w-full h-full object-contain` so it fills the wrapper by aspect.

Verified visually in real Chrome native fullscreen: video rect 1920x1024
(fills), was 1280x720 centred.
This commit is contained in:
2026-07-19 06:01:21 +02:00
parent 42ae451c1b
commit f8826b846b
+20 -11
View File
@@ -957,18 +957,24 @@ export default function PlexPlayer({ itemId, onClose, queue }: Props) {
onMouseMove={wake}
onClick={wake}
onWheel={onWheel}
// Everything in the player is 25% larger for lean-back / HTPC legibility. Laid out at 80vw×80vh
// and scaled ×1.25 from the top-left so it fills the viewport exactly (no overflow, controls stay
// on-screen). We use `transform: scale` rather than `zoom` deliberately: under `zoom`, event
// clientX stays in CSS pixels while getBoundingClientRect returns zoomed pixels, so the seek/
// volume/hover math (clientX − rect.left)/rect.width breaks; with `transform` both are in the same
// viewport space, so that ratio stays correct. Skip buttons ride this too → +25% like everything.
// Windowed (pseudo-fullscreen): everything is 25% larger for lean-back / HTPC legibility. Laid
// out at 80vw×80vh and scaled ×1.25 from the top-left so it fills the viewport exactly (no
// overflow, controls stay on-screen). We use `transform: scale` rather than `zoom` deliberately:
// under `zoom`, event clientX stays in CSS pixels while getBoundingClientRect returns zoomed
// pixels, so the seek/volume/hover math (clientX − rect.left)/rect.width breaks; with `transform`
// both are in the same viewport space, so that ratio stays correct. Skip buttons ride this too.
//
// In NATIVE fullscreen we must NOT carry the ×1.25 transform: with the element at 80vw×80vh, the
// scaled layer is composited letterboxed and the video never grows past its windowed size (the UA
// does NOT force this fixed, explicitly-sized element to fill). So in `fs` we size the wrapper to
// the full screen (100vw×100vh) with NO transform — the flex box then centres the video and its
// `max-h-full`/`max-w-full` aspect-fit fills the available height. The % seek-bar left and the
// clientX/rect math stay correct under the identity transform.
style={{
cursor: uiVisible ? "default" : "none",
width: "80vw",
height: "80vh",
transform: "scale(1.25)",
transformOrigin: "top left",
...(fs
? { width: "100vw", height: "100vh" }
: { width: "80vw", height: "80vh", transform: "scale(1.25)", transformOrigin: "top left" }),
}}
>
{/* Per-user subtitle appearance (font size / colour / background). ::cue is global, but this
@@ -979,7 +985,10 @@ export default function PlexPlayer({ itemId, onClose, queue }: Props) {
)}}`}</style>
<video
ref={videoRef}
className="max-h-full max-w-full w-auto h-auto"
// Fill the wrapper (which is sized to the viewport / screen) and letterbox by aspect via
// object-contain. NOT `w-auto h-auto max-*`: that caps the video at its intrinsic resolution
// (a 720p stream → 1280×720) and never scales it UP, so in fullscreen it sat tiny and centred.
className="w-full h-full object-contain"
onClick={togglePlay}
playsInline
>