//! ESEngine Editor - Tauri Backend //! //! Clean entry point that handles application setup and command registration. #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] mod commands; mod profiler_ws; mod state; use std::collections::HashMap; use std::sync::{Arc, Mutex}; use tauri::Manager; use state::{ProfilerState, ProjectPaths, ScriptWatcherState}; fn main() { // Initialize shared state | 初始化共享状态 let project_paths: ProjectPaths = Arc::new(Mutex::new(HashMap::new())); let project_paths_for_protocol = Arc::clone(&project_paths); let profiler_state = ProfilerState::new(); let script_watcher_state = ScriptWatcherState::new(); // Build and run the Tauri application tauri::Builder::default() // Register plugins .plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_updater::Builder::new().build()) .plugin(tauri_plugin_http::init()) .plugin(tauri_plugin_cli::init()) // Register custom URI scheme for project files .register_uri_scheme_protocol("project", move |_app, request| { handle_project_protocol(request, &project_paths_for_protocol) }) // Setup application state | 设置应用状态 .setup(move |app| { app.manage(project_paths); app.manage(profiler_state); app.manage(script_watcher_state); Ok(()) }) // Register all commands .invoke_handler(tauri::generate_handler![ // Project management commands::open_project, commands::save_project, commands::export_binary, commands::set_project_base_path, commands::scan_behavior_trees, // File system operations commands::read_file_content, commands::write_file_content, commands::write_binary_file, commands::path_exists, commands::create_directory, commands::create_file, commands::delete_file, commands::delete_folder, commands::rename_file_or_folder, commands::list_directory, commands::scan_directory, commands::read_file_as_base64, commands::copy_file, // Dialog operations commands::open_folder_dialog, commands::open_file_dialog, commands::save_file_dialog, // Profiler server commands::start_profiler_server, commands::stop_profiler_server, commands::get_profiler_status, // Plugin management commands::build_plugin, commands::install_marketplace_plugin, commands::uninstall_marketplace_plugin, // System operations commands::toggle_devtools, commands::open_file_with_default_app, commands::show_in_folder, commands::get_temp_dir, commands::open_with_editor, commands::copy_type_definitions, commands::get_app_resource_dir, commands::get_current_dir, commands::start_local_server, commands::stop_local_server, commands::get_local_ip, commands::generate_qrcode, // User code compilation | 用户代码编译 commands::compile_typescript, commands::watch_scripts, commands::stop_watch_scripts, // Build commands | 构建命令 commands::prepare_build_directory, commands::copy_directory, commands::bundle_scripts, commands::generate_html, commands::get_file_size, commands::get_directory_size, commands::write_json_file, commands::list_files_by_extension, commands::read_binary_file_as_base64, // Engine modules | 引擎模块 commands::read_engine_modules_index, commands::read_module_manifest, commands::get_engine_modules_base_path, ]) .run(tauri::generate_context!()) .expect("error while running tauri application"); } /// Handle the custom 'project://' URI scheme protocol /// /// This allows the frontend to load project files through a custom protocol, /// enabling features like hot-reloading plugins from the project directory. fn handle_project_protocol( request: tauri::http::Request>, project_paths: &ProjectPaths, ) -> tauri::http::Response> { let uri = request.uri(); let path = uri.path(); // Debug logging println!("[project://] Full URI: {}", uri); println!("[project://] Path: {}", path); let file_path = { let paths = match project_paths.lock() { Ok(p) => p, Err(_) => { return tauri::http::Response::builder() .status(500) .body(Vec::new()) .unwrap(); } }; match paths.get("current") { Some(base_path) => format!("{}{}", base_path, path), None => { return tauri::http::Response::builder() .status(404) .body(Vec::new()) .unwrap(); } } }; match std::fs::read(&file_path) { Ok(content) => { let mime_type = get_mime_type(&file_path); tauri::http::Response::builder() .status(200) .header("Content-Type", mime_type) .header("Access-Control-Allow-Origin", "*") .body(content) .unwrap() } Err(e) => { eprintln!("Failed to read file {}: {}", file_path, e); tauri::http::Response::builder() .status(404) .body(Vec::new()) .unwrap() } } } /// Get MIME type based on file extension fn get_mime_type(file_path: &str) -> &'static str { if file_path.ends_with(".ts") || file_path.ends_with(".tsx") { "application/javascript" } else if file_path.ends_with(".js") { "application/javascript" } else if file_path.ends_with(".json") { "application/json" } else if file_path.ends_with(".css") { "text/css" } else if file_path.ends_with(".html") { "text/html" } else { "text/plain" } }