diff --git a/package.json b/package.json index 7347121..1391fe1 100644 --- a/package.json +++ b/package.json @@ -15,12 +15,14 @@ "@tauri-apps/plugin-opener": "^2", "@tauri-apps/plugin-shell": "^2.3.0", "@vueuse/core": "^13.6.0", + "lodash-es": "^4.17.21", "lucide-vue-next": "^0.539.0", "vue": "^3.5.13" }, "devDependencies": { "@tailwindcss/postcss": "^4.1.11", "@tauri-apps/cli": "^2", + "@types/lodash-es": "^4.17.12", "@vitejs/plugin-vue": "^5.2.1", "autoprefixer": "^10.4.21", "postcss": "^8.5.6", diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index 344008a..7242f33 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -1,19 +1,14 @@ +use crate::plugins::PluginConfig; +// 全局配置管理器 +use crate::PluginManagerState; use log::{info, warn}; use serde::{Deserialize, Serialize}; use std::fs; use std::path::PathBuf; +use std::sync::Mutex; +use tauri::{AppHandle, Manager, command}; -#[derive(Debug, Clone, Serialize, Deserialize)] -pub struct PluginConfig { - pub enabled: bool, // 插件是否启用 - pub execute_home: String, // 插件的执行路径 - pub extensions: Vec, // 插件支持的文件扩展名 - pub language: String, // 插件所属语言 - pub before_compile: String, // 插件在编译前执行的命令 - pub after_compile: String, // 插件在编译完成后执行的命令 - pub run_command: String, // 插件执行的命令 - pub template: String, // 插件的模板 -} +static CONFIG_MANAGER: Mutex> = Mutex::new(None); #[derive(Debug, Clone, Serialize, Deserialize)] pub struct AppConfig { @@ -42,9 +37,9 @@ pub struct ConfigManager { } impl ConfigManager { - pub fn new() -> Result { + pub fn new(app_handle: Option<&AppHandle>) -> Result { let config_path = Self::get_config_path()?; - let config = Self::load_config(&config_path)?; + let config = Self::load_config(&config_path, app_handle)?; Ok(Self { config_path, @@ -66,27 +61,65 @@ impl ConfigManager { Ok(config_file) } - fn load_config(config_path: &PathBuf) -> Result { + fn load_config( + config_path: &PathBuf, + app_handle: Option<&AppHandle>, + ) -> Result { + println!("读取配置 -> 正在读取配置文件 {:?}", config_path); if config_path.exists() { match fs::read_to_string(config_path) { Ok(content) => match serde_json::from_str::(&content) { - Ok(config) => { - info!("读取配置 -> 成功加载配置文件: {:?}", config_path); + Ok(mut config) => { + println!("读取配置 -> 成功加载配置文件: {:?}", config_path); + + // 检查 plugins 是否为 null,如果是则加载默认配置 + if config.plugins.is_none() { + println!("读取配置 -> plugins 为 null,加载默认插件配置"); + config.plugins = Self::get_default_plugins_config(app_handle); + } + Ok(config) } Err(e) => { warn!("读取配置 -> 配置文件格式错误,使用默认配置: {}", e); - Ok(AppConfig::default()) + Ok(Self::create_default_config(app_handle)) } }, Err(e) => { warn!("读取配置 -> 读取配置文件失败,使用默认配置: {}", e); - Ok(AppConfig::default()) + Ok(Self::create_default_config(app_handle)) } } } else { - info!("读取配置 -> 配置文件不存在,使用默认配置"); - Ok(AppConfig::default()) + println!("读取配置 -> 配置文件不存在,使用默认配置"); + Ok(Self::create_default_config(app_handle)) + } + } + + fn get_default_plugins_config(app_handle: Option<&AppHandle>) -> Option> { + if let Some(handle) = app_handle { + // 从 Tauri 状态中获取 PluginManager + if let Some(plugin_manager_state) = handle.try_state::() { + // 同步访问插件管理器 + if let Ok(manager) = plugin_manager_state.try_lock() { + return Some(manager.get_all_plugin_default_config()); + } else { + println!("读取配置 -> 无法获取插件管理器锁,使用空配置"); + } + } else { + println!("读取配置 -> 无法获取插件管理器状态,使用空配置"); + } + } + Some(vec![]) + } + + fn create_default_config(app_handle: Option<&AppHandle>) -> AppConfig { + AppConfig { + log_directory: None, + auto_clear_logs: Some(true), + keep_log_days: Some(30), + theme: Some("system".to_string()), + plugins: Self::get_default_plugins_config(app_handle), } } @@ -96,7 +129,7 @@ impl ConfigManager { fs::write(&self.config_path, content).map_err(|e| format!("写入配置文件失败: {}", e))?; - info!("保存配置 -> 配置文件已保存: {:?}", self.config_path); + info!("保存配置 -> 配置文件已保存 {}", self.config_path.display()); Ok(()) } @@ -114,20 +147,16 @@ impl ConfigManager { } } -// 全局配置管理器 -use std::sync::Mutex; -static CONFIG_MANAGER: Mutex> = Mutex::new(None); - // 初始化配置 -pub fn init_config() -> Result<(), String> { - let config_manager = ConfigManager::new()?; +pub fn init_config(app_handle: Option<&AppHandle>) -> Result<(), String> { + let config_manager = ConfigManager::new(app_handle)?; // 如果配置中有自定义日志目录,设置到日志系统 if let Some(log_dir) = config_manager.get_log_directory() { - info!("初始化 -> 从配置文件加载日志目录: {}", log_dir); + println!("读取配置 -> 从配置文件加载日志目录: {}", log_dir); // 使用内部函数设置,避免循环保存 if let Err(e) = crate::logger::set_log_directory_internal(log_dir.to_string()) { - warn!("初始化 -> 应用配置中的日志目录失败: {}", e); + warn!("读取配置 -> 应用配置中的日志目录失败: {}", e); } } @@ -144,9 +173,6 @@ pub fn get_config_manager() -> Result Result { let guard = get_config_manager()?; @@ -157,6 +183,15 @@ pub async fn get_app_config() -> Result { } } +pub fn get_app_config_internal() -> Result { + let guard = get_config_manager()?; + if let Some(config_manager) = guard.as_ref() { + Ok(config_manager.get_config().clone()) + } else { + Err("配置管理器未初始化".to_string()) + } +} + #[command] pub async fn update_app_config(config: AppConfig) -> Result<(), String> { let mut guard = get_config_manager()?; diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index e23f671..01a5c9d 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -15,7 +15,7 @@ use crate::utils::logger::{ }; use config::{get_app_config, get_config_path, init_config, update_app_config}; -use log::{debug, info}; +use log::{debug, error, info}; use plugins::{CodeExecutionRequest, ExecutionResult, LanguageInfo, PluginManager}; use std::fs; use std::process::{Command, Stdio}; @@ -34,6 +34,7 @@ async fn execute_code( history: State<'_, ExecutionHistory>, plugin_manager: State<'_, PluginManagerState>, ) -> Result { + info!("执行代码 -> 调用插件 [ {} ] 开始", request.language); let manager = plugin_manager.lock().await; let plugin = manager .get_plugin(&request.language) @@ -48,74 +49,77 @@ async fn execute_code( plugin.get_file_extension() )); - // 预处理代码 - let processed_code = plugin - .pre_execute_hook(&request.code) - .map_err(|e| format!("Pre-execution hook failed: {}", e))?; + let processed_code = plugin.pre_execute_hook(&request.code).map_err(|e| { + error!( + "执行代码 -> 调用插件 [ {} ] pre_execute_hook 出现错误 {:?}", + request.language, e + ); + format!("Pre-execution hook failed: {}", e) + })?; // 写入代码到临时文件 fs::write(&file_path, &processed_code) .map_err(|e| format!("Failed to write temporary file: {}", e))?; let start_time = std::time::Instant::now(); - let mut last_error = String::new(); - - // 尝试不同的命令 - for cmd in plugin.get_commands() { - let args = plugin.get_execute_args(file_path.to_str().unwrap()); - debug!( - "执行 {:?} 代码 -> 执行命令: {:?} 携带参数: {:?}", - request.language, cmd, args - ); - - let output = Command::new(cmd) - .args(&args) - .stdout(Stdio::piped()) - .stderr(Stdio::piped()) - .output(); - - match output { - Ok(output) => { - let execution_time = start_time.elapsed().as_millis(); - let timestamp = SystemTime::now() - .duration_since(UNIX_EPOCH) - .unwrap() - .as_secs(); - - // 清理临时文件 - let _ = fs::remove_file(&file_path); - - let stdout = String::from_utf8_lossy(&output.stdout).to_string(); - let stderr = String::from_utf8_lossy(&output.stderr).to_string(); - - let mut result = ExecutionResult { - success: output.status.success(), - stdout, - stderr, - execution_time, - timestamp, - language: request.language.clone(), - }; - - // 后处理 - let _ = plugin.post_execute_hook(&mut result); - - // 添加到执行历史 - drop(manager); // 释放插件管理器锁 - let mut history_guard = history.lock().await; - history_guard.push(result.clone()); - - // 保持历史记录不超过100条 - if history_guard.len() > 100 { - history_guard.remove(0); - } + let mut _last_error: String = String::new(); - return Ok(result); - } - Err(e) => { - last_error = format!("Failed to execute {} - {}", cmd, e); - continue; + let cmd = plugin.get_command(); + let args = plugin.get_execute_args(file_path.to_str().unwrap()); + info!( + "执行代码 -> 调用插件 [ {} ] 执行命令 {} 携带参数 {}", + request.language, + cmd, + args.join(" ") + ); + + let output = Command::new(&cmd) + .args(&args) + .stdout(Stdio::piped()) + .stderr(Stdio::piped()) + .output(); + + match output { + Ok(output) => { + let execution_time = start_time.elapsed().as_millis(); + let timestamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .unwrap() + .as_secs(); + + // 清理临时文件 + let _ = fs::remove_file(&file_path); + + let stdout = String::from_utf8_lossy(&output.stdout).to_string(); + let stderr = String::from_utf8_lossy(&output.stderr).to_string(); + + let mut result = ExecutionResult { + success: output.status.success(), + stdout, + stderr, + execution_time, + timestamp, + language: request.language.clone(), + }; + + // 后处理 + let _ = plugin.post_execute_hook(&mut result); + + // 添加到执行历史 + drop(manager); // 释放插件管理器锁 + let mut history_guard = history.lock().await; + history_guard.push(result.clone()); + + // 保持历史记录不超过100条 + if history_guard.len() > 100 { + history_guard.remove(0); } + + info!("执行代码 -> 调用插件 [ {} ] 完成", request.language); + return Ok(result); + } + Err(e) => { + _last_error = format!("Failed to execute {} - {}", cmd, e); } } @@ -129,6 +133,7 @@ async fn execute_code( // 清理临时文件 let _ = fs::remove_file(&file_path); + error!("执行代码 -> 调用插件 [ {} ] 失败", request.language); Ok(ExecutionResult { success: false, stdout: String::new(), @@ -136,8 +141,8 @@ async fn execute_code( "{} interpreter not found. Please install {} and ensure it's in your PATH.\n\nLast error: {}\n\nTried commands: {:?}", request.language, request.language, - last_error, - plugin.get_commands() + _last_error, + plugin.get_command().to_string() ), execution_time, timestamp, @@ -151,51 +156,62 @@ async fn get_info( language: String, plugin_manager: State<'_, PluginManagerState>, ) -> Result { + info!("获取环境 -> 调用插件 [ {} ] 开始", language); let manager = plugin_manager.lock().await; let plugin = manager .get_plugin(&language) .ok_or_else(|| format!("Unsupported language: {}", language))?; - // 尝试不同的命令 - for cmd in plugin.get_commands() { - debug!("获取插件信息 -> 执行命令: {} 语言: {}", cmd, language); - let version_output = Command::new(cmd).args(plugin.get_version_args()).output(); - - if let Ok(version_out) = version_output { - if version_out.status.success() { - let path_result = Command::new(cmd) - .arg("-c") - .arg(plugin.get_path_command()) - .output(); - - let version = String::from_utf8_lossy(&version_out.stdout) - .trim() - .to_string(); - - let path = if let Ok(path_out) = path_result { - if path_out.status.success() { - String::from_utf8_lossy(&path_out.stdout).trim().to_string() - } else { - "Command found but path unavailable".to_string() - } + plugin.pre_execute_hook("").map_err(|e| { + error!( + "获取环境 -> 调用插件 [ {} ] pre_execute_hook 出现错误 {:?}", + language, e + ); + + error!("获取环境 -> 调用插件 [ {} ] 失败", language); + format!("Pre-execution hook failed: {}", e) + })?; + + let cmd = plugin.get_command(); + debug!("获取环境 -> 插件 [ {} ] 命令 {}", language, cmd); + + let version_output = Command::new(&cmd).args(plugin.get_version_args()).output(); + if let Ok(version_out) = version_output { + if version_out.status.success() { + let path_result = Command::new(&cmd) + .arg("-c") + .arg(plugin.get_path_command()) + .output(); + + let version = String::from_utf8_lossy(&version_out.stdout) + .trim() + .to_string(); + + let path = if let Ok(path_out) = path_result { + if path_out.status.success() { + String::from_utf8_lossy(&path_out.stdout).trim().to_string() } else { - "Path detection failed".to_string() - }; - - return Ok(LanguageInfo { - installed: true, - version, - path, - language: plugin.get_language_name().to_string(), - }); - } + "Command found but path unavailable".to_string() + } + } else { + "Path detection failed".to_string() + }; + + info!("获取环境 -> 调用插件 [ {} ] 完成", language); + return Ok(LanguageInfo { + installed: true, + version, + path, + language: plugin.get_language_name().to_string(), + }); } } + error!("获取环境 -> 调用插件 [ {} ] 失败", language); Ok(LanguageInfo { installed: false, version: "Not found".to_string(), - path: format!("Not found - tried: {:?}", plugin.get_commands()), + path: format!("Not found - tried: {:?}", plugin.get_command()), language: plugin.get_language_name().to_string(), }) } @@ -233,7 +249,7 @@ fn main() { .manage(PluginManagerState::new(PluginManager::new())) .setup(|app| { // 第一步:初始化配置系统 - if let Err(e) = init_config() { + if let Err(e) = init_config(Some(app.handle())) { eprintln!("Failed to initialize config: {}", e); } diff --git a/src-tauri/src/plugins/manager.rs b/src-tauri/src/plugins/manager.rs index 787641a..dca12c5 100644 --- a/src-tauri/src/plugins/manager.rs +++ b/src-tauri/src/plugins/manager.rs @@ -1,4 +1,4 @@ -use super::{LanguagePlugin, python2::Python2Plugin, python3::Python3Plugin}; +use super::{LanguagePlugin, PluginConfig, python2::Python2Plugin, python3::Python3Plugin}; use std::collections::HashMap; pub struct PluginManager { @@ -53,12 +53,8 @@ impl PluginManager { pub fn get_plugin_info(&self, language: &str) -> Option { self.get_plugin(language).map(|plugin| PluginInfo { name: plugin.get_language_name().to_string(), - file_extension: plugin.get_file_extension().to_string(), - available_commands: plugin - .get_commands() - .iter() - .map(|s| s.to_string()) - .collect(), + file_extension: plugin.get_file_extension(), + available_commands: vec![plugin.get_command().to_string()], }) } @@ -68,15 +64,18 @@ impl PluginManager { .values() .map(|plugin| PluginInfo { name: plugin.get_language_name().to_string(), - file_extension: plugin.get_file_extension().to_string(), - available_commands: plugin - .get_commands() - .iter() - .map(|s| s.to_string()) - .collect(), + file_extension: plugin.get_file_extension(), + available_commands: vec![plugin.get_command().to_string()], }) .collect() } + + pub fn get_all_plugin_default_config(&self) -> Vec { + self.plugins + .values() + .map(|plugin| plugin.get_default_config()) + .collect() + } } #[derive(Debug, serde::Serialize)] diff --git a/src-tauri/src/plugins/mod.rs b/src-tauri/src/plugins/mod.rs index a0f7f4c..2b62f8e 100644 --- a/src-tauri/src/plugins/mod.rs +++ b/src-tauri/src/plugins/mod.rs @@ -1,4 +1,7 @@ +use crate::config::get_app_config_internal; +use log::{debug, info}; use serde::{Deserialize, Serialize}; +use std::path::PathBuf; // 通用结构定义 #[derive(Debug, Serialize, Deserialize, Clone)] @@ -25,23 +28,171 @@ pub struct LanguageInfo { pub language: String, } +// 插件配置结构 +#[derive(Debug, Clone, Serialize, Deserialize)] +pub struct PluginConfig { + pub enabled: bool, // 插件是否启用 + pub execute_home: Option, // 插件的执行路径 + pub extension: String, // 插件支持的文件扩展名 + pub language: String, // 插件所属语言 + pub before_compile: Option, // 插件在编译前执行的命令 + pub after_compile: Option, // 插件在编译完成后执行的命令 + pub run_command: Option, // 插件执行的命令,例如 "python2 $filename" + pub template: Option, // 插件的模板 +} + // 语言插件接口 pub trait LanguagePlugin: Send + Sync { + // 获取插件优先级 fn get_order(&self) -> i32 { 0 } + + // 获取插件名称 fn get_language_name(&self) -> &'static str; - fn get_file_extension(&self) -> &'static str; - fn get_commands(&self) -> Vec<&'static str>; + + // 获取插件唯一标记 + fn get_language_key(&self) -> &'static str; + + // 获取插件支持的文件扩展名 + fn get_file_extension(&self) -> String { + self.get_config().unwrap().extension.clone() + } + + // 获取执行目录 + fn get_execute_home(&self) -> Option { + self.get_config() + .and_then(|config| config.execute_home.clone()) + .map(PathBuf::from) + } + + // 获取插件支持的命令 + fn get_command(&self) -> 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(); + } + } + self.get_default_command() + } + + // 获取插件配置 + fn get_config(&self) -> Option { + // 获取全局应用配置 + if let Ok(app_config) = get_app_config_internal() { + // 检查是否有插件配置 + if let Some(ref plugins) = app_config.plugins { + // 根据当前插件的语言名称过滤配置 + let language_name = self.get_language_key(); + + // 查找匹配的插件配置 + let found_config = plugins + .iter() + .find(|config| config.language == language_name) + .cloned(); + + debug!( + "执行代码 -> 获取插件 [ {} ] 配置 {:?}", + language_name, found_config + ); + return found_config; + } + } + + // 如果没有找到配置,返回默认配置 + debug!( + "执行代码 -> 插件 [ {} ] 未找到配置,使用默认配置", + self.get_language_key() + ); + Some(self.get_default_config()) + } + + // 检查插件是否启用 + #[allow(dead_code)] + fn is_enabled(&self) -> bool { + self.get_config() + .map(|config| config.enabled) + .unwrap_or(false) + } + fn get_version_args(&self) -> Vec<&'static str>; fn get_execute_args(&self, file_path: &str) -> Vec; fn get_path_command(&self) -> String; - // 可选的钩子函数 - fn pre_execute_hook(&self, _code: &str) -> Result { - Ok(_code.to_string()) + // 构建默认配置 + fn get_default_config(&self) -> PluginConfig; + + // 获取默认命令 + fn get_default_command(&self) -> String; + + // 预执行钩子 + fn pre_execute_hook(&self, code: &str) -> Result { + info!( + "执行代码 -> 插件 [ {} ] 处理 pre_execute_hook 开始", + self.get_language_key() + ); + + if let Some(config) = self.get_config() { + // 1. 执行 before_compile 命令 + if let Some(before_cmd) = &config.before_compile { + info!( + "执行代码 -> 插件 [ {} ] 处理 pre_execute_hook 执行 before_compile 命令: {}", + self.get_language_key(), + before_cmd + ); + let output = std::process::Command::new(before_cmd) + .output() + .map_err(|e| { + info!( + "执行代码 -> 插件 [ {} ] 处理 pre_execute_hook 执行 before_compile 命令 {} 失败 {:?}", + self.get_language_key(), + before_cmd, + e + ); + + format!("执行 before_compile 失败: {}", e) + })?; + + if !output.status.success() { + return Err(format!( + "before_compile 命令执行失败: {}", + String::from_utf8_lossy(&output.stderr) + )); + } + } + + // 2. 切换到 execute_home 目录 + if let Some(execute_home) = self.get_execute_home() { + info!( + "执行代码 -> 插件 [ {} ] 处理 pre_execute_hook 切换到执行目录 {}", + self.get_language_key(), + execute_home.display() + ); + std::env::set_current_dir(&execute_home).map_err(|e| { + info!( + "执行代码 -> 插件 [ {} ] 处理 pre_execute_hook 切换到执行目录 {} 失败 {:?}", + self.get_language_key(), + execute_home.display(), + e + ); + format!("切换目录失败: {}", e) + })?; + } + } + + info!( + "执行代码 -> 插件 [ {} ] 处理 pre_execute_hook 结束", + self.get_language_key() + ); + + Ok(code.to_string()) } + // 后执行钩子 fn post_execute_hook(&self, _result: &mut ExecutionResult) -> Result<(), String> { Ok(()) } @@ -51,5 +202,4 @@ pub trait LanguagePlugin: Send + Sync { pub mod manager; pub mod python2; pub mod python3; - pub use manager::PluginManager; diff --git a/src-tauri/src/plugins/python2.rs b/src-tauri/src/plugins/python2.rs index 6e387ea..808d09e 100644 --- a/src-tauri/src/plugins/python2.rs +++ b/src-tauri/src/plugins/python2.rs @@ -1,4 +1,5 @@ -use super::{ExecutionResult, LanguagePlugin}; +use super::{LanguagePlugin, PluginConfig}; +use std::vec; pub struct Python2Plugin; @@ -11,12 +12,8 @@ impl LanguagePlugin for Python2Plugin { "Python 2" } - fn get_file_extension(&self) -> &'static str { - "py" - } - - fn get_commands(&self) -> Vec<&'static str> { - vec!["python", "python2"] + fn get_language_key(&self) -> &'static str { + "python2" } fn get_version_args(&self) -> Vec<&'static str> { @@ -31,23 +28,20 @@ impl LanguagePlugin for Python2Plugin { "import sys; print(sys.executable)".to_string() } - fn pre_execute_hook(&self, code: &str) -> Result { - Ok(code.to_string()) - } - - 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:"); + fn get_default_config(&self) -> PluginConfig { + PluginConfig { + enabled: true, + language: String::from("python2"), + before_compile: None, + extension: String::from("py"), + execute_home: None, + run_command: Option::from(String::from("python2 $filename")), + after_compile: None, + template: None, } + } - Ok(()) + fn get_default_command(&self) -> String { + self.get_config().unwrap().run_command.unwrap() } } diff --git a/src-tauri/src/plugins/python3.rs b/src-tauri/src/plugins/python3.rs index 55db338..2887bf0 100644 --- a/src-tauri/src/plugins/python3.rs +++ b/src-tauri/src/plugins/python3.rs @@ -1,4 +1,4 @@ -use super::{ExecutionResult, LanguagePlugin}; +use super::{ExecutionResult, LanguagePlugin, PluginConfig}; pub struct Python3Plugin; @@ -11,12 +11,8 @@ impl LanguagePlugin for Python3Plugin { "Python 3" } - fn get_file_extension(&self) -> &'static str { - "py" - } - - fn get_commands(&self) -> Vec<&'static str> { - vec!["python", "python3"] + fn get_language_key(&self) -> &'static str { + "python3" } fn get_version_args(&self) -> Vec<&'static str> { @@ -31,8 +27,21 @@ impl LanguagePlugin for Python3Plugin { "import sys; print(sys.executable)".to_string() } - fn pre_execute_hook(&self, code: &str) -> Result { - Ok(code.to_string()) + fn get_default_config(&self) -> PluginConfig { + PluginConfig { + enabled: true, + language: String::from("python3"), + before_compile: None, + extension: String::from("py"), + execute_home: None, + run_command: Option::from(String::from("python3 $filename")), + after_compile: None, + template: None, + } + } + + fn get_default_command(&self) -> String { + self.get_config().unwrap().run_command.unwrap() } fn post_execute_hook(&self, result: &mut ExecutionResult) -> Result<(), String> { diff --git a/src-tauri/src/setup/menus/app.rs b/src-tauri/src/setup/menus/app.rs index 3a7c31b..55b3923 100644 --- a/src-tauri/src/setup/menus/app.rs +++ b/src-tauri/src/setup/menus/app.rs @@ -1,3 +1,4 @@ +use log::info; use tauri::{ AppHandle, Emitter, menu::{MenuItemBuilder, Submenu, SubmenuBuilder}, @@ -38,6 +39,7 @@ pub fn handle_app_menu_event(app: &AppHandle, event_id: &str) { let _event = app.emit("show-settings", ()); } "quit" => { + info!("CodeForge 应用关闭"); app.exit(0); } _ => {} diff --git a/src/components/Settings.vue b/src/components/Settings.vue index 8c1f8f6..d288f20 100644 --- a/src/components/Settings.vue +++ b/src/components/Settings.vue @@ -1,24 +1,32 @@ diff --git a/src/types/plugin.ts b/src/types/plugin.ts new file mode 100644 index 0000000..6aba4e4 --- /dev/null +++ b/src/types/plugin.ts @@ -0,0 +1,11 @@ +export default interface PluginConfig +{ + enabled: boolean // 插件是否启用 + execute_home?: string // 插件的执行路径 + extension: string // 插件支持的文件扩展名 + language: string // 插件所属语言 + before_compile?: string // 插件在编译前执行的命令 + after_compile?: string // 插件在编译完成后执行的命令 + run_command?: string // 插件执行的命令,例如 "python2 $filename" + template?: string // 插件的模板 +} diff --git a/src/ui/Tabs.vue b/src/ui/Tabs.vue index 86243ba..05374e1 100644 --- a/src/ui/Tabs.vue +++ b/src/ui/Tabs.vue @@ -189,7 +189,7 @@ const headerClasses = computed(() => { baseClasses.push('border-t', 'border-gray-200', 'dark:border-gray-700') } else if (props.position === 'left') { - baseClasses.push('flex-col', 'border-r', 'border-gray-200', 'dark:border-gray-700', 'mr-4', 'border-b-0') + baseClasses.push('flex-col', 'border-gray-200', 'dark:border-gray-700', 'mr-4', 'border-b-0') } else if (props.position === 'right') { baseClasses.push('flex-col', 'border-l', 'border-gray-200', 'dark:border-gray-700', 'ml-4', 'order-2', 'border-b-0')