* fix(embedded-mpv): stop native-view video jump by moving menus into the dock
Opening any control popover in the native-view embedded MPV dock used to
shrink the MPV view by a 300 px bottom cutout so the popover DOM stayed
clickable, which made mpv re-letterbox the video on every menu open/close.
All five menus now render horizontally inside the fixed-height controls
strip, so menu state never changes the native view bounds:
- volume expands as an inline horizontal slider next to the mute button
- audio/subtitle/speed/aspect morph the dock row into a back button, a
panel title, and a scrollable chip ribbon (app-embedded-mpv-dock-panel)
with wheel-to-horizontal-scroll mapping, edge fades, active-chip
reveal/focus, roving arrow-key navigation, ellipsis + tooltips, and
RTL-aware scrolling
- boundsProvider loses the menus.anyOpen() cutout branch and the
MENU_OPEN_BOTTOM_CUTOUT_PX constant is removed; HIDDEN_BOUNDS for modal
overlays is unchanged
- global arrow shortcuts (seek/volume) are suspended while a chip panel
is open so arrows walk the chips; Esc, click-outside, and close-on-select
semantics are preserved
- new EMBEDDED_MPV.PLAYER.BACK i18n key in all 18 languages
Regression coverage: the new dock-panels spec asserts the bounds provider
returns full host bounds while every menu is open (fails against the old
cutout behavior), plus panel morph/a11y/selection specs and a dedicated
dock-panel component spec (keyboard, wheel, tabindex, emits).
Frame-copy shared controls (app-player-controls) are untouched.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(embedded-mpv): address native-view dock review feedback
Resolves the actionable P2 review comments on the dock rework:
- Inline volume no longer clips the dock actions. At sidebar-constrained
player widths (~480-660px, viewport wider than the 720px breakpoint) the
new in-flow volume slider widened the non-shrinking actions column and,
under overflow:hidden, clipped the fullscreen button. Add min-width:0 to
.embedded-mpv-player__actions and __volume-group so the inline volume (a
scroll container) compresses its own slider instead of pushing neighbors
off-edge. Verified in Chromium: fullscreen stays visible down to 480px.
- Space now selects a focused chip. onPanelKeydown stops Space/Enter from
bubbling to the global shortcut handler (whose Space case preventDefault'd
the button's native activation and toggled playback) without calling
preventDefault itself, so the menuitemradio chip activates and emits
chipSelected. Matches the WAI-ARIA menu activation-key expectation.
- Simplify dock-panel opener tracking: always remember the toggled kind so
focus restoration is correct if in-panel switching ever becomes reachable
(currently unreachable — the toggle buttons are removed from the DOM while
a panel is open); restoreOpenerFocus still no-ops unless focus fell to body.
Not changed: the "closePanels no-ops when unavailable" comment — verified
unreachable (chip selection closes via menus.close() directly, not through
closePopovers; isAvailable() is engine-bound and the native dock only renders
while it is true, with engine handoff calling menus.closeAll()).
Regression test added for Space/Enter chip activation. The volume-overflow
fix is CSS layout (no jsdom layout engine) and was validated in a real
browser.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
* feat(artplayer): add feature-flagged shared controls
Co-authored-by: Lars Emig <lars.emig@pickware.de>
* fix(artplayer): align native type and signal inputs
---------
Co-authored-by: Lars Emig <lars.emig@pickware.de>
* feat(videojs): add feature-flagged shared controls
Rebuild the Video.js shared-controls integration on the current player lifecycle with Tech rebinds, source-scoped tracks, reset ordering, volume preservation, diagnostics gating, and default-off compatibility.
Credits and supersedes the stacked implementation proposed in #1153.
Co-authored-by: Lars Emig <lars.emig@pickware.de>
* fix(videojs): harden MPEG-TS reset lifecycle
---------
Co-authored-by: Lars Emig <lars.emig@pickware.de>
* feat(player-controls): shared engine-agnostic controls layer (flag off)
Introduce a shared, engine-agnostic player-controls layer in libs/ui/playback
as pure additive library code with no consumers yet.
- Contract (player-controls.model.ts): PlayerControlsCapabilities,
PlayerControlsState, and PlayerControlsCommands make up the
PlayerController interface every engine adapter implements.
- Single presentation component (app-player-controls): one controls UI
binding purely to a PlayerController, with focused helpers for
visibility auto-hide, volume, fullscreen (built-in DOM path), menus,
keyboard shortcuts, seek/volume feedback, and the controls surface.
- Web-video adapter (web-video-controls.adapter.ts + host directive):
drives the contract from an HTMLVideoElement, including optional
HLS.js quality/audio-track integration and series episode navigation.
- Feature flag WEB_PLAYER_SHARED_CONTROLS (web-player-controls.flag.ts)
defaults to OFF; no player component consumes the new layer yet, so
runtime behavior is unchanged.
- docs/architecture/player-controls-contract.md documents the target
architecture (later PRs add the embedded-MPV adapter, immersive
overlay, and host-supplied fullscreen delegate).
Review-driven hardening: the web-video adapter now holds the host
series-navigation signal reactively (updates after setContext are
reflected); the shared controls template is fully localized via
ngx-translate (reusing the EMBEDDED_MPV.PLAYER.* keys); single click on
the viewport toggles play/pause deferred so a double-click still
fullscreens; keyboard shortcuts are shadow-DOM-safe (composedPath) and
guard against duplicate-instance double-execution (defaultPrevented);
and hidden controls now also disable shortcuts.
Test coverage extended per Codecov patch report.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
* fix(player-controls): harden shared controls foundation
* fix(player-controls): restore detached adapter state
* fix(player-controls): harden keyboard and adapter state
* fix(player-controls): refresh readiness and hide timers
* fix(player-controls): keep controls root inside surface
* fix(player-controls): hide seek controls for live streams
* fix(player-controls): harden multi-engine control state
* fix(player-controls): respect runtime interaction availability
* fix(player-controls): gate loading toggles and localize mute
* fix(player-controls): harden surface and error states
* fix(player-controls): preserve volume and cursor state
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: 4gray <serega05@gmail.com>