From c31bafa07a25e3a2d13262185d15b6249bb73a4f Mon Sep 17 00:00:00 2001 From: vincent Date: Thu, 3 Sep 2026 20:52:31 +0200 Subject: [PATCH] fix: the keyboard shortcuts the player has always advertised The transport buttons have said "Play (space)" and "Full screen (f)" since they were built, with nothing listening for either key. Pressing f did nothing, which is exactly what a broken fullscreen button looks like. Both now work, verified in the running app: f enters and leaves fullscreen, space plays and pauses. Space is prevented from also pressing whichever button has focus, and neither fires while typing in a field or alongside a modifier. Escape now asks to leave fullscreen before the player considers closing, so one keypress cannot drop you all the way back to the feed. The button itself was never broken: it was checked on a downloaded video, on a stream, and inside a window already in macOS fullscreen, entering and leaving each time. --- src/components/Player.tsx | 5 +++-- src/components/PlayerControls.tsx | 32 +++++++++++++++++++++++++++++++ 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/src/components/Player.tsx b/src/components/Player.tsx index a22e4a5..98f117b 100644 --- a/src/components/Player.tsx +++ b/src/components/Player.tsx @@ -299,8 +299,9 @@ export default function Player({ // Escape backs out, as it does everywhere else in the app. useEffect(() => { const onKey = (e: KeyboardEvent) => { - // In fullscreen the browser already handles Escape; closing the player - // as well would drop you all the way back to the feed. + // In fullscreen, Escape is the transport bar's to handle: it leaves + // fullscreen. Closing the player as well would drop you all the way back + // to the feed in one keypress. if (e.key === "Escape" && !document.fullscreenElement) leave(); // Arrow keys only when the video does not own them for seeking. if (e.key === "ArrowLeft" && e.shiftKey) onPrev?.(); diff --git a/src/components/PlayerControls.tsx b/src/components/PlayerControls.tsx index 45108e4..e1c93f9 100644 --- a/src/components/PlayerControls.tsx +++ b/src/components/PlayerControls.tsx @@ -253,6 +253,38 @@ export default function PlayerControls({ } }, [stageRef, onActivity]); + /** + * The shortcuts the buttons advertise. Their tooltips have promised + * "(space)" and "(f)" all along with nothing listening, and Escape left + * fullscreen nowhere: WebKit does not handle it for an element made + * fullscreen this way, so pressing it did nothing and the only way out was + * to find the button again. + */ + useEffect(() => { + const onKey = (e: KeyboardEvent) => { + // Never while typing, and never over a shortcut of the system's own. + const t = e.target as HTMLElement | null; + if (t && (t.tagName === "INPUT" || t.tagName === "TEXTAREA" || t.isContentEditable)) return; + if (e.metaKey || e.ctrlKey || e.altKey) return; + + if (e.key === " " || e.code === "Space") { + // Or the space would also press whichever button has focus. + e.preventDefault(); + const v = videoRef.current; + if (!v) return; + if (v.paused) void v.play().catch(() => {}); + else v.pause(); + onActivity?.(); + } else if (e.key === "f" || e.key === "F") { + void toggleFull(); + } else if (e.key === "Escape" && document.fullscreenElement) { + void document.exitFullscreen(); + } + }; + window.addEventListener("keydown", onKey); + return () => window.removeEventListener("keydown", onKey); + }, [videoRef, toggleFull, onActivity]); + const pct = duration > 0 ? (time / duration) * 100 : 0; return (