diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 68b5b52..6117d3e 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -181,7 +181,7 @@ pub fn run() { shortcut::change_audio_feedback_setting, shortcut::change_translate_to_english_setting, shortcut::change_selected_language_setting, - shortcut::change_show_overlay_setting, + shortcut::change_overlay_position_setting, shortcut::change_debug_mode_setting, shortcut::suspend_binding, shortcut::resume_binding, diff --git a/src-tauri/src/settings.rs b/src-tauri/src/settings.rs index 3e3fa6a..983af26 100644 --- a/src-tauri/src/settings.rs +++ b/src-tauri/src/settings.rs @@ -30,8 +30,8 @@ pub struct AppSettings { pub translate_to_english: bool, #[serde(default = "default_selected_language")] pub selected_language: String, - #[serde(default = "default_show_overlay")] - pub show_overlay: bool, + #[serde(default = "default_overlay_position")] + pub overlay_position: String, #[serde(default = "default_debug_mode")] pub debug_mode: bool, } @@ -58,9 +58,9 @@ fn default_selected_language() -> String { "auto".to_string() } -fn default_show_overlay() -> bool { - // Default to true - users expect visual feedback by default - true +fn default_overlay_position() -> String { + // Default to "bottom" - less intrusive position + "bottom".to_string() } fn default_debug_mode() -> bool { @@ -114,7 +114,7 @@ pub fn get_default_settings() -> AppSettings { selected_output_device: None, translate_to_english: false, selected_language: "auto".to_string(), - show_overlay: true, + overlay_position: "bottom".to_string(), debug_mode: false, } } diff --git a/src-tauri/src/shortcut.rs b/src-tauri/src/shortcut.rs index 7dc7da2..5017c13 100644 --- a/src-tauri/src/shortcut.rs +++ b/src-tauri/src/shortcut.rs @@ -135,10 +135,14 @@ pub fn change_selected_language_setting(app: AppHandle, language: String) -> Res } #[tauri::command] -pub fn change_show_overlay_setting(app: AppHandle, enabled: bool) -> Result<(), String> { +pub fn change_overlay_position_setting(app: AppHandle, position: String) -> Result<(), String> { let mut settings = settings::get_settings(&app); - settings.show_overlay = enabled; + settings.overlay_position = position; settings::write_settings(&app, settings); + + // Update overlay position without recreating window + crate::utils::update_overlay_position(&app); + Ok(()) } diff --git a/src-tauri/src/utils.rs b/src-tauri/src/utils.rs index 0c09739..e22d438 100644 --- a/src-tauri/src/utils.rs +++ b/src-tauri/src/utils.rs @@ -338,53 +338,66 @@ fn play_audio_file( /* OVERLAY MANAGEMENT */ /* ──────────────────────────────────────────────────────────────── */ -/// Creates the recording overlay window and keeps it hidden by default -pub fn create_recording_overlay(app_handle: &AppHandle) { - // Get work area dimensions for positioning (respects taskbars, docks, etc.) +const OVERLAY_WIDTH: f64 = 172.0; +const OVERLAY_HEIGHT: f64 = 36.0; +const OVERLAY_OFFSET: f64 = 46.0; + +fn calculate_overlay_position(app_handle: &AppHandle) -> Option<(f64, f64)> { if let Ok(monitors) = app_handle.primary_monitor() { if let Some(monitor) = monitors { - const OVERLAY_WIDTH: f64 = 172.0; - const OVERLAY_HEIGHT: f64 = 36.0; - const OVERLAY_TOP_OFFSET: f64 = 46.0; - let work_area = monitor.work_area(); let scale = monitor.scale_factor(); let work_area_width = work_area.size.width as f64 / scale; + let work_area_height = work_area.size.height as f64 / scale; let work_area_x = work_area.position.x as f64 / scale; let work_area_y = work_area.position.y as f64 / scale; - // Position at top center of work area - let x = work_area_x + (work_area_width - OVERLAY_WIDTH) / 2.0; - let y = work_area_y + OVERLAY_TOP_OFFSET; + let settings = settings::get_settings(app_handle); + let overlay_position = settings.overlay_position.as_str(); - match WebviewWindowBuilder::new( - app_handle, - "recording_overlay", - tauri::WebviewUrl::App("src/overlay/index.html".into()), - ) - .title("Recording") - .position(x, y) - .resizable(false) - .inner_size(OVERLAY_WIDTH, OVERLAY_HEIGHT) - .shadow(false) - .maximizable(false) - .minimizable(false) - .closable(false) - .accept_first_mouse(true) - .decorations(false) - .always_on_top(true) - .skip_taskbar(true) - .transparent(true) - .focused(false) - .visible(false) // Start hidden - .build() - { - Ok(_window) => { - debug!("Recording overlay window created successfully (hidden)"); - } - Err(e) => { - debug!("Failed to create recording overlay window: {}", e); - } + let x = work_area_x + (work_area_width - OVERLAY_WIDTH) / 2.0; + let y = match overlay_position { + "top" => work_area_y + OVERLAY_OFFSET, + "bottom" => work_area_y + work_area_height - OVERLAY_HEIGHT - OVERLAY_OFFSET, + _ => work_area_y + work_area_height - OVERLAY_HEIGHT - OVERLAY_OFFSET, + }; + + return Some((x, y)); + } + } + None +} + +/// Creates the recording overlay window and keeps it hidden by default +pub fn create_recording_overlay(app_handle: &AppHandle) { + if let Some((x, y)) = calculate_overlay_position(app_handle) { + match WebviewWindowBuilder::new( + app_handle, + "recording_overlay", + tauri::WebviewUrl::App("src/overlay/index.html".into()), + ) + .title("Recording") + .position(x, y) + .resizable(false) + .inner_size(OVERLAY_WIDTH, OVERLAY_HEIGHT) + .shadow(false) + .maximizable(false) + .minimizable(false) + .closable(false) + .accept_first_mouse(true) + .decorations(false) + .always_on_top(true) + .skip_taskbar(true) + .transparent(true) + .focused(false) + .visible(false) + .build() + { + Ok(_window) => { + debug!("Recording overlay window created successfully (hidden)"); + } + Err(e) => { + debug!("Failed to create recording overlay window: {}", e); } } } @@ -392,9 +405,9 @@ pub fn create_recording_overlay(app_handle: &AppHandle) { /// Shows the recording overlay window with fade-in animation pub fn show_recording_overlay(app_handle: &AppHandle) { - // Check if show_overlay is enabled in settings + // Check if overlay should be shown based on position setting let settings = settings::get_settings(app_handle); - if !settings.show_overlay { + if settings.overlay_position == "none" { return; } @@ -407,9 +420,9 @@ pub fn show_recording_overlay(app_handle: &AppHandle) { /// Shows the transcribing overlay window pub fn show_transcribing_overlay(app_handle: &AppHandle) { - // Check if show_overlay is enabled in settings + // Check if overlay should be shown based on position setting let settings = settings::get_settings(app_handle); - if !settings.show_overlay { + if settings.overlay_position == "none" { return; } @@ -420,10 +433,19 @@ pub fn show_transcribing_overlay(app_handle: &AppHandle) { } } +/// Updates the overlay window position based on current settings +pub fn update_overlay_position(app_handle: &AppHandle) { + if let Some(overlay_window) = app_handle.get_webview_window("recording_overlay") { + if let Some((x, y)) = calculate_overlay_position(app_handle) { + let _ = overlay_window.set_position(tauri::Position::Logical(tauri::LogicalPosition { x, y })); + } + } +} + /// Hides the recording overlay window with fade-out animation pub fn hide_recording_overlay(app_handle: &AppHandle) { - // Check if show_overlay is enabled in settings - if disabled, the overlay shouldn't be shown anyway - // but we still want to hide it in case the setting was changed while recording + // Always hide the overlay regardless of settings - if setting was changed while recording, + // we still want to hide it properly if let Some(overlay_window) = app_handle.get_webview_window("recording_overlay") { // Emit event to trigger fade-out animation let _ = overlay_window.emit("hide-overlay", ()); diff --git a/src/components/settings/MicrophoneSelector.tsx b/src/components/settings/MicrophoneSelector.tsx index 1ddf5eb..a958719 100644 --- a/src/components/settings/MicrophoneSelector.tsx +++ b/src/components/settings/MicrophoneSelector.tsx @@ -55,6 +55,11 @@ export const MicrophoneSelector: React.FC = ({ console.log("Microphone reset to default"); }; + const microphoneOptions = audioDevices.map(device => ({ + value: device.name, + label: device.name + })); + return ( = ({ >
- {isOpen && !disabled && (
- {devices.length === 0 ? ( -
- No microphones found -
+ {options.length === 0 ? ( +
No options found
) : ( - devices.map((device) => ( + options.map((option) => ( )) )} diff --git a/src/hooks/useSettings.ts b/src/hooks/useSettings.ts index 2223ba8..17588bc 100644 --- a/src/hooks/useSettings.ts +++ b/src/hooks/useSettings.ts @@ -199,8 +199,8 @@ export const useSettings = (): UseSettingsReturn => { language: value, }); break; - case "show_overlay": - await invoke("change_show_overlay_setting", { enabled: value }); + case "overlay_position": + await invoke("change_overlay_position_setting", { position: value }); break; case "debug_mode": await invoke("change_debug_mode_setting", { enabled: value }); @@ -249,7 +249,7 @@ export const useSettings = (): UseSettingsReturn => { selected_output_device: "Default", translate_to_english: false, selected_language: "auto", - show_overlay: true, + overlay_position: "bottom", debug_mode: false, }; diff --git a/src/lib/types.ts b/src/lib/types.ts index 5ac069c..4ecd42e 100644 --- a/src/lib/types.ts +++ b/src/lib/types.ts @@ -29,7 +29,7 @@ export const SettingsSchema = z.object({ selected_output_device: z.string().nullable().optional(), translate_to_english: z.boolean(), selected_language: z.string(), - show_overlay: z.boolean(), + overlay_position: z.string(), debug_mode: z.boolean(), });