use iced::futures::channel::oneshot; /// Run a blocking closure on a thread pool thread and await its result. /// Used to offload blocking work (HTTP, disk, process spawning) without /// stalling the iced event loop. pub async fn async_blocking(f: F) -> T where T: Send + 'static, F: FnOnce() -> T + Send + 'static, { let (tx, rx) = oneshot::channel(); std::thread::spawn(move || { let _ = tx.send(f()); }); rx.await.expect("blocking task panicked") } /// Open a native folder picker dialog and return the chosen path, or None if /// the user cancelled. Tries zenity (GNOME/GTK) then kdialog (KDE) in order. pub fn pick_folder(title: &str) -> Option { for (cmd, args) in [ ("zenity", vec!["--file-selection", "--directory", "--title", title]), ("kdialog", vec!["--getexistingdirectory", "/home", "--title", title]), ] { let Ok(out) = std::process::Command::new(cmd).args(&args).output() else { continue; }; if out.status.success() { let s = String::from_utf8_lossy(&out.stdout).trim().to_string(); if !s.is_empty() { return Some(s); } } } None }