Commit Graph
8 Commits
Author SHA1 Message Date
4grayandClaude Fable 5 aa1941cf2c fix(embedded-mpv): stop native-view video jump by moving control menus into the dock (#1207)
* 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>
2026-07-19 18:39:59 +02:00
4gray c6ed504723 feat(playback): add shared picture-in-picture controls (#1199)
* docs(playback): design shared web picture-in-picture

* docs(playback): keep picture-in-picture exit available

* docs(playback): plan shared web picture-in-picture

* feat(playback): add picture-in-picture controls contract

* feat(playback): add shared web picture-in-picture

* feat(playback): expose shared picture-in-picture action

* docs(playback): document shared web picture-in-picture

* test(playback): cover shared picture-in-picture flow

* test(playback): wait for PiP video in Electron E2E

* refactor(playback): extract picture-in-picture controller
2026-07-17 22:11:29 +02:00
4gray beb62db314 feat(settings): add shared web player controls toggle (#1198)
* docs(playback): design shared controls setting

* docs(playback): plan shared controls setting

* feat(settings): persist shared web controls preference

* test(settings): harden shared controls normalization coverage

* feat(settings): expose shared web controls toggle

* fix(settings): label shared controls toggle

* feat(playback): resolve shared controls from settings

* test(playback): cover shared controls setting

* docs(playback): document shared controls preference

* fix(playback): await settings before host creation

* fix(settings): normalize shared controls updates
2026-07-17 13:38:06 +02:00
4grayandLars Emig 48e3736460 feat(artplayer): add feature-flagged shared controls (#1196)
* 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>
2026-07-17 00:12:49 +02:00
4grayandLars Emig 4e572c60ca feat(videojs): add feature-flagged shared controls (#1195)
* 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>
2026-07-16 23:08:18 +02:00
4gray c49ea2f6a8 feat(html-player): add feature-flagged shared controls (#1194)
* feat(html-player): bridge engine state to shared controls

* fix(html-player): avoid HLS subtitle event reentry

* fix(html-player): restore delayed HLS default subtitles

* refactor(html-player): split controls bridge collaborators

* feat(html-player): add feature-flagged shared controls

* test(html-player): cover shared-controls source ownership

* feat(html-player): pass shared-controls playback metadata

* docs(player-controls): describe HTML5 shared-controls bridge

* docs(player-controls): clarify HTML5 rollout effect

* fix(html-player): reveal diagnostics from fullscreen

* fix(html-player): defer HLS event resolution

* refactor(html-player): isolate video element session
2026-07-16 21:30:41 +02:00
4gray f611ea3d7c feat(embedded-mpv): use shared controls for frame-copy (#1193)
* docs(embedded-mpv): plan frame-copy shared controls

* feat(embedded-mpv): adapt frame-copy sessions to shared controls

* fix(embedded-mpv): correlate recording control updates

* fix(embedded-mpv): accept recording ack before command resolve

* fix(embedded-mpv): serialize delayed recording commands

* fix(embedded-mpv): latch buffered recording outcomes

* feat(embedded-mpv): use shared controls for frame-copy

* fix(embedded-mpv): isolate recording ticks by engine

* fix(embedded-mpv): reset controls on engine handoff

* docs(embedded-mpv): document frame-copy shared controls

* docs(embedded-mpv): normalize shared-controls plans

* fix(embedded-mpv): isolate legacy feedback on handoff

* fix(player-controls): block toggles while stalled

* refactor(embedded-mpv): isolate controls timing

* fix(player-controls): reset recording feedback on handoff

* fix(embedded-mpv): preserve newer session snapshots
2026-07-16 18:47:32 +02:00
aa6ee85d3f feat(player-controls): add shared engine-agnostic controls layer (#1148)
* 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>
2026-07-16 12:56:30 +02:00