diff --git a/package-lock.json b/package-lock.json index 58ab406..6a6da4d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,11 +1,11 @@ { - "name": "personal-translator", + "name": "my-translator", "version": "0.4.0", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "personal-translator", + "name": "my-translator", "version": "0.4.0", "dependencies": { "@tauri-apps/plugin-process": "^2.3.1", diff --git a/scripts/setup_mlx.py b/scripts/setup_mlx.py index 76dce9d..03b8ec4 100644 --- a/scripts/setup_mlx.py +++ b/scripts/setup_mlx.py @@ -56,9 +56,13 @@ def is_setup_complete(env_dir): def check_system_python(): """Find a suitable Python 3.10+ for creating venv.""" + home = os.path.expanduser("~") candidates = [ + os.path.join(home, ".pyenv", "shims", "python3"), "/opt/homebrew/bin/python3", "/usr/local/bin/python3", + os.path.join(home, "miniconda3", "bin", "python3"), + os.path.join(home, "anaconda3", "bin", "python3"), shutil.which("python3"), ] for path in candidates: diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 8f0e2e3..3a1147f 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2406,6 +2406,33 @@ dependencies = [ "windows-sys 0.60.2", ] +[[package]] +name = "my-translator" +version = "0.4.2" +dependencies = [ + "base64 0.22.1", + "chrono", + "cpal", + "dirs", + "futures-util", + "hex", + "http", + "rand 0.8.5", + "reqwest 0.12.28", + "screencapturekit", + "serde", + "serde_json", + "sha2", + "tauri", + "tauri-build", + "tauri-plugin-opener", + "tauri-plugin-process", + "tauri-plugin-updater", + "tokio-tungstenite", + "uuid", + "windows 0.58.0", +] + [[package]] name = "native-tls" version = "0.2.18" @@ -2863,32 +2890,6 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" -[[package]] -name = "personal-translator" -version = "0.4.2" -dependencies = [ - "base64 0.22.1", - "chrono", - "cpal", - "dirs", - "futures-util", - "hex", - "http", - "rand 0.8.5", - "reqwest 0.12.28", - "screencapturekit", - "serde", - "serde_json", - "sha2", - "tauri", - "tauri-build", - "tauri-plugin-opener", - "tauri-plugin-updater", - "tokio-tungstenite", - "uuid", - "windows 0.58.0", -] - [[package]] name = "phf" version = "0.8.0" @@ -4578,6 +4579,16 @@ dependencies = [ "zbus", ] +[[package]] +name = "tauri-plugin-process" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d55511a7bf6cd70c8767b02c97bf8134fa434daf3926cfc1be0a0f94132d165a" +dependencies = [ + "tauri", + "tauri-plugin", +] + [[package]] name = "tauri-plugin-updater" version = "2.10.0" diff --git a/src-tauri/src/commands/local_pipeline.rs b/src-tauri/src/commands/local_pipeline.rs index b63f2fd..4785590 100644 --- a/src-tauri/src/commands/local_pipeline.rs +++ b/src-tauri/src/commands/local_pipeline.rs @@ -8,6 +8,80 @@ pub struct LocalPipelineState { pub process: Mutex>, } +/// Build a PATH that includes common Python install locations and the user's shell PATH. +/// GUI apps on macOS don't inherit the user's shell PATH, so we must reconstruct it. +fn build_rich_path() -> String { + let home = std::env::var("HOME").unwrap_or_else(|_| String::from("/root")); + let mut dirs: Vec = vec![ + // Homebrew (Apple Silicon + Intel) + "/opt/homebrew/bin".into(), + "/usr/local/bin".into(), + // pyenv + format!("{}/.pyenv/shims", home), + format!("{}/.pyenv/bin", home), + // Typical conda locations + format!("{}/miniconda3/bin", home), + format!("{}/anaconda3/bin", home), + // System + "/usr/bin".into(), + "/bin".into(), + ]; + + // Also try to get the real user PATH from a login shell (best-effort) + if let Ok(output) = Command::new("/bin/zsh") + .args(["-l", "-c", "echo $PATH"]) + .output() + { + if output.status.success() { + let shell_path = String::from_utf8_lossy(&output.stdout).trim().to_string(); + for entry in shell_path.split(':') { + let s = entry.to_string(); + if !dirs.contains(&s) { + dirs.push(s); + } + } + } + } + + dirs.join(":") +} + +/// Find a working Python 3.10+ binary, checking common locations. +fn find_python3() -> String { + let home = std::env::var("HOME").unwrap_or_else(|_| String::from("/root")); + let candidates = [ + format!("{}/.pyenv/shims/python3", home), + "/opt/homebrew/bin/python3".into(), + "/usr/local/bin/python3".into(), + format!("{}/miniconda3/bin/python3", home), + format!("{}/anaconda3/bin/python3", home), + ]; + + for path in &candidates { + let p = std::path::Path::new(path); + if p.exists() { + // Verify it's actually 3.10+ + if let Ok(output) = Command::new(path).arg("--version").output() { + let ver = String::from_utf8_lossy(&output.stdout); + if let Some(version) = ver.strip_prefix("Python ") { + let parts: Vec<&str> = version.trim().split('.').collect(); + if let (Ok(major), Ok(minor)) = ( + parts.first().unwrap_or(&"0").parse::(), + parts.get(1).unwrap_or(&"0").parse::(), + ) { + if major >= 3 && minor >= 10 { + return path.clone(); + } + } + } + } + } + } + + // Fallback — hope it's on PATH + "python3".into() +} + fn log_to_file(msg: &str) { use std::fs::OpenOptions; let _ = OpenOptions::new() @@ -80,20 +154,19 @@ pub fn start_local_pipeline( let _ = channel.send(format!(r#"{{"type":"status","message":"Starting Python pipeline..."}}"#)); // Use venv python if MLX setup is complete, otherwise fall back to system python - let home = std::env::var("HOME").unwrap_or_else(|_| "/Users/phucnt".to_string()); + let home = std::env::var("HOME").unwrap_or_else(|_| String::from("/root")); let venv_python = format!("{}/Library/Application Support/My Translator/mlx-env/bin/python3", home); let python = if std::path::Path::new(&venv_python).exists() { log_to_file(&format!("Using venv python: {}", venv_python)); - venv_python.as_str().to_string() - } else if std::path::Path::new("/opt/homebrew/bin/python3").exists() { - log_to_file("Using homebrew python"); - "/opt/homebrew/bin/python3".to_string() + venv_python.clone() } else { - "python3".to_string() + let found = find_python3(); + log_to_file(&format!("Using system python: {}", found)); + found }; - let path_env = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"; + let path_env = build_rich_path(); let mut child = Command::new(&python) .arg(&script_path) @@ -222,7 +295,7 @@ fn stop_local_pipeline_inner(state: &LocalPipelineState) { /// Check if MLX setup is complete #[tauri::command] pub fn check_mlx_setup() -> Result { - let home = std::env::var("HOME").unwrap_or_else(|_| "/Users/phucnt".to_string()); + let home = std::env::var("HOME").unwrap_or_else(|_| String::from("/root")); let marker = format!("{}/Library/Application Support/My Translator/mlx-env/.setup_complete", home); let venv_python = format!("{}/Library/Application Support/My Translator/mlx-env/bin/python3", home); @@ -261,14 +334,14 @@ pub fn run_mlx_setup( }; // Use system python to run setup (which creates the venv) - let python = if std::path::Path::new("/opt/homebrew/bin/python3").exists() { - "/opt/homebrew/bin/python3" - } else { - "python3" - }; + let python = find_python3(); + let home = std::env::var("HOME").unwrap_or_else(|_| String::from("/root")); + let path_env = build_rich_path(); - let mut child = Command::new(python) + let mut child = Command::new(&python) .arg(&script_path) + .env("PATH", &path_env) + .env("HOME", &home) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn()