diff --git a/examples/apps/screenpipe-app-tauri/components/recording-settings.tsx b/examples/apps/screenpipe-app-tauri/components/recording-settings.tsx index 18c64c9f..0ede57b8 100644 --- a/examples/apps/screenpipe-app-tauri/components/recording-settings.tsx +++ b/examples/apps/screenpipe-app-tauri/components/recording-settings.tsx @@ -24,6 +24,7 @@ import { Settings, useSettings } from "@/lib/hooks/use-settings"; import { useToast } from "@/components/ui/use-toast"; import { useHealthCheck } from "@/lib/hooks/use-health-check"; import { invoke } from "@tauri-apps/api/core"; +import { Badge } from "./ui/badge"; interface AudioDevice { name: string; @@ -124,7 +125,15 @@ export function RecordingSettings({ try { console.log("localSettings", localSettings); - await updateSettings(localSettings); + // Only update specific fields + const settingsToUpdate = { + audioTranscriptionEngine: localSettings.audioTranscriptionEngine, + ocrEngine: localSettings.ocrEngine, + monitorId: localSettings.monitorId, + audioDevices: localSettings.audioDevices, + }; + console.log("Settings to update:", settingsToUpdate); + await updateSettings(settingsToUpdate); await invoke("kill_all_sreenpipes"); @@ -200,7 +209,12 @@ export function RecordingSettings({ - deepgram + +
+ deepgram + cloud +
+
whisper-tiny whisper-large
@@ -220,7 +234,12 @@ export function RecordingSettings({ - unstructured + +
+ unstructured + cloud +
+
{currentPlatform !== "macos" && ( tesseract )} diff --git a/examples/apps/screenpipe-app-tauri/components/screenpipe-status.tsx b/examples/apps/screenpipe-app-tauri/components/screenpipe-status.tsx index cb425311..ef4ed952 100644 --- a/examples/apps/screenpipe-app-tauri/components/screenpipe-status.tsx +++ b/examples/apps/screenpipe-app-tauri/components/screenpipe-status.tsx @@ -99,9 +99,9 @@ const DevModeSettings = () => { const { settings, updateSettings } = useSettings(); const [localSettings, setLocalSettings] = useState(settings); const handleDevModeToggle = (checked: boolean) => { - console.log("checked", checked); - setLocalSettings({ ...localSettings, devMode: checked }); - updateSettings({ ...localSettings, devMode: checked }); + + setLocalSettings((prev) => ({ ...prev, devMode: checked })); + updateSettings({ devMode: checked }); }; const [isLoading, setIsLoading] = useState(false); const { toast } = useToast(); diff --git a/examples/apps/screenpipe-app-tauri/components/settings.tsx b/examples/apps/screenpipe-app-tauri/components/settings.tsx index a7415ebb..667b713b 100644 --- a/examples/apps/screenpipe-app-tauri/components/settings.tsx +++ b/examples/apps/screenpipe-app-tauri/components/settings.tsx @@ -36,31 +36,34 @@ export function Settings({ className }: { className?: string }) { const [showApiKey, setShowApiKey] = React.useState(false); const handleApiKeyChange = (e: React.ChangeEvent) => { - setLocalSettings({ ...localSettings, openaiApiKey: e.target.value }); - updateSettings({ ...localSettings, openaiApiKey: e.target.value }); + const newValue = e.target.value; + setLocalSettings((prev) => ({ ...prev, openaiApiKey: newValue })); + updateSettings({ openaiApiKey: newValue }); }; const handleOllamaToggle = (checked: boolean) => { - console.log("checked", checked); - setLocalSettings({ ...localSettings, useOllama: checked }); - updateSettings({ ...localSettings, useOllama: checked }); + setLocalSettings((prev) => ({ ...prev, useOllama: checked })); + updateSettings({ useOllama: checked }); }; const handleOllamaUrlChange = (e: React.ChangeEvent) => { - setLocalSettings({ ...localSettings, ollamaUrl: e.target.value }); - updateSettings({ ...localSettings, ollamaUrl: e.target.value }); + const newValue = e.target.value; + setLocalSettings((prev) => ({ ...prev, ollamaUrl: newValue })); + updateSettings({ ollamaUrl: newValue }); }; const handleModelChange = (e: React.ChangeEvent) => { - setLocalSettings({ ...localSettings, aiModel: e.target.value }); - updateSettings({ ...localSettings, aiModel: e.target.value }); + const newValue = e.target.value; + setLocalSettings((prev) => ({ ...prev, aiModel: newValue })); + updateSettings({ aiModel: newValue }); }; const handleCustomPromptChange = ( e: React.ChangeEvent ) => { - setLocalSettings({ ...localSettings, customPrompt: e.target.value }); - updateSettings({ ...localSettings, customPrompt: e.target.value }); + const newValue = e.target.value; + setLocalSettings((prev) => ({ ...prev, customPrompt: newValue })); + updateSettings({ customPrompt: newValue }); }; const handleResetCustomPrompt = () => { @@ -239,9 +242,6 @@ export function Settings({ className }: { className?: string }) { - - - diff --git a/examples/apps/screenpipe-app-tauri/lib/hooks/use-settings.tsx b/examples/apps/screenpipe-app-tauri/lib/hooks/use-settings.tsx index d7c40286..fa708af7 100644 --- a/examples/apps/screenpipe-app-tauri/lib/hooks/use-settings.tsx +++ b/examples/apps/screenpipe-app-tauri/lib/hooks/use-settings.tsx @@ -96,8 +96,9 @@ export function useSettings() { const savedUserId = ((await store!.get("userId")) as string) || ""; const savedCustomPrompt = ((await store!.get("customPrompt")) as string) || ""; - const savedDevMode = - ((await store!.get("devMode")) as boolean) || false; + let savedDevMode = (await store!.get("devMode")) as boolean; + + savedDevMode = savedDevMode === true; const savedAudioTranscriptionEngine = ((await store!.get("audioTranscriptionEngine")) as string) || "whisper-tiny"; @@ -138,23 +139,24 @@ export function useSettings() { } try { - const updatedSettings = { ...settings, ...newSettings }; + // Only update the fields that are explicitly provided in newSettings + const updatedSettings = { ...settings }; + for (const key in newSettings) { + if (Object.prototype.hasOwnProperty.call(newSettings, key)) { + // @ts-ignore + updatedSettings[key as keyof Settings] = + newSettings[key as keyof Settings]!; + } + } + setSettings(updatedSettings); - await store!.set("openaiApiKey", updatedSettings.openaiApiKey); - await store!.set("useOllama", updatedSettings.useOllama); - await store!.set("ollamaUrl", updatedSettings.ollamaUrl); - await store!.set("aiModel", updatedSettings.aiModel); - await store!.set("installedPipes", updatedSettings.installedPipes); - await store!.set("userId", updatedSettings.userId); - await store!.set("customPrompt", updatedSettings.customPrompt); - await store!.set("devMode", updatedSettings.devMode); - await store!.set( - "audioTranscriptionEngine", - updatedSettings.audioTranscriptionEngine - ); - await store!.set("ocrEngine", updatedSettings.ocrEngine); - await store!.set("monitorId", updatedSettings.monitorId); - await store!.set("audioDevices", updatedSettings.audioDevices); + // Only update the store for the fields that were changed + for (const key in newSettings) { + if (Object.prototype.hasOwnProperty.call(newSettings, key)) { + await store!.set(key, updatedSettings[key as keyof Settings]); + } + } + await store!.save(); } catch (error) { console.error("Failed to update settings:", error); diff --git a/examples/apps/screenpipe-app-tauri/src-tauri/Cargo.toml b/examples/apps/screenpipe-app-tauri/src-tauri/Cargo.toml index 98c5ea1b..492f4e4b 100644 --- a/examples/apps/screenpipe-app-tauri/src-tauri/Cargo.toml +++ b/examples/apps/screenpipe-app-tauri/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "screenpipe-app" -version = "0.1.53" +version = "0.1.54" description = "" authors = ["you"] license = "" diff --git a/examples/apps/screenpipe-app-tauri/src-tauri/src/main.rs b/examples/apps/screenpipe-app-tauri/src-tauri/src/main.rs index 2cacfa6b..62c8c8b0 100755 --- a/examples/apps/screenpipe-app-tauri/src-tauri/src/main.rs +++ b/examples/apps/screenpipe-app-tauri/src-tauri/src/main.rs @@ -7,14 +7,13 @@ use tauri::Config; use serde_json::Value; use std::env; +use std::fs; use std::fs::File; use std::io::Write; -use tauri_plugin_shell::process::CommandEvent; - -use std::fs; use std::path::PathBuf; use std::sync::Arc; use std::sync::Mutex; +use std::time::Duration; use tauri::Manager; use tauri::State; use tauri::Wry; @@ -25,8 +24,10 @@ use tauri::{ use tauri_plugin_autostart::MacosLauncher; use tauri_plugin_autostart::ManagerExt; use tauri_plugin_shell::process::CommandChild; +use tauri_plugin_shell::process::CommandEvent; use tauri_plugin_shell::ShellExt; use tauri_plugin_store::{with_store, StoreCollection}; +use tokio::time::sleep; use uuid::Uuid; mod analytics; @@ -42,30 +43,54 @@ async fn kill_all_sreenpipes( ) -> Result<(), String> { debug!("Killing screenpipe"); - if let Some(child) = state.0.lock().unwrap().take() { - child.kill().map_err(|e| e.to_string())?; - } + const MAX_RETRIES: u32 = 3; + const RETRY_DELAY: Duration = Duration::from_secs(1); - // Hard kill the sidecar - #[cfg(not(target_os = "windows"))] - { - let _ = tokio::process::Command::new("pkill") - .arg("-f") - .arg("screenpipe") - .output() - .await - .map_err(|e| e.to_string())?; - } - #[cfg(target_os = "windows")] - { - let _ = tokio::process::Command::new("taskkill") - .args(&["/F", "/IM", "screenpipe.exe"]) - .output() - .await - .map_err(|e| e.to_string())?; + for attempt in 1..=MAX_RETRIES { + if let Some(child) = state.0.lock().unwrap().take() { + if let Err(e) = child.kill() { + error!("Failed to kill child process (attempt {}): {}", attempt, e); + } + } + + // Hard kill the sidecar + let kill_result = async { + #[cfg(not(target_os = "windows"))] + { + tokio::process::Command::new("pkill") + .arg("-f") + .arg("screenpipe") + .output() + .await + } + #[cfg(target_os = "windows")] + { + tokio::process::Command::new("taskkill") + .args(&["/F", "/IM", "screenpipe.exe"]) + .output() + .await + } + } + .await; + + match kill_result { + Ok(_) => { + debug!("Successfully killed screenpipe processes"); + return Ok(()); + } + Err(e) => { + error!( + "Failed to kill screenpipe processes (attempt {}): {}", + attempt, e + ); + if attempt < MAX_RETRIES { + sleep(RETRY_DELAY).await; + } + } + } } - Ok(()) + Err("Failed to kill screenpipe processes after multiple attempts".to_string()) } #[tauri::command] @@ -187,7 +212,10 @@ fn spawn_sidecar(app: &tauri::AppHandle) -> Result { let (mut rx, child) = result.unwrap(); + // only in production mode because it breaks the "bun tauri dev" + #[cfg(not(debug_assertions))] tauri::async_runtime::spawn(async move { + #[allow(unused_variables)] let mut i = 0; while let Some(event) = rx.recv().await { if let CommandEvent::Stdout(line) = event { @@ -443,6 +471,8 @@ async fn main() { debug!("Ready event"); } tauri::RunEvent::ExitRequested { .. } => { + let app_handle_clone = app_handle.clone(); + let app_handle_clone2 = app_handle.clone(); debug!("ExitRequested event"); // kill all screenpipe processes if the user is not using dev mode using pkill // get dev mode from the store @@ -462,20 +492,9 @@ async fn main() { .unwrap_or(false); if !use_dev_mode { tauri::async_runtime::spawn(async move { - #[cfg(not(target_os = "windows"))] - { - let _ = tokio::process::Command::new("pkill") - .arg("-f") - .arg("screenpipe") - .output() - .await; - } - #[cfg(target_os = "windows")] - { - let _ = tokio::process::Command::new("taskkill") - .args(&["/F", "/IM", "screenpipe.exe"]) - .output() - .await; + let state = app_handle_clone.state::(); + if let Err(e) = kill_all_sreenpipes(state, app_handle_clone2).await { + error!("Failed to kill screenpipe processes: {}", e); } }); }