From fe3c86394c706b9635cca6a7ee5dd8d062acf7c9 Mon Sep 17 00:00:00 2001 From: 4gray <4gray@users.noreply.github.com> Date: Fri, 4 Sep 2026 16:36:29 +0200 Subject: [PATCH] fix(playback): keep Video.js vendor-chrome shortcuts after a mouse click on a control (#1523) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Follow-up to #1516 for the vendor-chrome path (shared controls opted out). With Video.js's own controls, Chromium leaves a clicked control-bar button focused, and a focused Video.js component captures the keyboard entirely, so after clicking fullscreen Space left fullscreen instead of pausing and the seek, volume and mute keys did nothing until the user clicked the video. ArtPlayer and the native HTML5 controls were verified unaffected. The legacy Video.js chrome now releases the focus a pointer interaction leaves on a control (vjs-pointer-focus-release.ts). The release is scoped to the .vjs-control-bar and pointer-attributed, and runs on both focusin (focus landing on a control, e.g. a menu handing focus to its button) and click (a control clicked while already focused, which fires no focusin); keyboard Tab focus and modal-dialog focus traps are preserved. The eligibility helper is shared with ControlsSurface via pointer-focus-release.ts. Co-Authored-By: Claude Opus 4.8 šŸ¤– Generated with [Claude Code](https://claude.com/claude-code) --- ...ck-videojs-shortcuts-after-button-click.md | 10 + AGENTS.md | 22 +- CLAUDE.md | 2 +- apps/web-e2e/src/xtream.e2e.ts | 329 +++++++++++------- docs/architecture/player-controls-contract.md | 36 +- .../lib/player-controls/controls-surface.ts | 32 +- .../playback/src/lib/player-controls/index.ts | 1 + .../pointer-focus-release.spec.ts | 105 ++++++ .../player-controls/pointer-focus-release.ts | 52 +++ ...vjs-player.component.pointer-focus.spec.ts | 132 +++++++ .../lib/vjs-player/vjs-player.component.ts | 10 + .../vjs-pointer-focus-release.spec.ts | 196 +++++++++++ .../vjs-player/vjs-pointer-focus-release.ts | 120 +++++++ 13 files changed, 902 insertions(+), 145 deletions(-) create mode 100644 .changes/playback-videojs-shortcuts-after-button-click.md create mode 100644 libs/ui/playback/src/lib/player-controls/pointer-focus-release.spec.ts create mode 100644 libs/ui/playback/src/lib/player-controls/pointer-focus-release.ts create mode 100644 libs/ui/playback/src/lib/vjs-player/vjs-player.component.pointer-focus.spec.ts create mode 100644 libs/ui/playback/src/lib/vjs-player/vjs-pointer-focus-release.spec.ts create mode 100644 libs/ui/playback/src/lib/vjs-player/vjs-pointer-focus-release.ts diff --git a/.changes/playback-videojs-shortcuts-after-button-click.md b/.changes/playback-videojs-shortcuts-after-button-click.md new file mode 100644 index 000000000..6a9e38625 --- /dev/null +++ b/.changes/playback-videojs-shortcuts-after-button-click.md @@ -0,0 +1,10 @@ +--- +type: fix +area: playback +--- + +With IPTVnator's shared controls turned off, the Video.js player's keyboard +shortcuts now work again right after you click a button in its control bar. +Previously the clicked button kept the keyboard: after clicking fullscreen, +Space left fullscreen instead of pausing, and the seek, volume and mute keys +did nothing until you clicked the video. diff --git a/AGENTS.md b/AGENTS.md index c62df3c28..f3ac937ef 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -457,7 +457,27 @@ Key files: path keeps the existing Video.js skin and legacy series navigation unchanged (still without `userActions.hotkeys`), while the playback keyboard shortcuts attach through `LegacyPlayerShortcuts` and drive the player API so the - vendor control bar stays in sync (`vjs-legacy-shortcuts.ts`). + vendor control bar stays in sync (`vjs-legacy-shortcuts.ts`). That chrome + also releases the focus a pointer interaction leaves on a control + (`vjs-pointer-focus-release.ts`, sharing `pointer-focus-release.ts`'s + `blurFocusedControl` with `ControlsSurface`): a focused Video.js component + stops every key before the document and turns Space/Enter into a click, so + after a click on fullscreen Space left fullscreen instead of pausing. It is + driven mainly by `focusin`, not the click, because choosing a menu item + moves focus to the menu button a tick later and that click never bubbles to + the shell: an eligible control (button/`role=button`/slider, never a menu + item) is released when its focus is attributable to a recent shell + `pointerdown` not yet ended by a document `keydown`, so `Tab` focus is kept. + A `click` runs the same release for a control clicked while already focused + (Tab, then a mouse click), which fires no `focusin`. The release is scoped + to `.vjs-control-bar`, so the caption-settings dialog (a modal sibling of + the bar) keeps its focus trap. Menu buttons live in the bar and are not + exempt: a popup is navigated through its focused item, so releasing the + button never disturbs an open menu, and the button focus a pointer moves + through (open, item selection, toggling an open menu shut) is released so + Space works again after the menu closes. ArtPlayer + (non-focusable divs) and the native HTML5 controls (focus lands on the + `