mirror of
https://github.com/4gray/iptvnator.git
synced 2026-10-15 21:56:18 -08:00
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>
This commit is contained in:
1 parent
c707af1334
commit
aa1941cf2c
35 files changed
+1666
-622
No files matched your search
@@ -246,7 +246,7 @@ service and the adapter):
|
||||
`attachEmbeddedMpvFrameView`/`detachEmbeddedMpvFrameView`.
|
||||
- Renderer: `EmbeddedMpvPlayerComponent` renders the canvas when
|
||||
`support.engine === 'frame-copy'` and skips the compositor workarounds —
|
||||
no `HIDDEN_BOUNDS` when dialogs open, no popover bottom cutout; dialogs
|
||||
no `HIDDEN_BOUNDS` when dialogs open; dialogs
|
||||
and the shared `app-player-controls` overlay stack above the canvas as
|
||||
ordinary DOM. The canvas fills the player root; the native dock's reserved
|
||||
controls height is not applied. Legacy embedded-MPV pointer/click,
|
||||
@@ -533,7 +533,7 @@ player component stays a view-oriented orchestrator and engine-specific
|
||||
controls host. The renderer files live under
|
||||
`libs/ui/playback/src/lib/embedded-mpv-player/`:
|
||||
|
||||
- `embedded-mpv-format.utils.ts` — pure helpers (`formatTime`, `audioTrackLabel`, `subtitleTrackLabel`, `speedLabel`, `aspectLabel`, `volumeIcon`, `volumeLabel`, `readStoredVolume`, `persistVolume`, `measureBounds`) and preset constants (`SPEED_PRESETS`, `ASPECT_PRESETS`, `HIDDEN_BOUNDS`, `MENU_OPEN_BOTTOM_CUTOUT_PX`).
|
||||
- `embedded-mpv-format.utils.ts` — pure helpers (`formatTime`, `audioTrackLabel`, `subtitleTrackLabel`, `speedLabel`, `aspectLabel`, `volumeIcon`, `volumeLabel`, `readStoredVolume`, `persistVolume`, `measureBounds`) and preset constants (`SPEED_PRESETS`, `ASPECT_PRESETS`, `HIDDEN_BOUNDS`).
|
||||
- `embedded-mpv-controls.adapter.ts` — component-scoped `PlayerController`
|
||||
adapter for frame-copy. Maps session/support/playback signals to shared
|
||||
controls state and capabilities, delegates commands to
|
||||
@@ -554,7 +554,9 @@ controls host. The renderer files live under
|
||||
- `embedded-mpv-shortcuts.ts` — native-view-only `EmbeddedMpvShortcuts` class
|
||||
with `attach(handlers)` / `detach()`. Owns the legacy document keydown
|
||||
listener and routes through a callback interface; the component supplies
|
||||
callbacks for Space/K, F, arrow keys, M, and Escape.
|
||||
callbacks for Space/K, F, arrow keys, M, and Escape. The optional
|
||||
`arrowKeysBlocked` handler suspends the seek/volume arrows while a dock
|
||||
chip panel owns them for chip navigation.
|
||||
- `embedded-mpv-overlay-visibility.service.ts` — singleton service that exposes
|
||||
`overlayActive: signal<boolean>`. Tracks `MatDialog.afterOpened`/
|
||||
`afterAllClosed` for dialog-shaped overlays and falls back to a
|
||||
@@ -562,9 +564,21 @@ controls host. The renderer files live under
|
||||
backdrop-bearing CDK overlays. Native-view uses it to move the platform host
|
||||
off-screen; frame-copy uses it to gate shared playback shortcuts.
|
||||
- `embedded-mpv-ui-state.ts` — legacy native-view
|
||||
`EmbeddedMpvMenuState` (single-open popover state machine) and
|
||||
`EmbeddedMpvMenuState` (single-open menu state machine, incl. the
|
||||
`dockPanelOpen` chip-panel signal that suspends arrow shortcuts) and
|
||||
`EmbeddedMpvFeedback` (transient keypress feedback). They are not the
|
||||
frame-copy shared-controls state.
|
||||
- `embedded-mpv-dock-panels.ts` — native-view `EmbeddedMpvDockPanelState`:
|
||||
builds the active horizontal chip-panel view model (audio, subtitle, speed,
|
||||
aspect) from the menu state, routes chip selection back to the session
|
||||
controller, and restores toggle-button focus after a panel closes.
|
||||
- `embedded-mpv-dock-panel.component.ts` — standalone
|
||||
`app-embedded-mpv-dock-panel` that morphs the dock row inside the
|
||||
fixed-height controls strip: back button + title + horizontally scrollable
|
||||
chip ribbon (`role="menu"` with `aria-orientation="horizontal"`,
|
||||
`menuitemradio` chips, wheel-to-horizontal-scroll mapping, edge fades,
|
||||
active-chip reveal/focus, roving arrow keys, RTL-aware). Keeping the panels
|
||||
inside the strip is what lets menus open without any MPV bounds change.
|
||||
- `embedded-mpv-command-runner.ts` — transport/track/recording IPC delegation; contains addon-side throws; reconciles a returned snapshot only when the current canonical session id and returned snapshot id both match the captured command session id.
|
||||
- `embedded-mpv-session-factory.ts` — side-effect-free loading/error placeholder factories plus `waitForStartupPaint`.
|
||||
- `embedded-mpv-stalled-tracker.ts` — owns the 30-second loading timer and `stalled` signal.
|
||||
@@ -578,21 +592,37 @@ controls host. The renderer files live under
|
||||
|
||||
### Bounds compositing strategy
|
||||
|
||||
The following cutout strategy applies only to the native-view engine. Its video
|
||||
The following strategy applies only to the native-view engine. Its video
|
||||
host paints outside the normal DOM stacking model, so any DOM region it covers
|
||||
cannot reliably receive pointer events and any CSS `z-index` competition is
|
||||
unwinnable. The component compensates with a single `boundsProvider(host)`
|
||||
closure on the controller that returns one of three bound shapes, evaluated
|
||||
closure on the controller that returns one of two bound shapes, evaluated
|
||||
each time the active bounds-sync runs:
|
||||
|
||||
- **Modal overlay open** (any MatDialog, including the command palette) → `HIDDEN_BOUNDS`. The MPV video host moves off-screen so the dialog has the full window.
|
||||
- **Control popover open** (any of the menu states above) → host bounds with `MENU_OPEN_BOTTOM_CUTOUT_PX` (300 px) removed from the bottom. The popover region becomes DOM-receiving while video keeps playing in the upper region.
|
||||
- **Idle** → full host bounds.
|
||||
- **Otherwise** → full host bounds.
|
||||
|
||||
The viewport DOM element also reserves `--embedded-mpv-controls-height` (64 px) at the bottom when controls are enabled, so the controls strip itself is always DOM and always reachable for hover-to-reveal even before the popover-cutout takes effect.
|
||||
Control menus never influence bounds: all five (volume, audio, subtitle,
|
||||
speed, aspect) render horizontally inside the fixed-height controls strip
|
||||
below the video host. Volume expands as an inline horizontal slider next to
|
||||
the mute button; the audio/subtitle/speed/aspect menus morph the dock row
|
||||
into `app-embedded-mpv-dock-panel` — back button, panel title, and a
|
||||
horizontally scrollable chip ribbon (vertical wheel mapped to horizontal
|
||||
scroll, edge fades as continuation hints, auto-reveal and focus of the active
|
||||
chip, roving arrow-key navigation, RTL-aware). Because the strip height never
|
||||
changes, opening or closing a menu sends no new MPV bounds and the video never
|
||||
re-letterboxes. The popover-era 300 px bottom cutout
|
||||
(`MENU_OPEN_BOTTOM_CUTOUT_PX`) is gone; while a chip panel is open, the
|
||||
global arrow-key shortcuts (seek/volume) are suspended so arrows walk the
|
||||
chips instead.
|
||||
|
||||
The viewport DOM element reserves `--embedded-mpv-controls-height` (64 px;
|
||||
88 px under the narrow breakpoint) at the bottom when controls are enabled, so
|
||||
the controls strip — including the in-dock panels — is always DOM and always
|
||||
reachable for hover-to-reveal.
|
||||
|
||||
For frame-copy, `boundsProvider` always returns the measured full host bounds:
|
||||
there is no `HIDDEN_BOUNDS`, popover cutout, or reserved dock height. Dialogs
|
||||
there is no `HIDDEN_BOUNDS` or reserved dock height. Dialogs
|
||||
and controls layer naturally over the canvas, while bounds sync still updates
|
||||
the helper's render size.
|
||||
|
||||
|
||||
@@ -506,8 +506,9 @@ back to the command's stale baseline.
|
||||
|
||||
The native MPV surface paints outside Chromium's DOM stacking model. It keeps
|
||||
the compositor-safe fixed controls dock below the viewport. Modal overlays hide
|
||||
the native surface with `HIDDEN_BOUNDS`, and control popovers reserve a bottom
|
||||
cutout so their DOM region remains interactive.
|
||||
the native surface with `HIDDEN_BOUNDS`; control menus render as horizontal
|
||||
panels inside the fixed-height dock strip, so they stay interactive without
|
||||
any bounds change.
|
||||
|
||||
The transparent BrowserWindow / `NSWindowBelow` tunnel-and-backdrop approach is
|
||||
not the shipped architecture. The shared-controls integration does not add
|
||||
|
||||
Reference in new issue
Block a user