diff --git a/electron/electron-env.d.ts b/electron/electron-env.d.ts index 773784ae..bdd6d8df 100644 --- a/electron/electron-env.d.ts +++ b/electron/electron-env.d.ts @@ -88,6 +88,7 @@ interface Window { hudOverlayDrag: (phase: "start" | "move" | "end", screenX: number, screenY: number) => void; hudOverlayHide: () => void; hudOverlayClose: () => void; + hudOverlayRendererReady: () => void; setHudOverlayExpanded: (expanded: boolean) => void; setHudOverlayCompactWidth: (width: number) => void; setHudOverlayMeasuredHeight: (height: number, expanded: boolean) => void; diff --git a/electron/preload.ts b/electron/preload.ts index 4d988b35..5a9f5ea7 100644 --- a/electron/preload.ts +++ b/electron/preload.ts @@ -84,6 +84,9 @@ contextBridge.exposeInMainWorld("electronAPI", { hudOverlayClose: () => { ipcRenderer.send("hud-overlay-close"); }, + hudOverlayRendererReady: () => { + ipcRenderer.send("hud-overlay-renderer-ready"); + }, setHudOverlayExpanded: (expanded: boolean) => { ipcRenderer.send("set-hud-overlay-expanded", expanded); }, diff --git a/electron/windows.ts b/electron/windows.ts index 37f4853b..fff60a07 100644 --- a/electron/windows.ts +++ b/electron/windows.ts @@ -364,6 +364,7 @@ ipcMain.handle("set-hud-overlay-capture-protection", (_event, enabled: boolean) export function createHudOverlayWindow(): BrowserWindow { loadHudOverlayCaptureProtectionSetting(); const initialBounds = getHudOverlayBounds(); + let hasShownHudWindow = false; const win = new BrowserWindow({ width: initialBounds.width, @@ -372,6 +373,7 @@ export function createHudOverlayWindow(): BrowserWindow { y: initialBounds.y, frame: false, transparent: true, + backgroundColor: "#00000000", resizable: false, alwaysOnTop: true, skipTaskbar: true, @@ -386,6 +388,23 @@ export function createHudOverlayWindow(): BrowserWindow { }, }); + const showHudWindow = () => { + if (hasShownHudWindow || win.isDestroyed()) { + return; + } + hasShownHudWindow = true; + win.show(); + win.moveTop(); + if (process.platform === "win32" && isHudOverlayMousePassthroughSupported()) { + win.setIgnoreMouseEvents(false); + setTimeout(() => { + if (!win.isDestroyed()) { + win.setIgnoreMouseEvents(true, { forward: true }); + } + }, 50); + } + }; + if (isHudOverlayCaptureProtectionSupported()) { win.setContentProtection(hudOverlayHiddenFromCapture); } @@ -415,32 +434,18 @@ export function createHudOverlayWindow(): BrowserWindow { win.webContents.on("did-finish-load", () => { win?.webContents.send("main-process-message", new Date().toLocaleString()); + // Safety fallback if renderer-ready signal never arrives. setTimeout(() => { - if (!win.isDestroyed()) { - win.show(); - win.moveTop(); - if (process.platform === "win32" && isHudOverlayMousePassthroughSupported()) { - win.setIgnoreMouseEvents(false); - setTimeout(() => { - if (!win.isDestroyed()) { - win.setIgnoreMouseEvents(true, { forward: true }); - } - }, 50); - } - } - }, 100); + showHudWindow(); + }, 1800); }); - // Safety net: on Linux the renderer may fail to fire did-finish-load - // (e.g. GPU/VAAPI errors). Show the window after ready-to-show as fallback. - win.once("ready-to-show", () => { - setTimeout(() => { - if (!win.isDestroyed() && !win.isVisible()) { - win.show(); - win.moveTop(); - } - }, 500); - }); + const handleHudRendererReady = () => { + if (!win.isDestroyed()) { + showHudWindow(); + } + }; + ipcMain.on("hud-overlay-renderer-ready", handleHudRendererReady); hudOverlayWindow = win; @@ -480,6 +485,7 @@ export function createHudOverlayWindow(): BrowserWindow { screen.on("display-metrics-changed", handleDisplayMetricsChanged); win.on("closed", () => { + ipcMain.removeListener("hud-overlay-renderer-ready", handleHudRendererReady); screen.removeListener("display-removed", handleDisplayRemoved); screen.removeListener("display-metrics-changed", handleDisplayMetricsChanged); if (hudOverlayWindow === win) { diff --git a/src/components/launch/LaunchWindow.module.css b/src/components/launch/LaunchWindow.module.css index 3764270a..9d48ba0b 100644 --- a/src/components/launch/LaunchWindow.module.css +++ b/src/components/launch/LaunchWindow.module.css @@ -104,11 +104,13 @@ width: 300px; max-height: 400px; overflow-y: auto; - background: var(--launch-panel, rgba(34, 40, 56, 0.62)); + background: var(--launch-surface, rgba(18, 18, 24, 0.97)); border: 1px solid var(--launch-border, rgba(255, 255, 255, 0.07)); border-radius: 16px; - backdrop-filter: blur(18px) saturate(140%); - -webkit-backdrop-filter: blur(18px) saturate(140%); + backdrop-filter: blur(var(--launch-popover-blur, 24px)) + saturate(var(--launch-popover-saturate, 145%)); + -webkit-backdrop-filter: blur(var(--launch-popover-blur, 24px)) + saturate(var(--launch-popover-saturate, 145%)); padding: 8px; margin-top: auto; margin-bottom: 8px; diff --git a/src/components/launch/LaunchWindow.tsx b/src/components/launch/LaunchWindow.tsx index 480c7c81..11c14770 100644 --- a/src/components/launch/LaunchWindow.tsx +++ b/src/components/launch/LaunchWindow.tsx @@ -186,6 +186,11 @@ export function LaunchWindow() { const supportsHudCaptureProtection = platform !== "linux"; + useEffect(() => { + // Tell main process to reveal the HUD only after renderer mounted. + window.electronAPI?.hudOverlayRendererReady?.(); + }, []); + useEffect(() => { if (!selectedDeviceId) { return; diff --git a/src/components/launch/SourceSelector.css b/src/components/launch/SourceSelector.css index 24d6ae7e..225b0ac8 100644 --- a/src/components/launch/SourceSelector.css +++ b/src/components/launch/SourceSelector.css @@ -1,9 +1,11 @@ .source-selector-popover { - background: var(--launch-panel, rgba(34, 40, 56, 0.62)); + background: var(--launch-surface, rgba(18, 18, 24, 0.97)); border: 1px solid var(--launch-border, rgba(255, 255, 255, 0.07)); border-radius: 16px; - backdrop-filter: blur(18px) saturate(140%); - -webkit-backdrop-filter: blur(18px) saturate(140%); + backdrop-filter: blur(var(--launch-popover-blur, 24px)) + saturate(var(--launch-popover-saturate, 145%)); + -webkit-backdrop-filter: blur(var(--launch-popover-blur, 24px)) + saturate(var(--launch-popover-saturate, 145%)); box-shadow: 0 16px 40px rgba(0, 0, 0, 0.28), 0 2px 12px rgba(0, 0, 0, 0.12), diff --git a/src/components/launch/launchTheme.css b/src/components/launch/launchTheme.css index 06ff9586..f1b8e3ef 100644 --- a/src/components/launch/launchTheme.css +++ b/src/components/launch/launchTheme.css @@ -1,7 +1,9 @@ .launch-theme { - --launch-surface: rgba(26, 28, 36, 0.92); + --launch-surface: rgba(18, 18, 24, 0.97); --launch-panel: rgba(18, 18, 24, 0.82); --launch-border: rgba(255, 255, 255, 0.07); + --launch-popover-blur: 24px; + --launch-popover-saturate: 145%; --launch-border-strong: rgba(255, 255, 255, 0.12); --launch-text: #f3f6ff; --launch-text-muted: #aab3c7;