fix: align Hyprland overlay windows to mpv and stop pinning them

- Force-apply exact Hyprland move/resize/setprop dispatches when bounds are provided
- Stop pinning overlay windows; toggle pin off when Hyprland reports pinned=true
- Compensate stats overlay outer placement for Electron/Wayland content insets
- Make stats overlay window and page opaque so mpv cannot show through transparent insets
- Constrain stats app to h-screen with internal scroll so content covers mpv from y=0
- Lock overlay/stats window titles against page-title-updated events
- Add regression coverage for placement dispatches, inset compensation, and CSS overlay mode
This commit is contained in:
2026-05-03 23:56:52 -07:00
parent 8342fa0c0e
commit 4d5bf3de41
15 changed files with 398 additions and 85 deletions
+2 -2
View File
@@ -127,7 +127,7 @@ export function App() {
);
return (
<div className="min-h-screen flex flex-col bg-ctp-base">
<div className="h-screen min-h-0 overflow-hidden flex flex-col bg-ctp-base">
<header className="px-4 pt-3 pb-0">
<button
type="button"
@@ -139,7 +139,7 @@ export function App() {
</button>
<TabBar activeTab={activeTab} onTabChange={handleTabChange} />
</header>
<main className="flex-1 overflow-y-auto p-4">
<main className="flex-1 min-h-0 overflow-y-auto p-4">
{mediaDetail ? (
<Suspense fallback={<LoadingSurface label="Loading media detail..." />}>
<MediaDetailView
+14 -1
View File
@@ -34,17 +34,30 @@
--font-mono: 'Geist Mono Variable', 'JetBrains Mono', 'Fira Code', ui-monospace, monospace;
}
html,
body,
#root {
width: 100%;
height: 100%;
min-height: 100%;
}
body {
margin: 0;
font-family: var(--font-sans);
background-color: var(--color-ctp-base);
color: var(--color-ctp-text);
overflow: hidden;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
body.overlay-mode {
background-color: rgba(36, 39, 58, 0.85);
background-color: var(--color-ctp-base);
}
body.overlay-mode #root {
background-color: var(--color-ctp-base);
}
/* Custom scrollbar */
+15
View File
@@ -0,0 +1,15 @@
import assert from 'node:assert/strict';
import { readFileSync } from 'node:fs';
import { fileURLToPath } from 'node:url';
const css = readFileSync(fileURLToPath(new URL('./globals.css', import.meta.url)), 'utf8');
test('stats overlay mode paints an opaque full-viewport background', () => {
assert.match(css, /html,\s*body,\s*#root\s*\{[^}]*height:\s*100%;/s);
assert.match(css, /body\.overlay-mode\s*\{[^}]*background-color:\s*var\(--color-ctp-base\);/s);
assert.doesNotMatch(css, /body\.overlay-mode\s*\{[^}]*rgba\(/s);
assert.match(
css,
/body\.overlay-mode #root\s*\{[^}]*background-color:\s*var\(--color-ctp-base\);/s,
);
});