From 1a2ec72fb4a4335300e12fe37da64c603ef999a1 Mon Sep 17 00:00:00 2001 From: Meng Zhang Date: Wed, 8 Oct 2025 15:33:01 -0700 Subject: [PATCH] feat: display overlay on active monitor (#175) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change updates the recording and transcribing overlay to appear on the monitor that currently contains the mouse cursor, rather than always appearing on the primary monitor. This improves the user experience for those with multi-monitor setups. 🤖 Generated with [Pochi](https://getpochi.com) Co-authored-by: Pochi --- bun.lockb | Bin 69758 -> 71142 bytes src-tauri/src/overlay.rs | 44 +++++++++++++++++++++++++++++++++++---- 2 files changed, 40 insertions(+), 4 deletions(-) diff --git a/bun.lockb b/bun.lockb index 08e5b1ffc7690ea3b104b3661a0dde1655e57e30..061edb0a7856e783ca100f99474a38d46f18db6d 100755 GIT binary patch delta 2812 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 diff --git a/src-tauri/src/overlay.rs b/src-tauri/src/overlay.rs index ca1b820..7361fc1 100644 --- a/src-tauri/src/overlay.rs +++ b/src-tauri/src/overlay.rs @@ -1,7 +1,8 @@ use crate::settings; use crate::settings::OverlayPosition; use log::debug; -use tauri::{AppHandle, Emitter, Manager, WebviewWindowBuilder}; +use enigo::{Enigo, Mouse}; +use tauri::{AppHandle, Emitter, Manager, PhysicalPosition, PhysicalSize, WebviewWindowBuilder}; const OVERLAY_WIDTH: f64 = 172.0; const OVERLAY_HEIGHT: f64 = 36.0; @@ -17,9 +18,41 @@ const OVERLAY_BOTTOM_OFFSET: f64 = 15.0; #[cfg(any(target_os = "windows", target_os = "linux"))] const OVERLAY_BOTTOM_OFFSET: f64 = 40.0; +fn get_monitor_with_cursor(app_handle: &AppHandle) -> Option { + let enigo = Enigo::new(&Default::default()); + if let Ok(enigo) = enigo { + if let Ok(mouse_location) = enigo.location() { + if let Ok(monitors) = app_handle.available_monitors() { + for monitor in monitors { + let is_within = is_mouse_within_monitor(mouse_location, monitor.position(), monitor.size()); + if is_within { + return Some(monitor); + } + } + } + } + } + + app_handle.primary_monitor().ok().flatten() +} + +fn is_mouse_within_monitor( + mouse_pos: (i32, i32), + monitor_pos: &PhysicalPosition, + monitor_size: &PhysicalSize, +) -> bool { + let (mouse_x, mouse_y) = mouse_pos; + let PhysicalPosition { x: monitor_x, y: monitor_y } = *monitor_pos; + let PhysicalSize { width: monitor_width, height: monitor_height } = *monitor_size; + + mouse_x >= monitor_x + && mouse_x < (monitor_x + monitor_width as i32) + && mouse_y >= monitor_y + && mouse_y < (monitor_y + monitor_height as i32) +} + fn calculate_overlay_position(app_handle: &AppHandle) -> Option<(f64, f64)> { - if let Ok(monitors) = app_handle.primary_monitor() { - if let Some(monitor) = monitors { + if let Some(monitor) = get_monitor_with_cursor(app_handle) { let work_area = monitor.work_area(); let scale = monitor.scale_factor(); let work_area_width = work_area.size.width as f64 / scale; @@ -40,7 +73,6 @@ fn calculate_overlay_position(app_handle: &AppHandle) -> Option<(f64, f64)> { return Some((x, y)); } - } None } @@ -87,6 +119,8 @@ pub fn show_recording_overlay(app_handle: &AppHandle) { return; } + update_overlay_position(app_handle); + if let Some(overlay_window) = app_handle.get_webview_window("recording_overlay") { let _ = overlay_window.show(); // Emit event to trigger fade-in animation with recording state @@ -102,6 +136,8 @@ pub fn show_transcribing_overlay(app_handle: &AppHandle) { return; } + update_overlay_position(app_handle); + if let Some(overlay_window) = app_handle.get_webview_window("recording_overlay") { let _ = overlay_window.show(); // Emit event to switch to transcribing state