From fcccd1def641b816c9ab69dc716e8d690b0aba2c Mon Sep 17 00:00:00 2001 From: wiiiii123 Date: Tue, 21 Apr 2026 02:54:00 +0700 Subject: [PATCH] fix(launch): use live HUD bounds after drag --- src/components/launch/LaunchWindow.tsx | 33 +++++++----- .../launch/hudMousePassthrough.test.ts | 51 +++++++++++-------- src/components/launch/hudMousePassthrough.ts | 16 ++++++ 3 files changed, 65 insertions(+), 35 deletions(-) diff --git a/src/components/launch/LaunchWindow.tsx b/src/components/launch/LaunchWindow.tsx index 31ffd0c7..a9403304 100644 --- a/src/components/launch/LaunchWindow.tsx +++ b/src/components/launch/LaunchWindow.tsx @@ -41,7 +41,10 @@ import { ContentClamp } from "../ui/content-clamp"; import ProjectBrowserDialog, { type ProjectLibraryEntry, } from "../video-editor/ProjectBrowserDialog"; -import { shouldRestoreHudMousePassthroughAfterDrag } from "./hudMousePassthrough"; +import { + mergeHudInteractiveBounds, + shouldRestoreHudMousePassthroughAfterDrag, +} from "./hudMousePassthrough"; import styles from "./LaunchWindow.module.css"; interface DesktopSource { @@ -466,21 +469,25 @@ export function LaunchWindow() { if (event.currentTarget.hasPointerCapture(event.pointerId)) { event.currentTarget.releasePointerCapture(event.pointerId); } - const hudBounds = hudContentRef.current?.getBoundingClientRect(); - if ( - wasDragging && - shouldRestoreHudMousePassthroughAfterDrag( - hudBounds + const hudBounds = mergeHudInteractiveBounds( + [ + dropdownRef.current?.getBoundingClientRect(), + hudBarRef.current?.getBoundingClientRect(), + recordingWebcamPreviewContainerRef.current?.getBoundingClientRect(), + ].map((bounds) => + bounds ? { - left: hudBounds.left, - top: hudBounds.top, - right: hudBounds.right, - bottom: hudBounds.bottom, + left: bounds.left, + top: bounds.top, + right: bounds.right, + bottom: bounds.bottom, } : null, - event.clientX, - event.clientY, - ) + ), + ); + if ( + wasDragging && + shouldRestoreHudMousePassthroughAfterDrag(hudBounds, event.clientX, event.clientY) ) { window.electronAPI?.hudOverlaySetIgnoreMouse?.(true); } diff --git a/src/components/launch/hudMousePassthrough.test.ts b/src/components/launch/hudMousePassthrough.test.ts index bc17e697..9490b054 100644 --- a/src/components/launch/hudMousePassthrough.test.ts +++ b/src/components/launch/hudMousePassthrough.test.ts @@ -1,36 +1,43 @@ import { describe, expect, it } from "vitest"; -import { shouldRestoreHudMousePassthroughAfterDrag } from "./hudMousePassthrough"; +import { + mergeHudInteractiveBounds, + shouldRestoreHudMousePassthroughAfterDrag, +} from "./hudMousePassthrough"; + +const hudBounds = { left: 100, top: 200, right: 300, bottom: 260 }; + +describe("mergeHudInteractiveBounds", () => { + it("returns null when there are no interactive bounds", () => { + expect(mergeHudInteractiveBounds([null, undefined])).toBeNull(); + }); + + it("merges the dropdown, bar, and webcam preview bounds", () => { + expect( + mergeHudInteractiveBounds([ + { left: 120, top: 220, right: 260, bottom: 320 }, + { left: 100, top: 200, right: 300, bottom: 260 }, + { left: 140, top: 280, right: 340, bottom: 430 }, + ]), + ).toEqual({ left: 100, top: 200, right: 340, bottom: 430 }); + }); +}); describe("shouldRestoreHudMousePassthroughAfterDrag", () => { it("keeps the HUD interactive when the pointer is still inside the HUD", () => { - expect( - shouldRestoreHudMousePassthroughAfterDrag( - { left: 100, top: 200, right: 300, bottom: 260 }, - 180, - 230, - ), - ).toBe(false); + expect(shouldRestoreHudMousePassthroughAfterDrag(hudBounds, 180, 230)).toBe(false); }); it("keeps the HUD interactive when the pointer ends on the HUD edge", () => { - expect( - shouldRestoreHudMousePassthroughAfterDrag( - { left: 100, top: 200, right: 300, bottom: 260 }, - 300, - 260, - ), - ).toBe(false); + expect(shouldRestoreHudMousePassthroughAfterDrag(hudBounds, 100, 200)).toBe(false); + expect(shouldRestoreHudMousePassthroughAfterDrag(hudBounds, 300, 260)).toBe(false); }); it("restores passthrough when the pointer ends outside the HUD", () => { - expect( - shouldRestoreHudMousePassthroughAfterDrag( - { left: 100, top: 200, right: 300, bottom: 260 }, - 301, - 261, - ), - ).toBe(true); + expect(shouldRestoreHudMousePassthroughAfterDrag(hudBounds, 99, 230)).toBe(true); + expect(shouldRestoreHudMousePassthroughAfterDrag(hudBounds, 180, 199)).toBe(true); + expect(shouldRestoreHudMousePassthroughAfterDrag(hudBounds, 301, 230)).toBe(true); + expect(shouldRestoreHudMousePassthroughAfterDrag(hudBounds, 180, 261)).toBe(true); }); it("restores passthrough when no HUD bounds are available", () => { diff --git a/src/components/launch/hudMousePassthrough.ts b/src/components/launch/hudMousePassthrough.ts index 50d190d4..5d972f55 100644 --- a/src/components/launch/hudMousePassthrough.ts +++ b/src/components/launch/hudMousePassthrough.ts @@ -5,6 +5,22 @@ export interface HudInteractiveBounds { bottom: number; } +export function mergeHudInteractiveBounds( + bounds: Array, +): HudInteractiveBounds | null { + const presentBounds = bounds.filter((value): value is HudInteractiveBounds => Boolean(value)); + if (presentBounds.length === 0) { + return null; + } + + return presentBounds.reduce((merged, current) => ({ + left: Math.min(merged.left, current.left), + top: Math.min(merged.top, current.top), + right: Math.max(merged.right, current.right), + bottom: Math.max(merged.bottom, current.bottom), + })); +} + export function shouldRestoreHudMousePassthroughAfterDrag( bounds: HudInteractiveBounds | null, clientX: number,