From acf796b0749490fda3ceeb5065a72a151b9f355b Mon Sep 17 00:00:00 2001 From: KBCats Date: Sun, 15 Mar 2026 20:28:16 -0700 Subject: [PATCH] fix(hud): persist capture hiding review fixes --- electron/windows.ts | 66 +++++++- src/components/launch/LaunchWindow.tsx | 59 +++++-- src/vite-env.d.ts | 208 ------------------------- 3 files changed, 104 insertions(+), 229 deletions(-) diff --git a/electron/windows.ts b/electron/windows.ts index 2420743e..cf52206f 100644 --- a/electron/windows.ts +++ b/electron/windows.ts @@ -1,7 +1,8 @@ +import fs from "node:fs"; import { createRequire } from "node:module"; import path from "node:path"; import { fileURLToPath } from "node:url"; -import { BrowserWindow, ipcMain } from "electron"; +import { app, BrowserWindow, ipcMain } from "electron"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const nodeRequire = createRequire(import.meta.url); @@ -16,7 +17,50 @@ const WINDOW_ICON_PATH = path.join( ); let hudOverlayWindow: BrowserWindow | null = null; -let hudOverlayHiddenFromCapture = false; +let hudOverlayHiddenFromCapture = true; +let hudOverlayCaptureProtectionLoaded = false; + +const HUD_OVERLAY_SETTINGS_FILE = path.join(app.getPath("userData"), "hud-overlay-settings.json"); + +function isHudOverlayCaptureProtectionSupported(): boolean { + return process.platform !== "linux"; +} + +function loadHudOverlayCaptureProtectionSetting(): boolean { + if (hudOverlayCaptureProtectionLoaded) { + return hudOverlayHiddenFromCapture; + } + + hudOverlayCaptureProtectionLoaded = true; + + try { + if (!fs.existsSync(HUD_OVERLAY_SETTINGS_FILE)) { + return hudOverlayHiddenFromCapture; + } + + const raw = fs.readFileSync(HUD_OVERLAY_SETTINGS_FILE, "utf-8"); + const parsed = JSON.parse(raw) as { hiddenFromCapture?: unknown }; + if (typeof parsed.hiddenFromCapture === "boolean") { + hudOverlayHiddenFromCapture = parsed.hiddenFromCapture; + } + } catch { + // Ignore settings read failures and fall back to defaults. + } + + return hudOverlayHiddenFromCapture; +} + +function persistHudOverlayCaptureProtectionSetting(enabled: boolean): void { + try { + fs.writeFileSync( + HUD_OVERLAY_SETTINGS_FILE, + JSON.stringify({ hiddenFromCapture: enabled }, null, 2), + "utf-8", + ); + } catch { + // Ignore settings write failures and keep runtime state working. + } +} function getScreen() { return nodeRequire("electron").screen as typeof import("electron").screen; @@ -29,16 +73,24 @@ ipcMain.on("hud-overlay-hide", () => { }); ipcMain.handle("get-hud-overlay-capture-protection", () => { + const enabled = loadHudOverlayCaptureProtectionSetting(); + return { success: true, - enabled: hudOverlayHiddenFromCapture, + enabled, }; }); ipcMain.handle("set-hud-overlay-capture-protection", (_event, enabled: boolean) => { + loadHudOverlayCaptureProtectionSetting(); hudOverlayHiddenFromCapture = Boolean(enabled); + persistHudOverlayCaptureProtectionSetting(hudOverlayHiddenFromCapture); - if (hudOverlayWindow && !hudOverlayWindow.isDestroyed()) { + if ( + isHudOverlayCaptureProtectionSupported() && + hudOverlayWindow && + !hudOverlayWindow.isDestroyed() + ) { hudOverlayWindow.setContentProtection(hudOverlayHiddenFromCapture); } @@ -49,6 +101,8 @@ ipcMain.handle("set-hud-overlay-capture-protection", (_event, enabled: boolean) }); export function createHudOverlayWindow(): BrowserWindow { + loadHudOverlayCaptureProtectionSetting(); + const primaryDisplay = getScreen().getPrimaryDisplay(); const { workArea } = primaryDisplay; @@ -81,7 +135,9 @@ export function createHudOverlayWindow(): BrowserWindow { }, }); - win.setContentProtection(hudOverlayHiddenFromCapture); + if (isHudOverlayCaptureProtectionSupported()) { + win.setContentProtection(hudOverlayHiddenFromCapture); + } win.webContents.on("did-finish-load", () => { win?.webContents.send("main-process-message", new Date().toLocaleString()); diff --git a/src/components/launch/LaunchWindow.tsx b/src/components/launch/LaunchWindow.tsx index f4f69e38..3c4e307c 100644 --- a/src/components/launch/LaunchWindow.tsx +++ b/src/components/launch/LaunchWindow.tsx @@ -85,7 +85,8 @@ export function LaunchWindow() { const [selectedSource, setSelectedSource] = useState("Screen"); const [hasSelectedSource, setHasSelectedSource] = useState(false); const [recordingsDirectory, setRecordingsDirectory] = useState(null); - const [hideHudFromCapture, setHideHudFromCapture] = useState(false); + const [hideHudFromCapture, setHideHudFromCapture] = useState(true); + const [platform, setPlatform] = useState(null); useEffect(() => { const checkSelectedSource = async () => { @@ -106,6 +107,27 @@ export function LaunchWindow() { return () => clearInterval(interval); }, []); + useEffect(() => { + let cancelled = false; + + const loadPlatform = async () => { + try { + const nextPlatform = await window.electronAPI.getPlatform(); + if (!cancelled) { + setPlatform(nextPlatform); + } + } catch (error) { + console.error("Failed to load platform:", error); + } + }; + + void loadPlatform(); + + return () => { + cancelled = true; + }; + }, []); + useEffect(() => { let cancelled = false; @@ -204,6 +226,7 @@ export function LaunchWindow() { ? recordingsDirectory.split(/[\\/]/).filter(Boolean).pop() || recordingsDirectory : "recordings"; const dividerClass = "mx-1 h-5 w-px shrink-0 bg-white/35"; + const supportsHudCaptureProtection = platform !== "linux"; const toggleMicrophone = () => { if (!recording) { @@ -266,21 +289,25 @@ export function LaunchWindow() {
- + {supportsHudCaptureProtection && ( + + )}