From 21c3c86d22754a5c0a3b3320107c150ba0a47197 Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Sun, 10 Aug 2025 22:42:56 +0800 Subject: [PATCH 1/4] =?UTF-8?q?feat=20(core):=20=E4=BC=98=E5=8C=96=20Tabs?= =?UTF-8?q?=20=E7=BB=84=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/ui/Tabs.vue | 23 +++++++++++++++++++---- 1 file changed, 19 insertions(+), 4 deletions(-) diff --git a/src/ui/Tabs.vue b/src/ui/Tabs.vue index 05374e1..73ad136 100644 --- a/src/ui/Tabs.vue +++ b/src/ui/Tabs.vue @@ -244,6 +244,14 @@ const getTabButtonClasses = (tab: Tab, _index: number) => { 'transition-all', 'duration-200', 'cursor-pointer', 'whitespace-nowrap' ] + // 位置相关 + if (props.position === 'left' || props.position === 'right') { + baseClasses.push('my-0.5') + } + else { + baseClasses.push('mx-0.5') + } + // 尺寸 if (props.size === 'sm') { baseClasses.push('px-3', 'py-1.5', 'text-xs') @@ -270,7 +278,7 @@ const getTabButtonClasses = (tab: Tab, _index: number) => { baseClasses.push('text-blue-600', 'dark:text-blue-400', 'border-blue-600', 'dark:border-blue-400') } else { - baseClasses.push('text-gray-600', 'hover:text-gray-900', 'dark:text-gray-400', 'dark:hover:text-gray-100', 'hover:border-gray-300', 'dark:hover:border-gray-600') + baseClasses.push('text-gray-600', 'hover:text-blue-500', 'dark:text-gray-400', 'dark:hover:text-blue-300', 'hover:border-blue-300', 'dark:hover:border-blue-500') } break @@ -281,7 +289,7 @@ const getTabButtonClasses = (tab: Tab, _index: number) => { baseClasses.push('bg-white', 'dark:bg-gray-700', 'shadow-sm', 'text-blue-600', 'dark:text-blue-400') } else { - baseClasses.push('text-gray-600', 'hover:text-gray-900', 'dark:text-gray-400', 'dark:hover:text-gray-100') + baseClasses.push('text-gray-600', 'hover:text-blue-500', 'dark:text-gray-400', 'dark:hover:text-blue-300', 'hover:bg-gray-100', 'dark:hover:bg-gray-600') } break @@ -291,7 +299,14 @@ const getTabButtonClasses = (tab: Tab, _index: number) => { baseClasses.push('bg-blue-600', 'text-white', 'border-blue-600', 'z-10') } else { - baseClasses.push('bg-white', 'dark:bg-gray-800', 'text-gray-600', 'hover:text-gray-900', 'dark:text-gray-400', 'dark:hover:text-gray-100') + baseClasses.push('bg-white', + 'dark:bg-gray-800', + 'text-gray-600', + 'hover:text-blue-500', + 'dark:text-gray-400', + 'dark:hover:text-blue-300', + 'hover:bg-blue-50', + 'dark:hover:bg-blue-900/20') } break @@ -300,7 +315,7 @@ const getTabButtonClasses = (tab: Tab, _index: number) => { baseClasses.push('text-blue-600', 'dark:text-blue-400') } else { - baseClasses.push('text-gray-600', 'hover:text-gray-900', 'dark:text-gray-400', 'dark:hover:text-gray-100') + baseClasses.push('text-gray-600', 'hover:text-blue-500', 'dark:text-gray-400', 'dark:hover:text-blue-300') } } From 27267c638b5be79ec7874f9fe91193e71d181174 Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Sun, 10 Aug 2025 23:18:01 +0800 Subject: [PATCH 2/4] =?UTF-8?q?fix=20(core):=20=E4=BF=AE=E5=A4=8D=20execut?= =?UTF-8?q?e=5Fhome=20=E4=B8=BA=E7=A9=BA=E5=AF=BC=E8=87=B4=E8=BF=9B?= =?UTF-8?q?=E5=85=A5=E7=A9=BA=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/main.rs | 12 ++++++----- src-tauri/src/plugins/manager.rs | 4 ++-- src-tauri/src/plugins/mod.rs | 35 +++++++++++++++++++++++++------- src-tauri/src/plugins/python2.rs | 4 ---- src-tauri/src/plugins/python3.rs | 4 ---- 5 files changed, 37 insertions(+), 22 deletions(-) diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 01a5c9d..6833c92 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -64,7 +64,7 @@ async fn execute_code( let start_time = std::time::Instant::now(); let mut _last_error: String = String::new(); - let cmd = plugin.get_command(); + let cmd = plugin.get_command(None); let args = plugin.get_execute_args(file_path.to_str().unwrap()); info!( "执行代码 -> 调用插件 [ {} ] 执行命令 {} 携带参数 {}", @@ -74,7 +74,7 @@ async fn execute_code( ); let output = Command::new(&cmd) - .args(&args) + .args(args) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .output(); @@ -142,7 +142,9 @@ async fn execute_code( request.language, request.language, _last_error, - plugin.get_command().to_string() + plugin + .get_command(Some(file_path.to_str().unwrap())) + .to_string() ), execution_time, timestamp, @@ -172,7 +174,7 @@ async fn get_info( format!("Pre-execution hook failed: {}", e) })?; - let cmd = plugin.get_command(); + let cmd = plugin.get_command(None); debug!("获取环境 -> 插件 [ {} ] 命令 {}", language, cmd); let version_output = Command::new(&cmd).args(plugin.get_version_args()).output(); @@ -211,7 +213,7 @@ async fn get_info( Ok(LanguageInfo { installed: false, version: "Not found".to_string(), - path: format!("Not found - tried: {:?}", plugin.get_command()), + path: format!("Not found - tried: {:?}", plugin.get_command(None)), language: plugin.get_language_name().to_string(), }) } diff --git a/src-tauri/src/plugins/manager.rs b/src-tauri/src/plugins/manager.rs index dca12c5..ad74146 100644 --- a/src-tauri/src/plugins/manager.rs +++ b/src-tauri/src/plugins/manager.rs @@ -54,7 +54,7 @@ impl PluginManager { self.get_plugin(language).map(|plugin| PluginInfo { name: plugin.get_language_name().to_string(), file_extension: plugin.get_file_extension(), - available_commands: vec![plugin.get_command().to_string()], + available_commands: vec![plugin.get_command(None).to_string()], }) } @@ -65,7 +65,7 @@ impl PluginManager { .map(|plugin| PluginInfo { name: plugin.get_language_name().to_string(), file_extension: plugin.get_file_extension(), - available_commands: vec![plugin.get_command().to_string()], + available_commands: vec![plugin.get_command(None).to_string()], }) .collect() } diff --git a/src-tauri/src/plugins/mod.rs b/src-tauri/src/plugins/mod.rs index 2b62f8e..5b6f7b7 100644 --- a/src-tauri/src/plugins/mod.rs +++ b/src-tauri/src/plugins/mod.rs @@ -63,18 +63,23 @@ pub trait LanguagePlugin: Send + Sync { fn get_execute_home(&self) -> Option { self.get_config() .and_then(|config| config.execute_home.clone()) + .filter(|path| !path.trim().is_empty()) // 过滤掉空字符串和只有空白字符的字符串 .map(PathBuf::from) } // 获取插件支持的命令 - fn get_command(&self) -> String { + fn get_command(&self, file_path: Option<&str>) -> String { if let Some(config) = self.get_config() { if let Some(run_cmd) = &config.run_command { - return run_cmd - .split_whitespace() - .next() - .unwrap_or(&config.language) - .to_string(); + if let Some(path) = file_path { + return run_cmd.replace("$filename", path); + } else { + return run_cmd + .split_whitespace() + .next() + .unwrap_or(&config.language) + .to_string(); + } } } self.get_default_command() @@ -120,7 +125,23 @@ pub trait LanguagePlugin: Send + Sync { } fn get_version_args(&self) -> Vec<&'static str>; - fn get_execute_args(&self, file_path: &str) -> Vec; + + fn get_execute_args(&self, file_path: &str) -> Vec { + if let Some(config) = self.get_config() { + if let Some(run_cmd) = &config.run_command { + // 替换 $filename 后分割,跳过第一个元素(命令本身) + let full_cmd = run_cmd.replace("$filename", file_path); + return full_cmd + .split_whitespace() + .skip(1) // 跳过命令部分,只返回参数 + .map(|s| s.to_string()) + .collect(); + } + } + // 默认情况下,文件路径就是唯一的参数 + vec![file_path.to_string()] + } + fn get_path_command(&self) -> String; // 构建默认配置 diff --git a/src-tauri/src/plugins/python2.rs b/src-tauri/src/plugins/python2.rs index 808d09e..2178021 100644 --- a/src-tauri/src/plugins/python2.rs +++ b/src-tauri/src/plugins/python2.rs @@ -20,10 +20,6 @@ impl LanguagePlugin for Python2Plugin { vec!["--version"] } - fn get_execute_args(&self, file_path: &str) -> Vec { - vec![file_path.to_string()] - } - fn get_path_command(&self) -> String { "import sys; print(sys.executable)".to_string() } diff --git a/src-tauri/src/plugins/python3.rs b/src-tauri/src/plugins/python3.rs index 2887bf0..71ff750 100644 --- a/src-tauri/src/plugins/python3.rs +++ b/src-tauri/src/plugins/python3.rs @@ -19,10 +19,6 @@ impl LanguagePlugin for Python3Plugin { vec!["--version"] } - fn get_execute_args(&self, file_path: &str) -> Vec { - vec![file_path.to_string()] - } - fn get_path_command(&self) -> String { "import sys; print(sys.executable)".to_string() } From 8c336d8f707c919cc5ac73ca9dd2871005871d5c Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Sun, 10 Aug 2025 23:33:19 +0800 Subject: [PATCH 3/4] =?UTF-8?q?feat=20(core):=20=E6=97=A5=E5=BF=97?= =?UTF-8?q?=E6=A0=B9=E6=8D=AE=20level=20=E6=8B=86=E5=88=86=E7=8B=AC?= =?UTF-8?q?=E7=AB=8B=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/logger.rs | 55 +++++++++++++++++++++++++++---------- src/components/Settings.vue | 2 +- 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src-tauri/src/logger.rs b/src-tauri/src/logger.rs index 222ea40..c466c83 100644 --- a/src-tauri/src/logger.rs +++ b/src-tauri/src/logger.rs @@ -8,33 +8,35 @@ use tauri::{AppHandle, Manager}; static LOG_DIRECTORY: Mutex> = Mutex::new(None); pub fn setup_logger(app: &AppHandle) -> Result<(), fern::InitError> { - // 获取日志目录(可能是自定义的,也可能是默认的) + // 获取日志目录 let log_dir = get_effective_log_directory(app); // 创建日志目录 if let Err(e) = fs::create_dir_all(&log_dir) { eprintln!("Failed to create log directory: {}", e); - // 如果自定义目录创建失败,回退到默认目录 let default_dir = get_default_log_directory(app); fs::create_dir_all(&default_dir).expect("Failed to create default log directory"); - // 更新为默认目录 { let mut guard = LOG_DIRECTORY.lock().unwrap(); - *guard = None; // 清除自定义设置 + *guard = None; } warn!("日志目录创建失败,使用默认目录: {:?}", default_dir); - } else { - info!("日志目录: {:?}", log_dir); } // 生成当天的日志文件名 let today = Local::now().format("%Y-%m-%d").to_string(); - let log_file = log_dir.join(format!("codeforge-{}.log", today)); - // 配置日志 - fern::Dispatch::new() + // 不同级别的日志文件 + let all_log_file = log_dir.join(format!("codeforge-{}.log", today)); + let error_log_file = log_dir.join(format!("codeforge-error-{}.log", today)); + let warn_log_file = log_dir.join(format!("codeforge-warn-{}.log", today)); + let info_log_file = log_dir.join(format!("codeforge-info-{}.log", today)); + let debug_log_file = log_dir.join(format!("codeforge-debug-{}.log", today)); + + // 基础配置 + let base_config = fern::Dispatch::new() .format(|out, message, record| { out.finish(format_args!( "[{}] [{}] [{}:{}] {}", @@ -45,17 +47,40 @@ pub fn setup_logger(app: &AppHandle) -> Result<(), fern::InitError> { message )) }) - .level(LevelFilter::Debug) // 设置日志级别 - .level_for("hyper", LevelFilter::Warn) // 减少第三方库的日志 + .level(LevelFilter::Debug) + .level_for("hyper", LevelFilter::Warn) .level_for("reqwest", LevelFilter::Warn) - .level_for("tauri", LevelFilter::Info) // Tauri 框架日志 - .chain(std::io::stdout()) // 同时输出到控制台 - .chain(fern::log_file(&log_file)?) // 输出到文件 + .level_for("tauri", LevelFilter::Info); + + // 配置不同级别的日志输出 + base_config + .chain(std::io::stdout()) // 控制台输出 + .chain(fern::log_file(&all_log_file)?) // 所有级别写入总文件 + .chain( + fern::Dispatch::new() + .filter(|metadata| metadata.level() == log::Level::Error) + .chain(fern::log_file(&error_log_file)?), + ) + .chain( + fern::Dispatch::new() + .filter(|metadata| metadata.level() == log::Level::Warn) + .chain(fern::log_file(&warn_log_file)?), + ) + .chain( + fern::Dispatch::new() + .filter(|metadata| metadata.level() == log::Level::Info) + .chain(fern::log_file(&info_log_file)?), + ) + .chain( + fern::Dispatch::new() + .filter(|metadata| metadata.level() == log::Level::Debug) + .chain(fern::log_file(&debug_log_file)?), + ) .apply()?; info!("CodeForge 应用启动"); info!("应用版本: {}", env!("CARGO_PKG_VERSION")); - info!("日志文件: {:?}", log_file); + info!("日志文件目录 {}", log_dir.display()); Ok(()) } diff --git a/src/components/Settings.vue b/src/components/Settings.vue index d288f20..ec139a0 100644 --- a/src/components/Settings.vue +++ b/src/components/Settings.vue @@ -8,7 +8,7 @@ From 7f479121b77db171565e0a9de7b0ab13241e7b58 Mon Sep 17 00:00:00 2001 From: qianmoQ Date: Sun, 10 Aug 2025 23:47:41 +0800 Subject: [PATCH 4/4] =?UTF-8?q?feat=20(core):=20=E4=BC=98=E5=8C=96=20execu?= =?UTF-8?q?te=5Fhook=20=E8=BF=94=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src-tauri/src/plugins/mod.rs | 6 +++++- src-tauri/src/plugins/python3.rs | 18 +----------------- src/components/setting/Language.vue | 2 +- 3 files changed, 7 insertions(+), 19 deletions(-) diff --git a/src-tauri/src/plugins/mod.rs b/src-tauri/src/plugins/mod.rs index 5b6f7b7..062b759 100644 --- a/src-tauri/src/plugins/mod.rs +++ b/src-tauri/src/plugins/mod.rs @@ -214,7 +214,11 @@ pub trait LanguagePlugin: Send + Sync { } // 后执行钩子 - fn post_execute_hook(&self, _result: &mut ExecutionResult) -> Result<(), String> { + fn post_execute_hook(&self, result: &mut ExecutionResult) -> Result<(), String> { + if result.success && result.stdout.is_empty() && result.stderr.is_empty() { + result.stdout = "代码执行成功 (无输出)".to_string(); + } + Ok(()) } } diff --git a/src-tauri/src/plugins/python3.rs b/src-tauri/src/plugins/python3.rs index 71ff750..557efe6 100644 --- a/src-tauri/src/plugins/python3.rs +++ b/src-tauri/src/plugins/python3.rs @@ -1,4 +1,4 @@ -use super::{ExecutionResult, LanguagePlugin, PluginConfig}; +use super::{LanguagePlugin, PluginConfig}; pub struct Python3Plugin; @@ -39,20 +39,4 @@ impl LanguagePlugin for Python3Plugin { fn get_default_command(&self) -> String { self.get_config().unwrap().run_command.unwrap() } - - fn post_execute_hook(&self, result: &mut ExecutionResult) -> Result<(), String> { - // Python 特定的后处理 - if result.success && result.stdout.is_empty() && result.stderr.is_empty() { - result.stdout = "代码执行成功 (无输出)".to_string(); - } - - // 清理 Python 特定的错误信息 - if !result.stderr.is_empty() { - result.stderr = result - .stderr - .replace("Traceback (most recent call last):", "Error:"); - } - - Ok(()) - } } diff --git a/src/components/setting/Language.vue b/src/components/setting/Language.vue index 4519032..e69fbed 100644 --- a/src/components/setting/Language.vue +++ b/src/components/setting/Language.vue @@ -1,6 +1,6 @@