diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index f1baf20..d0e21e9 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -7,6 +7,8 @@ name = "CodeForge" version = "25.0.0" dependencies = [ "chrono", + "fern", + "log", "serde", "serde_json", "tauri", @@ -952,6 +954,15 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "fern" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4316185f709b23713e41e3195f90edef7fb00c3ed4adc79769cf09cc762a3b29" +dependencies = [ + "log", +] + [[package]] name = "field-offset" version = "0.3.6" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index bfc38dc..ac60792 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -23,3 +23,5 @@ tokio = "1.47.1" uuid = { version = "1.17.0", features = ["v4"] } tempfile = "3.0" chrono = { version = "0.4", features = ["serde"] } +log = "0.4" +fern = "0.7.1" diff --git a/src-tauri/build.rs b/src-tauri/build.rs index 920cd1c..db3af91 100644 --- a/src-tauri/build.rs +++ b/src-tauri/build.rs @@ -1,8 +1,8 @@ fn main() { // 设置构建时间 - let build_time = chrono::Utc::now() - .format("%Y-%m-%d %H:%M:%S UTC") - .to_string(); + let utc_time = chrono::Utc::now(); + let beijing_time = utc_time + chrono::Duration::hours(8); + let build_time = beijing_time.format("%Y年%m月%d日 %H:%M:%S").to_string(); println!("cargo:rustc-env=BUILD_TIME={}", build_time); // 重新构建触发条件 diff --git a/src-tauri/icons/128x128.png b/src-tauri/icons/128x128.png index df0a529..6cd16da 100644 Binary files a/src-tauri/icons/128x128.png and b/src-tauri/icons/128x128.png differ diff --git a/src-tauri/icons/32x32.png b/src-tauri/icons/32x32.png index c2eedec..30b770b 100644 Binary files a/src-tauri/icons/32x32.png and b/src-tauri/icons/32x32.png differ diff --git a/src-tauri/icons/icon.icns b/src-tauri/icons/icon.icns index 3740d58..73625e8 100644 Binary files a/src-tauri/icons/icon.icns and b/src-tauri/icons/icon.icns differ diff --git a/src-tauri/icons/icon.ico b/src-tauri/icons/icon.ico index d113d49..c8fa875 100644 Binary files a/src-tauri/icons/icon.ico and b/src-tauri/icons/icon.ico differ diff --git a/src-tauri/icons/icon.png b/src-tauri/icons/icon.png index 92d275c..fec6d4b 100644 Binary files a/src-tauri/icons/icon.png and b/src-tauri/icons/icon.png differ diff --git a/src-tauri/src/logger.rs b/src-tauri/src/logger.rs new file mode 100644 index 0000000..cdbac23 --- /dev/null +++ b/src-tauri/src/logger.rs @@ -0,0 +1,44 @@ +use chrono::Local; +use log::LevelFilter; +use std::fs; +use tauri::{AppHandle, Manager}; + +pub fn setup_logger(app: &AppHandle) -> Result<(), fern::InitError> { + // 获取应用数据目录 + let app_data_dir = app + .path() + .app_data_dir() + .expect("Failed to get app data dir"); + + // 创建日志目录 + let log_dir = app_data_dir.join("logs"); + fs::create_dir_all(&log_dir).expect("Failed to create log directory"); + + // 生成当天的日志文件名 + let today = Local::now().format("%Y-%m-%d").to_string(); + let log_file = log_dir.join(format!("codeforge-{}.log", today)); + + // 配置日志 + fern::Dispatch::new() + .format(|out, message, record| { + out.finish(format_args!( + "[{}] [{}] [{}:{}] {}", + Local::now().format("%Y-%m-%d %H:%M:%S%.3f"), + record.level(), + record.file().unwrap_or("unknown"), + record.line().unwrap_or(0), + message + )) + }) + .level(LevelFilter::Debug) // 设置日志级别 + .level_for("hyper", LevelFilter::Warn) // 减少第三方库的日志 + .level_for("reqwest", LevelFilter::Warn) + .chain(std::io::stdout()) // 同时输出到控制台 + .chain(fern::log_file(&log_file)?) // 输出到文件 + .apply()?; + + log::info!("日志系统初始化完成"); + log::info!("日志文件: {:?}", log_file); + + Ok(()) +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 23a7b53..0278e46 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -3,11 +3,12 @@ windows_subsystem = "windows" )] +mod logger; mod plugins; mod setup; use crate::setup::app::get_app_info; -use chrono::Utc; +use log::{debug, info}; use plugins::{CodeExecutionRequest, ExecutionResult, LanguageInfo, PluginManager}; use std::fs; use std::process::{Command, Stdio}; @@ -55,8 +56,10 @@ async fn execute_code( // 尝试不同的命令 for cmd in plugin.get_commands() { let args = plugin.get_execute_args(file_path.to_str().unwrap()); - - println!("Trying command: {} with args: {:?}", cmd, args); + debug!( + "执行插件代码 -> 执行命令: {} 携带参数: {:?} 语言: {}", + cmd, args, request.language + ); let output = Command::new(cmd) .args(&args) @@ -148,8 +151,7 @@ async fn get_info( // 尝试不同的命令 for cmd in plugin.get_commands() { - println!("Trying command: {} for language: {}", cmd, language); - + debug!("获取插件信息 -> 执行命令: {} 语言: {}", cmd, language); let version_output = Command::new(cmd).args(plugin.get_version_args()).output(); if let Ok(version_out) = version_output { @@ -216,14 +218,18 @@ async fn clear_execution_history(history: State<'_, ExecutionHistory>) -> Result } fn main() { - let build_time = Utc::now().format("%Y-%m-%d %H:%M:%S UTC").to_string(); - println!("cargo:rustc-env=BUILD_TIME={}", build_time); - tauri::Builder::default() .plugin(tauri_plugin_shell::init()) .manage(ExecutionHistory::default()) .manage(PluginManagerState::new(PluginManager::new())) .setup(|app| { + // 初始化日志系统 + if let Err(e) = logger::setup_logger(app.handle()) { + eprintln!("Failed to setup logger: {}", e); + } + info!("CodeForge 应用启动"); + info!("应用版本: {}", env!("CARGO_PKG_VERSION")); + let menu = setup::menu::create_menu(app.handle())?; app.set_menu(menu)?; setup::menu::setup_menu_handler(app.handle()); diff --git a/src-tauri/src/setup/menu.rs b/src-tauri/src/setup/menu.rs index 3b703ac..85ad74a 100644 --- a/src-tauri/src/setup/menu.rs +++ b/src-tauri/src/setup/menu.rs @@ -1,26 +1,27 @@ +use super::menus; + use tauri::{ - AppHandle, Emitter, - menu::{Menu, MenuBuilder, MenuItemBuilder, SubmenuBuilder}, + AppHandle, + menu::{Menu, MenuBuilder}, }; pub fn create_menu(app: &AppHandle) -> tauri::Result> { - let about_item = MenuItemBuilder::new("关于 CodeForge") - .id("about") - .build(app)?; + // 应用菜单 + let app_submenu = menus::app::create_app_submenu(app)?; - let app_submenu = SubmenuBuilder::new(app, "CodeForge") - .item(&about_item) - .build()?; + // 编辑菜单 + let edit_submenu = menus::edit::create_edit_submenu(app)?; - let menu = MenuBuilder::new(app).items(&[&app_submenu]).build()?; + let menu = MenuBuilder::new(app) + .items(&[&app_submenu, &edit_submenu]) + .build()?; Ok(menu) } pub fn setup_menu_handler(app: &AppHandle) { app.on_menu_event(move |app, event| { - if event.id().as_ref() == "about" { - let _event = app.emit("show-about", ()); - } + menus::app::handle_app_menu_event(app, event.id().as_ref()); + menus::edit::handle_edit_menu_event(app, event.id().as_ref()); }); } diff --git a/src-tauri/src/setup/menus/app.rs b/src-tauri/src/setup/menus/app.rs new file mode 100644 index 0000000..e1524c0 --- /dev/null +++ b/src-tauri/src/setup/menus/app.rs @@ -0,0 +1,35 @@ +use tauri::{ + AppHandle, Emitter, + menu::{MenuItemBuilder, Submenu, SubmenuBuilder}, +}; + +pub fn create_app_submenu(app: &AppHandle) -> tauri::Result> { + let about_item = MenuItemBuilder::new("关于 CodeForge") + .id("about") + .build(app)?; + + let quit_item = MenuItemBuilder::new("退出 CodeForge") + .id("quit") + .accelerator("CmdOrCtrl+Q") + .build(app)?; + + let app_submenu = SubmenuBuilder::new(app, "CodeForge") + .item(&about_item) + .separator() + .item(&quit_item) + .build()?; + + Ok(app_submenu) +} + +pub fn handle_app_menu_event(app: &AppHandle, event_id: &str) { + match event_id { + "about" => { + let _event = app.emit("show-about", ()); + } + "quit" => { + app.exit(0); + } + _ => {} + } +} diff --git a/src-tauri/src/setup/menus/edit.rs b/src-tauri/src/setup/menus/edit.rs new file mode 100644 index 0000000..0635ee8 --- /dev/null +++ b/src-tauri/src/setup/menus/edit.rs @@ -0,0 +1,70 @@ +use tauri::menu::PredefinedMenuItem; + +use tauri::{ + AppHandle, Manager, + menu::{MenuItemBuilder, Submenu, SubmenuBuilder}, +}; + +pub fn create_edit_submenu(app: &AppHandle) -> tauri::Result> { + let undo_item = MenuItemBuilder::new("撤销") + .id("undo") + .accelerator("CmdOrCtrl+Z") + .build(app)?; + + let redo_item = MenuItemBuilder::new("重做") + .id("redo") + .accelerator("CmdOrCtrl+Shift+Z") + .build(app)?; + + let cut_item = MenuItemBuilder::new("剪切") + .id("cut") + .accelerator("CmdOrCtrl+X") + .build(app)?; + + let copy_item = MenuItemBuilder::new("复制") + .id("copy") + .accelerator("CmdOrCtrl+C") + .build(app)?; + + let select_all_item = MenuItemBuilder::new("全选") + .id("select_all") + .accelerator("CmdOrCtrl+A") + .build(app)?; + + let edit_submenu = SubmenuBuilder::new(app, "编辑") + .item(&undo_item) + .item(&redo_item) + .separator() + .item(&cut_item) + .item(©_item) + .item(&PredefinedMenuItem::paste(app, Option::from("粘贴"))?) + .separator() + .item(&select_all_item) + .build()?; + + Ok(edit_submenu) +} + +pub fn handle_edit_menu_event(app: &AppHandle, event_id: &str) { + let binding = app.webview_windows(); + let webview = binding.values().next().unwrap(); + + match event_id { + "undo" => { + webview.eval("document.execCommand('undo')").ok(); + } + "redo" => { + webview.eval("document.execCommand('redo')").ok(); + } + "cut" => { + webview.eval("document.execCommand('cut')").ok(); + } + "copy" => { + webview.eval("document.execCommand('copy')").ok(); + } + "select_all" => { + webview.eval("document.execCommand('selectAll')").ok(); + } + _ => {} + } +} diff --git a/src-tauri/src/setup/menus/mod.rs b/src-tauri/src/setup/menus/mod.rs new file mode 100644 index 0000000..fe00cba --- /dev/null +++ b/src-tauri/src/setup/menus/mod.rs @@ -0,0 +1,2 @@ +pub mod app; +pub mod edit; diff --git a/src-tauri/src/setup/mod.rs b/src-tauri/src/setup/mod.rs index 36f60ad..046a6e2 100644 --- a/src-tauri/src/setup/mod.rs +++ b/src-tauri/src/setup/mod.rs @@ -1,2 +1,3 @@ pub mod app; pub mod menu; +pub mod menus;