diff --git a/electron/main.ts b/electron/main.ts index b403c6c5..68829e43 100644 --- a/electron/main.ts +++ b/electron/main.ts @@ -232,7 +232,7 @@ function showHudOverlayFromTray() { if (process.platform === "win32" && isHudOverlayMousePassthroughSupported()) { hud.showInactive(); hud.moveTop(); - reassertHudOverlayMouseState(); + reassertHudOverlayMouseState({ interactiveGraceMs: 1200 }); return true; } diff --git a/electron/windows.ts b/electron/windows.ts index 486f6d82..6a5abf67 100644 --- a/electron/windows.ts +++ b/electron/windows.ts @@ -613,7 +613,11 @@ export function getHudOverlayWindow(): BrowserWindow | null { * hover detection on the HUD is immediately restored without requiring the * user to move their mouse over the bar. */ -export function reassertHudOverlayMousePassthrough(): void { +export function reassertHudOverlayMousePassthrough({ + interactiveGraceMs = 50, +}: { + interactiveGraceMs?: number; +} = {}): void { if (process.platform !== "win32" || !isHudOverlayMousePassthroughSupported()) { return; } @@ -639,7 +643,7 @@ export function reassertHudOverlayMousePassthrough(): void { if (!hud.isDestroyed()) { setHudOverlayMousePassthrough(hudOverlayIgnoringMouse); } - }, 50); + }, interactiveGraceMs); } export function setHudOverlayRecordingActive(recording: boolean): void { diff --git a/src/components/launch/hooks/useLaunchHudInteractionState.ts b/src/components/launch/hooks/useLaunchHudInteractionState.ts index 9d0160a7..d7c086b1 100644 --- a/src/components/launch/hooks/useLaunchHudInteractionState.ts +++ b/src/components/launch/hooks/useLaunchHudInteractionState.ts @@ -13,6 +13,23 @@ export function useLaunchHudInteractionState({ }) { const isMouseOverHudRef = useRef(false); const timeoutRef = useRef(null); + const lastInteractiveReassertAtRef = useRef(0); + + const setHudMouseInteractive = useCallback((force = false) => { + const now = performance.now(); + if ( + !force && + isMouseOverHudRef.current && + now - lastInteractiveReassertAtRef.current < 250 + ) { + return; + } + + isMouseOverHudRef.current = true; + lastInteractiveReassertAtRef.current = now; + if (timeoutRef.current) clearTimeout(timeoutRef.current); + window.electronAPI?.hudOverlaySetIgnoreMouse?.(false); + }, []); useEffect(() => { if (openId !== null) { @@ -28,7 +45,7 @@ export function useLaunchHudInteractionState({ }, [openId]); useEffect(() => { - const handleMouseOver = (e: globalThis.MouseEvent) => { + const handleMouseTracking = (e: globalThis.MouseEvent) => { const target = e.target as HTMLElement | null; if (!target) return; const isInteractive = !!target.closest( @@ -36,9 +53,7 @@ export function useLaunchHudInteractionState({ ); if (isInteractive) { - isMouseOverHudRef.current = true; - if (timeoutRef.current) clearTimeout(timeoutRef.current); - window.electronAPI?.hudOverlaySetIgnoreMouse?.(false); + setHudMouseInteractive(); } else { isMouseOverHudRef.current = false; if (timeoutRef.current) clearTimeout(timeoutRef.current); @@ -55,20 +70,26 @@ export function useLaunchHudInteractionState({ } }; - window.addEventListener("mouseover", handleMouseOver); - return () => window.removeEventListener("mouseover", handleMouseOver); - }, [isHudDraggingRef, isWebcamPreviewDraggingRef, webcamPreviewDragStartRef]); + window.addEventListener("mouseover", handleMouseTracking); + window.addEventListener("mousemove", handleMouseTracking); + return () => { + window.removeEventListener("mouseover", handleMouseTracking); + window.removeEventListener("mousemove", handleMouseTracking); + }; + }, [ + isHudDraggingRef, + isWebcamPreviewDraggingRef, + setHudMouseInteractive, + webcamPreviewDragStartRef, + ]); const beginInteractiveHudAction = useCallback(() => { - isMouseOverHudRef.current = true; - window.electronAPI?.hudOverlaySetIgnoreMouse?.(false); - }, []); + setHudMouseInteractive(true); + }, [setHudMouseInteractive]); const handleHudMouseEnter = useCallback(() => { - isMouseOverHudRef.current = true; - if (timeoutRef.current) clearTimeout(timeoutRef.current); - window.electronAPI?.hudOverlaySetIgnoreMouse?.(false); - }, []); + setHudMouseInteractive(true); + }, [setHudMouseInteractive]); const handleHudMouseLeave = useCallback( (event: MouseEvent) => {