diff --git a/apps/desktop-tauri/src-tauri/src/floatbar/window.rs b/apps/desktop-tauri/src-tauri/src/floatbar/window.rs index 016ae16cce..9857016d11 100644 --- a/apps/desktop-tauri/src-tauri/src/floatbar/window.rs +++ b/apps/desktop-tauri/src-tauri/src/floatbar/window.rs @@ -447,8 +447,11 @@ pub fn resize( height: f64, click_through: bool, ) -> Result<(), String> { + let width = width.ceil().clamp(1.0, u32::MAX as f64) as u32; + let height = height.ceil().clamp(1.0, u32::MAX as f64) as u32; + window - .set_size(LogicalSize::new(width, height)) + .set_size(PhysicalSize::new(width, height)) .map_err(|e| e.to_string())?; apply_no_activate(window); apply_click_through(window, click_through); diff --git a/apps/desktop-tauri/src/floatbar/FloatBar.css b/apps/desktop-tauri/src/floatbar/FloatBar.css index cd35e79e60..188957ac36 100644 --- a/apps/desktop-tauri/src/floatbar/FloatBar.css +++ b/apps/desktop-tauri/src/floatbar/FloatBar.css @@ -22,6 +22,7 @@ body.floatbar-window #root { * group reads like a compact widget sitting on the Windows taskbar. */ .floatbar { display: inline-flex; + flex: none; align-items: center; justify-content: center; gap: calc(4px * var(--floatbar-scale, 1)); diff --git a/apps/desktop-tauri/src/floatbar/FloatBar.test.tsx b/apps/desktop-tauri/src/floatbar/FloatBar.test.tsx index c592f87451..fa99a7abcc 100644 --- a/apps/desktop-tauri/src/floatbar/FloatBar.test.tsx +++ b/apps/desktop-tauri/src/floatbar/FloatBar.test.tsx @@ -358,6 +358,46 @@ describe("FloatBar", () => { }); }); + it("resizes the native window in physical pixels at the WebView DPI", async () => { + tauriMocks.getCachedProviders.mockResolvedValue([]); + tauriMocks.getSettingsSnapshot.mockResolvedValue(settings()); + const originalDevicePixelRatio = window.devicePixelRatio; + Object.defineProperty(window, "devicePixelRatio", { + configurable: true, + value: 1.5, + }); + const rectSpy = vi + .spyOn(HTMLElement.prototype, "getBoundingClientRect") + .mockReturnValue({ + x: 0, + y: 0, + width: 100, + height: 20, + top: 0, + right: 100, + bottom: 20, + left: 0, + toJSON: () => ({}), + }); + + try { + renderFloatBar(bootstrap()); + + await waitFor(() => { + expect(coreMocks.invoke).toHaveBeenCalledWith("resize_float_bar", { + width: 162, + height: 42, + }); + }); + } finally { + rectSpy.mockRestore(); + Object.defineProperty(window, "devicePixelRatio", { + configurable: true, + value: originalDevicePixelRatio, + }); + } + }); + it("uses the localized reset formatter in pill tooltips", async () => { const resetsAt = new Date(Date.now() + 3 * 60 * 60_000 + 42 * 60_000).toISOString(); tauriMocks.getCachedProviders.mockResolvedValue([ diff --git a/apps/desktop-tauri/src/floatbar/FloatBar.tsx b/apps/desktop-tauri/src/floatbar/FloatBar.tsx index 6f892a2064..afb65e19e3 100644 --- a/apps/desktop-tauri/src/floatbar/FloatBar.tsx +++ b/apps/desktop-tauri/src/floatbar/FloatBar.tsx @@ -381,8 +381,14 @@ export default function FloatBar({ state }: { state: BootstrapState }) { resizeRafRef.current = null; const rect = el.getBoundingClientRect(); const padding = 8; - const w = Math.ceil(rect.width + padding); - const h = Math.ceil(rect.height + padding); + const dpr = + Number.isFinite(window.devicePixelRatio) && window.devicePixelRatio > 0 + ? window.devicePixelRatio + : 1; + // DOM measurements are CSS pixels; the native command accepts physical + // pixels so the window remains correctly sized on scaled displays. + const w = Math.ceil(Math.ceil(rect.width + padding) * dpr); + const h = Math.ceil(Math.ceil(rect.height + padding) * dpr); const last = lastResizeRef.current; if (last && Math.abs(last.w - w) <= 1 && Math.abs(last.h - h) <= 1) return; lastResizeRef.current = { w, h }; @@ -411,6 +417,12 @@ export default function FloatBar({ state }: { state: BootstrapState }) { return () => observer.disconnect(); }, [resizeToContent]); + useEffect(() => { + // Re-measure after moving onto a monitor with a different scale factor. + window.addEventListener("resize", resizeToContent); + return () => window.removeEventListener("resize", resizeToContent); + }, [resizeToContent]); + useEffect( () => () => { if (resizeRafRef.current !== null) {