diff --git a/electron/windows.ts b/electron/windows.ts index 5e52e92e..78c73f5e 100644 --- a/electron/windows.ts +++ b/electron/windows.ts @@ -210,7 +210,25 @@ function applyHudOverlayBounds(expanded: boolean) { hudOverlayExpanded = expanded; - hudOverlayWindow.setBounds(getHudOverlayBounds(expanded), false); + const computed = getHudOverlayBounds(expanded); + + if (hudUserPosition) { + // Resize in-place at the user's dragged position, clamped so the + // window stays fully within the current display's work area. + const { workArea } = getHudOverlayDisplay(); + const x = Math.max( + workArea.x, + Math.min(hudUserPosition.x, workArea.x + workArea.width - computed.width), + ); + const y = Math.max( + workArea.y, + Math.min(hudUserPosition.y, workArea.y + workArea.height - computed.height), + ); + hudOverlayWindow.setBounds({ x, y, width: computed.width, height: computed.height }, false); + } else { + hudOverlayWindow.setBounds(computed, false); + } + positionUpdateToastWindow(); if (!hudOverlayWindow.isVisible()) { return; @@ -272,6 +290,10 @@ ipcMain.on("hud-overlay-set-ignore-mouse", (_event, ignore: boolean) => { } }); +// When the user drags the HUD, remember their chosen position so that +// subsequent size changes (e.g. idle → recording UI swap) resize in-place +// instead of snapping back to the default centered location. +let hudUserPosition: { x: number; y: number } | null = null; let hudDragOffset: { x: number; y: number } | null = null; let hudDragLastCursor: { x: number; y: number } | null = null; let hudDragFixedSize: { width: number; height: number } | null = null; @@ -304,6 +326,9 @@ ipcMain.on("hud-overlay-drag", (_event, phase: string, screenX: number, screenY: false, ); } else if (phase === "end") { + const finalBounds = hudOverlayWindow.getBounds(); + hudUserPosition = { x: finalBounds.x, y: finalBounds.y }; + hudDragOffset = null; hudDragLastCursor = null; hudDragFixedSize = null; @@ -477,6 +502,29 @@ export function createHudOverlayWindow(): BrowserWindow { } }); + // Reset the user's saved HUD position when displays change so the bar + // doesn't end up stranded off-screen after a monitor is disconnected. + const screen = getScreen(); + screen.on("display-removed", () => { + hudUserPosition = null; + }); + screen.on("display-metrics-changed", () => { + if (hudUserPosition) { + const displays = screen.getAllDisplays(); + const onScreen = displays.some( + (d) => + hudUserPosition!.x >= d.workArea.x && + hudUserPosition!.x < d.workArea.x + d.workArea.width && + hudUserPosition!.y >= d.workArea.y && + hudUserPosition!.y < d.workArea.y + d.workArea.height, + ); + if (!onScreen) { + hudUserPosition = null; + applyHudOverlayBounds(hudOverlayExpanded); + } + } + }); + if (VITE_DEV_SERVER_URL) { win.loadURL(VITE_DEV_SERVER_URL + "?windowType=hud-overlay"); } else {