#[cfg(desktop)]
mod db;
mod ipfs;
mod metrics;
mod mining;
#[cfg(desktop)]
mod server;
#[cfg(desktop)]
mod utils;

use std::sync::Arc;

#[cfg(desktop)]
use db::DbState;
use ipfs::{get_ipfs_mode, init_ipfs, is_ipfs_initialized, is_ipfs_running, start_ipfs, stop_ipfs};
use mining::MiningState;
#[cfg(desktop)]
use server::start_server;
use tauri::generate_handler;
use tauri::Manager;
use tauri::RunEvent;
#[cfg(desktop)]
use tauri::WebviewWindow;

#[cfg(desktop)]
use utils::update_splash_message;

#[cfg(desktop)]
async fn init_ipfs_with_progress(splash: &WebviewWindow) {
    // Step 1: Initialize IPFS repo if needed
    update_splash_message(splash, "Initializing IPFS...");
    println!("[CYB.AI] Step 1: Checking if IPFS is initialized...");

    let is_init = is_ipfs_initialized().unwrap_or(false);
    if !is_init {
        println!("[CYB.AI] Initializing IPFS repo...");
        if let Err(e) = init_ipfs() {
            eprintln!("[CYB.AI] Failed to init IPFS: {}", e);
            update_splash_message(splash, &format!("IPFS init failed: {}", e));
            tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
            return;
        }
    }

    // Step 2: Start the daemon (handles CORS config internally)
    update_splash_message(splash, "Starting IPFS daemon...");
    println!("[CYB.AI] Step 2: Starting IPFS daemon...");

    match start_ipfs().await {
        Ok(_) => println!("[CYB.AI] IPFS daemon started"),
        Err(e) => {
            eprintln!("[CYB.AI] Failed to start IPFS: {:?}", e);
            update_splash_message(splash, &format!("IPFS start failed: {:?}", e));
            tokio::time::sleep(tokio::time::Duration::from_secs(2)).await;
            return;
        }
    }

    // Step 3: Wait for daemon API to be ready
    update_splash_message(splash, "Waiting for IPFS daemon...");
    println!("[CYB.AI] Step 3: Waiting for daemon API...");

    let client = reqwest::Client::builder()
        .timeout(std::time::Duration::from_secs(3))
        .build()
        .unwrap();

    let max_attempts = 15;
    for i in 0..max_attempts {
        match client.post("http://127.0.0.1:5001/api/v0/id").send().await {
            Ok(resp) if resp.status().is_success() => {
                println!("[CYB.AI] IPFS API is ready!");
                break;
            }
            _ => {
                if i == max_attempts - 1 {
                    eprintln!("[CYB.AI] IPFS API did not become ready in time");
                    update_splash_message(splash, "IPFS daemon slow to start, continuing...");
                } else {
                    println!(
                        "[CYB.AI] Waiting for IPFS API... ({}/{})",
                        i + 1,
                        max_attempts
                    );
                    tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
                }
            }
        }
    }

    update_splash_message(splash, "IPFS started successfully!");
    println!("[CYB.AI] IPFS initialization complete!");
}

/// Read and delete bootstrap.json written by cyb-boot installer.
/// Returns {mnemonic, referrer} if found, null otherwise.
#[tauri::command]
fn read_bootstrap() -> Option<serde_json::Value> {
    let data_dir = dirs::data_dir()?.join("ai.cyb.app");
    let path = data_dir.join("bootstrap.json");
    let content = std::fs::read_to_string(&path).ok()?;
    let value: serde_json::Value = serde_json::from_str(&content).ok()?;
    // Delete after reading โ€” one-time import
    let _ = std::fs::remove_file(&path);
    println!("[CYB.AI] Imported bootstrap.json from cyb-boot installer");
    Some(value)
}

#[tauri::command]
fn toggle_devtools(window: tauri::WebviewWindow) {
    if window.is_devtools_open() {
        window.close_devtools();
    } else {
        window.open_devtools();
    }
}

#[tauri::command]
fn reveal_in_finder(path: String) -> Result<(), String> {
    #[cfg(target_os = "macos")]
    {
        std::process::Command::new("open")
            .args(["-R", &path])
            .spawn()
            .map_err(|e| e.to_string())?;
    }
    #[cfg(target_os = "linux")]
    {
        std::process::Command::new("xdg-open")
            .arg(std::path::Path::new(&path).parent().unwrap_or(std::path::Path::new(&path)))
            .spawn()
            .map_err(|e| e.to_string())?;
    }
    #[cfg(target_os = "windows")]
    {
        std::process::Command::new("explorer")
            .args(["/select,", &path])
            .spawn()
            .map_err(|e| e.to_string())?;
    }
    Ok(())
}

#[cfg(desktop)]
fn build_tauri_app() -> tauri::Builder<tauri::Wry> {
    tauri::Builder::default()
        .plugin(tauri_plugin_shell::init())
        .plugin(tauri_plugin_deep_link::init())
        .plugin(tauri_plugin_dialog::init())
        .plugin(tauri_plugin_fs::init())
        .manage(Arc::new(MiningState::new()))
        .invoke_handler(generate_handler![
            toggle_devtools,
            read_bootstrap,
            reveal_in_finder,
            get_ipfs_mode,
            start_ipfs,
            stop_ipfs,
            is_ipfs_running,
            is_ipfs_initialized,
            init_ipfs,
            mining::start_mining,
            mining::stop_mining,
            mining::update_challenge,
            mining::get_mining_status,
            mining::take_proofs,
            mining::mining_benchmark,
            mining::get_mining_params,
            mining::report_proof_submitted,
            mining::report_proof_failed,
        ])
}

#[cfg(not(desktop))]
fn build_tauri_app() -> tauri::Builder<tauri::Wry> {
    tauri::Builder::default()
        .plugin(tauri_plugin_shell::init())
        .plugin(tauri_plugin_deep_link::init())
        .plugin(tauri_plugin_dialog::init())
        .plugin(tauri_plugin_fs::init())
        .manage(Arc::new(MiningState::new()))
        .invoke_handler(generate_handler![
            toggle_devtools,
            read_bootstrap,
            reveal_in_finder,
            get_ipfs_mode,
            start_ipfs,
            stop_ipfs,
            is_ipfs_running,
            is_ipfs_initialized,
            init_ipfs,
            mining::start_mining,
            mining::stop_mining,
            mining::update_challenge,
            mining::get_mining_status,
            mining::take_proofs,
            mining::mining_benchmark,
            mining::get_mining_params,
            mining::report_proof_submitted,
            mining::report_proof_failed,
        ])
}

#[cfg_attr(mobile, tauri::mobile_entry_point)]
pub fn run() {
    #[cfg(desktop)]
    {
        let app_state = Arc::new(DbState::new());

        tokio::runtime::Builder::new_multi_thread()
            .enable_all()
            .build()
            .unwrap()
            .block_on(async {
                let server_state = app_state.clone();
                tokio::spawn(async move {
                    println!("[CYB.AI] Starting server...");
                    start_server(server_state).await;
                    println!("[CYB.AI] Server is started!");
                });

                let app = build_tauri_app()
                    .setup(|app| {
                        println!("[CYB.AI] Starting setup...");

                        let splashscreen_window =
                            app.get_webview_window("splashscreen").unwrap();
                        splashscreen_window.show().unwrap();

                        let main_window = app.get_webview_window("main").unwrap();

                        tauri::async_runtime::spawn(async move {
                            init_ipfs_with_progress(&splashscreen_window).await;

                            tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;

                            println!("[CYB.AI] Showing main window...");
                            main_window.show().unwrap();
                            if cfg!(debug_assertions) {
                                main_window.open_devtools();
                            }
                            let _ = splashscreen_window.close();
                            println!("[CYB.AI] App ready!");
                        });

                        Ok(())
                    })
                    .build(tauri::generate_context!())
                    .expect("error while building tauri application");

                app.run(|_app_handle, event| {
                    if let RunEvent::Exit = event {
                        println!("[CYB.AI] App exiting โ€” cleaning up...");

                        // Stop IPFS daemon
                        if let Err(e) = stop_ipfs() {
                            eprintln!("[CYB.AI] Failed to stop IPFS: {}", e);
                        }
                        // Kill IPFS child process directly if shutdown command didn't work
                        if let Ok(mut guard) = ipfs::IPFS_CHILD.lock() {
                            if let Some(ref mut child) = *guard {
                                let _ = child.kill();
                                let _ = child.wait();
                                println!("[CYB.AI] IPFS child process killed");
                            }
                            *guard = None;
                        }

                        // Stop mining (releases GPU/CPU resources)
                        let state = _app_handle.state::<Arc<MiningState>>();
                        state.mining.store(false, std::sync::atomic::Ordering::SeqCst);
                        println!("[CYB.AI] Mining stopped");
                    }
                });
            });
    }

    #[cfg(not(desktop))]
    {
        use tauri::Emitter;

        println!("[CYB.AI] Mobile platform startup");
        build_tauri_app()
            .setup(|_app| {
                println!("[CYB.AI] App ready!");
                Ok(())
            })
            .build(tauri::generate_context!())
            .expect("error while building tauri application")
            .run(|app_handle, event| {
                if let RunEvent::Resumed { .. } = event {
                    println!("[CYB.AI] App resumed (foregrounded)");
                    let _ = app_handle.emit("app-resumed", ());
                }
            });
    }
}

Dimensions

bbg/src/lib.rs
zheng/src/lib.rs
lens/src/lib.rs
kuro/rs/lib.rs
nox/rs/lib.rs
trident/src/lib.rs
nebu/rs/lib.rs
optica/src/lib.rs
kuro/wgsl/src/lib.rs
jali/wgsl/src/lib.rs
rs/macros/src/lib.rs
trop/rs/src/lib.rs
trop/wgsl/src/lib.rs
genies/rs/src/lib.rs
genies/wgsl/src/lib.rs
nebu/wgsl/src/lib.rs
hemera/rs/src/lib.rs
hemera/wgsl/src/lib.rs
jali/rs/src/lib.rs
rs/core/src/lib.rs
rs/mir-format/src/lib.rs
cw-cyber/contracts/cw-cyber-gift/src/lib.rs
trident/editor/zed/src/lib.rs
cw-cyber/contracts/hub-channels/src/lib.rs
cw-cyber/packages/hub-base/src/lib.rs
cw-cyber/contracts/std-test/src/lib.rs
cw-cyber/contracts/hub-skills/src/lib.rs
cw-cyber/contracts/hub-tokens/src/lib.rs
cw-cyber/contracts/cw-cyber-passport/src/lib.rs
cw-cyber/contracts/litium-core/src/lib.rs
cw-cyber/contracts/litium-mine/src/lib.rs
cw-cyber/contracts/litium-stake/src/lib.rs
cw-cyber/packages/cyber-std/src/lib.rs
cw-cyber/contracts/graph-filter/src/lib.rs
cw-cyber/contracts/cw-cyber-subgraph/src/lib.rs
rs/tests/macro-integration/src/lib.rs
cw-cyber/contracts/hub-networks/src/lib.rs
cw-cyber/packages/cyber-std-test/src/lib.rs
cw-cyber/contracts/litium-wrap/src/lib.rs
cw-cyber/contracts/hub-libs/src/lib.rs
cw-cyber/contracts/hub-protocols/src/lib.rs
cw-cyber/contracts/cybernet/src/lib.rs
cw-cyber/contracts/litium-refer/src/lib.rs

Local Graph