From 2659991d84738d20608489691a9342b7a758ac99 Mon Sep 17 00:00:00 2001 From: npeter83 Date: Thu, 9 Jul 2026 01:32:04 +0200 Subject: [PATCH] fix(plex): seek tooltip tracks cursor under scale; tidy the tuning menu MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - The seek-bar hover tooltip positioned itself with a px `left` derived from the scaled getBoundingClientRect, so inside the transform:scale player it got re-scaled and drifted right of the cursor (only 0 was right). Position it by fraction (% of the bar) instead — it now sits under the cursor at any point, like the volume tooltip already did. - The tuning (gear) menu tabs wrapped to two lines at the +25% scale. Widen the menu, keep tab labels on one line (whitespace-nowrap), and shorten the labels: "Sync offset" → "Offsets", "Subtitle style" → "Subtitles" (all three languages). --- frontend/src/components/PlexPlayer.tsx | 19 +++++++++++-------- frontend/src/i18n/locales/de/plex.json | 4 ++-- frontend/src/i18n/locales/en/plex.json | 4 ++-- frontend/src/i18n/locales/hu/plex.json | 4 ++-- 4 files changed, 17 insertions(+), 14 deletions(-) diff --git a/frontend/src/components/PlexPlayer.tsx b/frontend/src/components/PlexPlayer.tsx index 1409a85..9d840ff 100644 --- a/frontend/src/components/PlexPlayer.tsx +++ b/frontend/src/components/PlexPlayer.tsx @@ -860,8 +860,11 @@ export default function PlexPlayer({ itemId, onClose, queue }: Props) { // eslint-disable-next-line react-hooks/exhaustive-deps }, [activeMarker?.type, activeMarker?.start_s, prefs.autoSkipIntro, prefs.autoSkipCredits, prefs.autoSkipDelay, doSkip]); - // Seekbar hover: a timestamp tooltip that follows the cursor. - const [hover, setHover] = useState<{ x: number; t: number } | null>(null); + // Seekbar hover: a timestamp tooltip that follows the cursor. Store the position as a FRACTION + // (0..1), not pixels: the bar sits inside the transform:scale(1.25) subtree, so a px `left` would be + // re-scaled and drift right of the cursor (only 0 stayed correct). A % `left` is relative to the + // bar's own width and scales cleanly. + const [hover, setHover] = useState<{ frac: number; t: number } | null>(null); // Seekbar scrubbing: the absolute time under the dragging finger. While non-null the head/thumb // preview this position; the real seek fires once on release (avoids restarting the session on // every out-of-buffer step of the drag). A plain click is a down+up at one spot → seeks there. @@ -1037,9 +1040,9 @@ export default function PlexPlayer({ itemId, onClose, queue }: Props) { }} onPointerMove={(e) => { const r = (e.currentTarget as HTMLElement).getBoundingClientRect(); - const x = Math.max(0, Math.min(e.clientX - r.left, r.width)); - const tt = (x / r.width) * duration; - setHover({ x, t: tt }); + const frac = Math.max(0, Math.min((e.clientX - r.left) / r.width, 1)); + const tt = frac * duration; + setHover({ frac, t: tt }); if (e.buttons & 1) setScrub(tt); // dragging with the primary button held }} onPointerUp={() => { @@ -1054,7 +1057,7 @@ export default function PlexPlayer({ itemId, onClose, queue }: Props) { {hover && duration > 0 && (
{fmt(hover.t)}
@@ -1236,14 +1239,14 @@ export default function PlexPlayer({ itemId, onClose, queue }: Props) { ref={menuRef} onWheel={(e) => e.stopPropagation()} onClick={(e) => e.stopPropagation()} - className="glass-menu absolute bottom-full right-0 mb-2 w-72 rounded-xl p-2 text-sm text-white shadow-2xl" + className="glass-menu absolute bottom-full right-0 mb-2 w-80 rounded-xl p-2 text-sm text-white shadow-2xl" >
{settingsTabs.map((tab) => (