fix: hide embedded MPV view while modal overlays are open

The embedded MPV player is a native NSView that sits above the WebContents
layer, so DOM dialogs (command palette, MatDialog, etc.) always paint behind
it regardless of z-index. Track CDK overlay backdrops via OverlayContainer +
MutationObserver and send off-screen bounds to the native view while any
backdrop is present, restoring real bounds on close. Playback continues
uninterrupted.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: f957cd9849e0
This commit is contained in:
4grayandClaude Opus 4.7 committed 2026-05-05 08:57:59 +02:00
1 parent e192cba776
commit 1c499856ab
2 files changed
+54 -4

No files matched your search

@@ -0,0 +1,29 @@
import { Injectable, inject, signal } from '@angular/core';
import { OverlayContainer } from '@angular/cdk/overlay';
@Injectable({ providedIn: 'root' })
export class EmbeddedMpvOverlayVisibilityService {
readonly overlayActive = signal(false);
private readonly overlayContainer = inject(OverlayContainer);
private observer: MutationObserver | null = null;
constructor() {
if (typeof MutationObserver === 'undefined') {
return;
}
const container = this.overlayContainer.getContainerElement();
this.observer = new MutationObserver(() => this.recompute(container));
this.observer.observe(container, { childList: true, subtree: true });
this.recompute(container);
}
private recompute(container: HTMLElement): void {
const hasBackdrop =
container.querySelector('.cdk-overlay-backdrop') !== null;
if (this.overlayActive() !== hasBackdrop) {
this.overlayActive.set(hasBackdrop);
}
}
}
@@ -7,11 +7,20 @@ import {
Output,
computed,
effect,
inject,
input,
signal,
untracked,
viewChild,
} from '@angular/core';
import { EmbeddedMpvOverlayVisibilityService } from './embedded-mpv-overlay-visibility.service';
const HIDDEN_BOUNDS = Object.freeze({
x: -100000,
y: -100000,
width: 1,
height: 1,
});
import { MatButtonModule } from '@angular/material/button';
import { MatIconModule } from '@angular/material/icon';
import { MatTooltipModule } from '@angular/material/tooltip';
@@ -42,6 +51,10 @@ export class EmbeddedMpvPlayerComponent implements OnDestroy {
duration: number;
}>();
private readonly overlayVisibility = inject(
EmbeddedMpvOverlayVisibilityService
);
readonly viewport = viewChild<ElementRef<HTMLDivElement>>('viewport');
readonly playerRoot = viewChild<ElementRef<HTMLDivElement>>('playerRoot');
readonly support = signal<EmbeddedMpvSupport | null>(null);
@@ -242,11 +255,14 @@ export class EmbeddedMpvPlayerComponent implements OnDestroy {
return;
}
const bounds = untracked(() =>
this.overlayVisibility.overlayActive()
)
? HIDDEN_BOUNDS
: this.measureBounds(host);
void window.electron
.setEmbeddedMpvBounds(
activeSessionId,
this.measureBounds(host)
)
.setEmbeddedMpvBounds(activeSessionId, bounds)
.catch(() => undefined);
};
@@ -364,6 +380,11 @@ export class EmbeddedMpvPlayerComponent implements OnDestroy {
}
});
});
effect(() => {
this.overlayVisibility.overlayActive();
untracked(() => this.activeBoundsSync?.());
});
}
ngOnDestroy(): void {