maverick_os 2.0.0

Maverick OS
use std::process::Command;
use std::path::Path;
use std::fs;
use std::thread;

#[derive(Clone)]
pub struct OsPhotoPicker;

impl OsPhotoPicker {
    pub fn open(callback: impl FnOnce(Vec<u8>) + Send + 'static) {
        thread::spawn(move || {
            let result = Self::try_zenity()
                .or_else(|| Self::try_kdialog());

            match result {
                Some(file_path) => {
                    if let Ok(image_data) = fs::read(&file_path) {
                        callback(image_data);
                    } else {
                        callback(Vec::new());
                    }
                }
                None => callback(Vec::new()),
            }
        });
    }

    fn try_zenity() -> Option<String> {
        let output = Command::new("zenity")
            .args(&[
                "--file-selection",
                "--file-filter=Image files | *.jpg *.jpeg *.png",
                "--file-filter=All files | *",
                "--title=Select an Image",
            ])
            .output().ok()?;

        if output.status.success() {
            let file_path = String::from_utf8_lossy(&output.stdout).trim().to_string();
            if !file_path.is_empty() && Path::new(&file_path).exists() {
                return Some(file_path);
            }
        }
        None
    }

    fn try_kdialog() -> Option<String> {
        let output = Command::new("kdialog")
            .args(&[
                "--getopenfilename",
                ".",
                "*.jpg *.jpeg *.png|Image files",
                "--title",
                "Select an Image",
            ])
            .output().ok()?;

        if output.status.success() {
            let file_path = String::from_utf8_lossy(&output.stdout).trim().to_string();
            if !file_path.is_empty() && Path::new(&file_path).exists() {
                return Some(file_path);
            }
        }
        None
    }
}