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);
}
});
}