diff --git a/apps/desktop/pet-preload.cjs b/apps/desktop/pet-preload.cjs index a755d87a..e0282679 100644 --- a/apps/desktop/pet-preload.cjs +++ b/apps/desktop/pet-preload.cjs @@ -129,10 +129,6 @@ const applyScalePreview = (scale) => { }; const sendScalePreview = (scale) => { - if (scalePreviewTimer) return; - scalePreviewTimer = setTimeout(() => { - scalePreviewTimer = null; - }, 120); ipcRenderer.send("openpets:pet-scale-preview", { scale }); }; @@ -446,7 +442,7 @@ const installMouseInterop = () => { } } if (scaling) { - const newScale = scaleStartValue + (event.clientY - scaleStartY) * 0.003; + const newScale = scaleStartValue + (event.screenY - scaleStartY) * 0.003; const clamped = applyScalePreview(newScale); sendScalePreview(clamped); } @@ -461,7 +457,7 @@ const installMouseInterop = () => { if (event.button !== 0) return; event.preventDefault(); scaling = true; - scaleStartY = event.clientY; + scaleStartY = event.screenY; scaleStartValue = getCurrentSpriteScale(); setInteractiveHit(true); ipcRenderer.send("openpets:pet-scale-start"); diff --git a/apps/desktop/src/pet-window.ts b/apps/desktop/src/pet-window.ts index 94426828..c65de9f4 100644 --- a/apps/desktop/src/pet-window.ts +++ b/apps/desktop/src/pet-window.ts @@ -433,10 +433,7 @@ function installMousePassthroughAndDrag(window: BrowserWindow, hooks: PetWindowI dragging = null; petWindowDragging.set(window, false); debug("pet.window", "drag end", { windowId, position: window.isDestroyed() ? null : readWindowPosition(window) }); - if (!window.isDestroyed()) { - const scale = getAppStateSnapshot().preferences.petScale as PetScaleValue; - resizePetWindowForDisplay(window, scale, petWindowLastDisplay.get(window) ?? null); - } + // Do not resize/re-anchor after a drag — the user already positioned the window. if (wasDragging) onPetEvent?.("pet:dragEnd", {}); }; @@ -1469,33 +1466,41 @@ function sendBubbleLayout(window: BrowserWindow, layout: PetWindowBubbleLayout): }); } -function resizePetWindowForDisplay(window: BrowserWindow, scale: PetScaleValue, display: PetTransientDisplay | null): void { - if (window.isDestroyed()) return; - - const nextSize = getPetWindowSize(window, scale, display); - const anchor = readWindowPosition(window); - const nextY = nextSize.bubbleBelow - ? anchor.y - : anchor.y - Math.max(0, nextSize.height - defaultPetWindowSize.height); - const nextBounds = { - x: anchor.x - Math.round((nextSize.width - defaultPetWindowSize.width) / 2), - y: nextY, - width: nextSize.width, - height: nextSize.height, - }; +function computePetWindowBounds(window: BrowserWindow, nextSize: { readonly width: number; readonly height: number }): Electron.Rectangle | null { + if (window.isDestroyed()) return null; + const currentBounds = window.getBounds(); + const petBottom = 22; + const petCenterX = currentBounds.x + Math.round(currentBounds.width / 2); + const petFootY = currentBounds.y + currentBounds.height - petBottom; + const nextX = petCenterX - Math.round(nextSize.width / 2); + const nextY = petFootY - (nextSize.height - petBottom); const displayForBounds = screen.getDisplayNearestPoint({ - x: anchor.x + Math.round(defaultPetWindowSize.width / 2), - y: anchor.y + defaultPetWindowSize.height, + x: petCenterX, + y: currentBounds.y + currentBounds.height, }) ?? screen.getPrimaryDisplay(); const workArea = displayForBounds.workArea; const maxX = workArea.x + Math.max(0, workArea.width - nextSize.width); const maxY = workArea.y + Math.max(0, workArea.height - nextSize.height); - window.setBounds({ - x: Math.min(Math.max(nextBounds.x, workArea.x), maxX), - y: Math.min(Math.max(nextBounds.y, workArea.y), maxY), + return { + x: clampNumber(nextX, workArea.x, maxX), + y: clampNumber(nextY, workArea.y, maxY), width: nextSize.width, height: nextSize.height, - }, false); + }; +} + +function resizePetWindowForDisplay(window: BrowserWindow, scale: PetScaleValue, display: PetTransientDisplay | null): void { + if (window.isDestroyed()) return; + + const nextSize = getPetWindowSize(window, scale, display); + const currentBounds = window.getBounds(); + if (currentBounds.width === nextSize.width && currentBounds.height === nextSize.height) { + return; + } + + const nextBounds = computePetWindowBounds(window, nextSize); + if (!nextBounds) return; + window.setBounds(nextBounds, false); sendBubbleLayout(window, nextSize); } @@ -1506,26 +1511,9 @@ function resizePetWindowForScale(window: BrowserWindow, scale: PetScaleValue): v if (currentBounds.width === nextSize.width && currentBounds.height === nextSize.height) { return; } - const anchor = readWindowPosition(window); - const nextBounds = { - x: anchor.x - Math.round((nextSize.width - defaultPetWindowSize.width) / 2), - y: anchor.y - Math.max(0, nextSize.height - defaultPetWindowSize.height), - width: nextSize.width, - height: nextSize.height, - }; - const displayForBounds = screen.getDisplayNearestPoint({ - x: anchor.x + Math.round(defaultPetWindowSize.width / 2), - y: anchor.y + defaultPetWindowSize.height, - }) ?? screen.getPrimaryDisplay(); - const workArea = displayForBounds.workArea; - const maxX = workArea.x + Math.max(0, workArea.width - nextSize.width); - const maxY = workArea.y + Math.max(0, workArea.height - nextSize.height); - window.setBounds({ - x: Math.min(Math.max(nextBounds.x, workArea.x), maxX), - y: Math.min(Math.max(nextBounds.y, workArea.y), maxY), - width: nextSize.width, - height: nextSize.height, - }, false); + const nextBounds = computePetWindowBounds(window, nextSize); + if (!nextBounds) return; + window.setBounds(nextBounds, false); } interface PetWindowBubbleLayout {