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.
This commit is contained in:
vincent
2026-09-03 20:52:31 +02:00
parent dac159050c
commit c31bafa07a
2 changed files with 35 additions and 2 deletions
+3 -2
View File
@@ -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?.();
+32
View File
@@ -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 (