vios_app 0.1.1

Small JSON vaults: Argon2id + AES-GCM, with optional AAD binding.
Documentation
// 🧠 Sovereign Launcher v1.3 – Compile-Clean + Functional

// When `watch` is OFF: tiny no-op so tests/clippy don’t try to build egui + notify.
#[cfg(not(feature = "watch"))]
fn main() {}

#[cfg(feature = "watch")]
mod app {
    use eframe::egui::{
        self, CentralPanel, Context, FontDefinitions, Grid, ScrollArea, TextEdit, TopBottomPanel,
        Visuals,
    };
    use notify::{RecommendedWatcher, RecursiveMode, Watcher};
    use rfd::FileDialog;
    use std::{
        collections::HashMap,
        fs,
        process::{Command, Stdio},
        sync::{Arc, Mutex},
        time::Instant,
    };

    pub struct MyApp {
        pub vault_preview: Arc<Mutex<String>>,
        pub metadata: Arc<Mutex<HashMap<String, String>>>,
        pub dark_mode: bool,
        pub logs: Arc<Mutex<String>>,
        pub last_opened_file: Option<String>,
        pub launch_time: Instant,
        pub auto_reload: bool,
        pub file_watcher: Option<RecommendedWatcher>,
    }

    impl Default for MyApp {
        fn default() -> Self {
            Self {
                vault_preview: Arc::new(Mutex::new(String::new())),
                metadata: Arc::new(Mutex::new(HashMap::new())),
                dark_mode: true,
                logs: Arc::new(Mutex::new(String::new())),
                last_opened_file: None,
                launch_time: Instant::now(),
                auto_reload: false,
                file_watcher: None,
            }
        }
    }

    impl eframe::App for MyApp {
        fn update(&mut self, ctx: &Context, _frame: &mut eframe::Frame) {
            ctx.set_visuals(if self.dark_mode {
                Visuals::dark()
            } else {
                Visuals::light()
            });

            ctx.set_fonts(Self::load_custom_fonts());

            TopBottomPanel::bottom("status_bar").show(ctx, |ui| {
                ui.horizontal(|ui| {
                    let uptime = self.launch_time.elapsed().as_secs();
                    ui.label(format!("🧠 VIOS | Vault Protocol v2.0 | ⏱️ {uptime}s"));
                    if let Some(path) = &self.last_opened_file {
                        ui.label(format!("πŸ“‚ {path}"));
                    }
                });
            });

            CentralPanel::default().show(ctx, |ui| {
                ui.heading("πŸ” VIOS Sovereign Launcher");

                ui.horizontal(|ui| {
                    ui.label("πŸŒ“ Theme:");
                    if ui
                        .button(if self.dark_mode {
                            "β˜€οΈ Light"
                        } else {
                            "πŸŒ‘ Dark"
                        })
                        .clicked()
                    {
                        self.dark_mode = !self.dark_mode;
                    }

                    ui.checkbox(&mut self.auto_reload, "πŸ” Auto-Reload");
                });

                ui.separator();

                if ui.button("πŸ” Load .vault File").clicked() {
                    if let Some(path) = FileDialog::new()
                        .add_filter("Vault files", &["vault"])
                        .pick_file()
                    {
                        let display_path = path.display().to_string();
                        self.last_opened_file = Some(display_path.clone());

                        match fs::read_to_string(&path) {
                            Ok(content) => {
                                {
                                    let mut vp = self.vault_preview.lock().unwrap();
                                    *vp = content.clone();
                                }
                                self.extract_metadata(&content);

                                if self.auto_reload {
                                    self.watch_file(display_path.clone());
                                }
                            }
                            Err(e) => {
                                let mut vp = self.vault_preview.lock().unwrap();
                                *vp = format!("❌ Failed to read file: {e}");
                                self.metadata.lock().unwrap().clear();
                            }
                        }
                    }
                }

                ui.separator();
                ui.horizontal(|ui| {
                    ui.vertical(|ui| {
                        ui.label("πŸ” Encrypted Vault Content:");
                        let mut vp = self.vault_preview.lock().unwrap();
                        ui.add_sized([360.0, 250.0], TextEdit::multiline(&mut *vp));
                    });

                    ui.vertical(|ui| {
                        ui.label("🧬 Parsed Echo Metadata:");
                        ScrollArea::vertical().show(ui, |ui| {
                            let meta = self.metadata.lock().unwrap();
                            Grid::new("metadata_grid").striped(true).show(ui, |ui| {
                                for (key, value) in meta.iter() {
                                    ui.label(format!("πŸ”Ή {key}"));
                                    ui.label(value);
                                    ui.end_row();
                                }
                            });
                        });
                    });
                });

                ui.separator();
                ui.heading("πŸ“œ Runtime Logs:");
                ScrollArea::vertical().show(ui, |ui| {
                    let logs = self.logs.lock().unwrap();
                    ui.add_sized([730.0, 80.0], TextEdit::multiline(&mut logs.clone()));
                });

                ui.separator();
                ui.heading("πŸ§ͺ Run Tools:");
                ui.horizontal(|ui| {
                    self.tool_button(ui, "πŸ” Replay Naorix", "naorix_reply", "Run replay agent");
                    self.tool_button(ui, "🎀 Whisper Input", "whisper_input", "Capture input");
                });
            });
        }
    }

    impl MyApp {
        fn extract_metadata(&self, contents: &str) {
            let mut meta = self.metadata.lock().unwrap();
            meta.clear();
            for line in contents.lines() {
                if let Some((key, value)) = line.split_once(':') {
                    meta.insert(key.trim().to_string(), value.trim().to_string());
                }
            }
        }

        // Fallback to default system fonts – prevents crash on missing or invalid font files
        fn load_custom_fonts() -> FontDefinitions {
            FontDefinitions::default()
        }

        fn watch_file(&mut self, path: String) {
            let preview = Arc::clone(&self.vault_preview);
            let metadata = Arc::clone(&self.metadata);
            let path_for_closure = path.clone();

            let watcher_result =
                notify::recommended_watcher(move |res: notify::Result<notify::Event>| {
                    if res.is_ok() {
                        if let Ok(content) = fs::read_to_string(&path_for_closure) {
                            if let Ok(mut p) = preview.lock() {
                                *p = content.clone();
                            }
                            if let Ok(mut m) = metadata.lock() {
                                m.clear();
                                for line in content.lines() {
                                    if let Some((k, v)) = line.split_once(':') {
                                        m.insert(k.trim().to_string(), v.trim().to_string());
                                    }
                                }
                            }
                        }
                    }
                });

            if let Ok(mut watcher) = watcher_result {
                let _ = watcher.watch(path.as_ref(), RecursiveMode::NonRecursive);
                self.file_watcher = Some(watcher);
            }
        }

        fn tool_button(&self, ui: &mut egui::Ui, label: &str, bin: &'static str, tooltip: &str) {
            if ui.button(label).on_hover_text(tooltip).clicked() {
                let bin_name = bin.to_string();
                std::thread::spawn(move || {
                    let output = Command::new("cargo")
                        .args(["run", "--bin", &bin_name])
                        .stdout(Stdio::piped())
                        .stderr(Stdio::piped())
                        .spawn()
                        .expect("Failed to launch binary")
                        .wait_with_output()
                        .expect("Failed to wait for output");

                    if !output.status.success() {
                        eprintln!(
                            "Error running {}: {}",
                            bin_name,
                            String::from_utf8_lossy(&output.stderr)
                        );
                    } else {
                        println!(
                            "Output of {}:\n{}",
                            bin_name,
                            String::from_utf8_lossy(&output.stdout)
                        );
                    }
                });
            }
        }
    }

    pub fn run() -> eframe::Result<()> {
        eframe::run_native(
            "πŸ” VIOS Sovereign Launcher",
            eframe::NativeOptions::default(),
            Box::new(|_cc| Box::new(MyApp::default())),
        )
    }
}

#[cfg(feature = "watch")]
fn main() -> eframe::Result<()> {
    app::run()
}