You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat(embedded-mpv): real macOS native fullscreen for the embedded player
Use native NSWindow fullscreen instead of DOM fullscreen for the
embedded player. Extend the player-controls contract with a fullscreen
delegate (PlayerFullscreenController + ControlsFullscreenBinding) and
add window-state IPC so OS-initiated exits (green button, Ctrl+Cmd+F,
ESC) reconcile with the in-app fullscreen state.
Review-driven hardening: the deferred window-fullscreen request is now
gated by a generation counter + destroyed flag so it becomes a no-op if
fullscreen is exited/re-toggled or the component is torn down before the
two requestAnimationFrame ticks run (pending rAF ids are cancelled in
ngOnDestroy); the fill snap is awaited (raced with a ~150ms timeout so a
hung IPC can not block fullscreen) before scheduling the window request;
and the fullscreen controller now requires BOTH the setMainWindowFullScreen
and setEmbeddedMpvFill bridges in canToggle (older bridges fall back to the
built-in DOM fullscreen path).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/architecture/embedded-mpv-native.md
+3-3Lines changed: 3 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -150,7 +150,7 @@ covers the embedded-MPV-specific native/session internals.
150
150
151
151
The Angular side of the embedded MPV player is intentionally split so the player component stays a view-only orchestrator. The renderer files live under `libs/ui/playback/src/lib/embedded-mpv-player/`:
152
152
153
-
-`embedded-mpv-session-controller.ts` — component-scoped `Injectable` that owns the `support`, `session`, `sessionId`, `stalled`, and `retryToken` signals. Subscribes to `onEmbeddedMpvSessionUpdate`, runs the polling-driven `stalled` timer, owns the bounds-sync (resize/scroll/RAF), exposes `setBoundsProvider` / `triggerBoundsSync`, and delegates transport/track/recording commands to `EmbeddedMpvCommandRunner`.
153
+
-`embedded-mpv-session-controller.ts` — component-scoped `Injectable` that owns the `support`, `session`, `sessionId`, `stalled`, and `retryToken` signals. Subscribes to `onEmbeddedMpvSessionUpdate`, runs the polling-driven `stalled` timer, owns the bounds-sync (resize/scroll/RAF), exposes `setBoundsProvider` / `triggerBoundsSync` / `setFill`, and delegates transport/track/recording commands to `EmbeddedMpvCommandRunner`.
-`embedded-mpv-session-factory.ts` — pure session-snapshot constructors (`createLoadingSession`, `createAttachingSession`, `createErrorSession`) and `waitForStartupPaint`.
156
156
-`embedded-mpv-compositor.ts` — `measureBounds(host)` and bounds helpers that keep the native surface aligned with the Angular layout.
@@ -159,13 +159,13 @@ The Angular side of the embedded MPV player is intentionally split so the player
159
159
-`embedded-mpv-labels.ts` — label/format helpers (`formatTime`, audio/subtitle/speed/aspect/volume labels) and the preset constants.
160
160
-`embedded-mpv-stalled-tracker.ts` — `EmbeddedMpvStalledTracker` driving the "taking longer than expected" state.
161
161
-`embedded-mpv-controls.adapter.ts` — `EmbeddedMpvControlsAdapter`, the `PlayerController` implementation bound by `app-player-controls`.
162
-
-`embedded-mpv-player.component.ts` — view-only shell. Holds view children, derived `computed` signals, DOM event listeners (pointermove, pointerdown, fullscreenchange, dblclick), and the `effect()`s (immersive activation, bounds→backdrop rect, session lifecycle, time/ended bridging).
162
+
-`embedded-mpv-player.component.ts` — view-only shell. Holds view children, derived `computed` signals, DOM event listeners (pointermove, pointerdown, dblclick), a window-state subscription that reconciles OS-initiated fullscreen exits, and the `effect()`s (immersive activation, bounds→backdrop rect, session lifecycle, time/ended bridging).
163
163
164
164
### Shipped path: immersive overlay
165
165
166
166
The shipped embedded-MPV player composites the libmpv surface **below** the WebContents (`NSWindowBelow`) and keeps it **always full-bleed** — the controller's default `measureBounds` provider; there are no docked or cutout bound shapes. Because the web layer is on top, modals, popovers, and the inline `app-player-controls` all paint and receive input normally — no off-screen "hide the surface" trick is needed. The app stays opaque via the single global backdrop with a transparent hole at the video rect (`EmbeddedMpvImmersiveService` + `embedded-mpv-immersive-backdrop`). The `setBoundsProvider` hook remains for a future docked / bottom-mini-bar mode but is unused on the current full-bleed path. The full immersive design, and the rationale for choosing it over the earlier (rejected) child-window and docked-above approaches, are documented in [player-controls-refactor.md](./player-controls-refactor.md).
167
167
168
-
Fullscreen runs the controls component's built-in `ControlsFullscreen` against the player root element: the root goes DOM-fullscreen, the surrounding chrome is hidden via `body.embedded-mpv-fullscreen` (backdrop off) so the transparent fullscreen surface reveals the native video filling the screen, and the bounds sync re-measures the new viewport. See `player-controls-refactor.md` for the immersive-overlay design.
168
+
Fullscreen uses real macOS **native fullscreen** of the Electron window (`setMainWindowFullScreen` → `win.setFullScreen`) via the player-controls `PlayerFullscreenController` delegate — not DOM`requestFullscreen`. On enter the native surface is put in autoresize "fill" mode with its render frozen during macOS's snapshot transition (the last frame scales cleanly; the video briefly pauses, as the HTML5 player also does), and OS-initiated exits (green button / Ctrl+Cmd+F / ESC) are reconciled through the window-state bridge. See `player-controls-refactor.md` for the full fullscreen choreography.
0 commit comments