Compare commits
15 Commits
14
UPDATELOG.md
14
UPDATELOG.md
@@ -1,4 +1,4 @@
|
|||||||
## v2.1.1
|
## v2.1.2
|
||||||
|
|
||||||
**发行代号:臻**
|
**发行代号:臻**
|
||||||
|
|
||||||
@@ -6,8 +6,16 @@
|
|||||||
|
|
||||||
感谢 Tychristine 对社区群组管理做出的重大贡献!
|
感谢 Tychristine 对社区群组管理做出的重大贡献!
|
||||||
|
|
||||||
2.1.1相对2.1.0(已下架不在提供)更新了:
|
2.1.2相对2.1.1(已下架不在提供)更新了:
|
||||||
|
|
||||||
|
- 无法更新和签名验证失败的问题(该死的CDN缓存)
|
||||||
|
- 设置菜单区分Verge基本设置和高级设置
|
||||||
|
- 增加v2 Updater的更多功能和权限
|
||||||
|
- 退出Verge后Tun代理状态仍保留的问题
|
||||||
|
|
||||||
|
2.1.1相对2.1.0(已下架不在提供)更新了:
|
||||||
|
|
||||||
|
- 检测所需的Clash Verge Service版本(杀毒软件误报可能与此有关,因为检测和安装新版本Service需管理员权限)
|
||||||
- MacOS下支持彩色托盘图标和更好速率显示(感谢Tunglies)
|
- MacOS下支持彩色托盘图标和更好速率显示(感谢Tunglies)
|
||||||
- 文件类型判断不准导致脚本检测报错的问题
|
- 文件类型判断不准导致脚本检测报错的问题
|
||||||
- 打开Win下的阴影(Win10因底层兼容性问题,可能圆角和边框显示不太完美)
|
- 打开Win下的阴影(Win10因底层兼容性问题,可能圆角和边框显示不太完美)
|
||||||
@@ -15,6 +23,8 @@
|
|||||||
- 修复Linux下编译问题
|
- 修复Linux下编译问题
|
||||||
- 修复热键无法关闭面板的问题
|
- 修复热键无法关闭面板的问题
|
||||||
|
|
||||||
|
## v2.1.0 - 发行代号:臻
|
||||||
|
|
||||||
### 功能新增
|
### 功能新增
|
||||||
|
|
||||||
- 新增窗口状态实时监控与自动保存功能
|
- 新增窗口状态实时监控与自动保存功能
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "clash-verge",
|
"name": "clash-verge",
|
||||||
"version": "2.1.1",
|
"version": "2.1.2",
|
||||||
"license": "GPL-3.0-only",
|
"license": "GPL-3.0-only",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "cross-env RUST_BACKTRACE=1 tauri dev",
|
"dev": "cross-env RUST_BACKTRACE=1 tauri dev",
|
||||||
|
|||||||
@@ -60,35 +60,35 @@ async function resolveUpdater() {
|
|||||||
const { name, browser_download_url } = asset;
|
const { name, browser_download_url } = asset;
|
||||||
|
|
||||||
// win64 url
|
// win64 url
|
||||||
if (name.endsWith("x64-setup.nsis.zip")) {
|
if (name.endsWith("x64-setup.exe")) {
|
||||||
updateData.platforms.win64.url = browser_download_url;
|
updateData.platforms.win64.url = browser_download_url;
|
||||||
updateData.platforms["windows-x86_64"].url = browser_download_url;
|
updateData.platforms["windows-x86_64"].url = browser_download_url;
|
||||||
}
|
}
|
||||||
// win64 signature
|
// win64 signature
|
||||||
if (name.endsWith("x64-setup.nsis.zip.sig")) {
|
if (name.endsWith("x64-setup.exe.sig")) {
|
||||||
const sig = await getSignature(browser_download_url);
|
const sig = await getSignature(browser_download_url);
|
||||||
updateData.platforms.win64.signature = sig;
|
updateData.platforms.win64.signature = sig;
|
||||||
updateData.platforms["windows-x86_64"].signature = sig;
|
updateData.platforms["windows-x86_64"].signature = sig;
|
||||||
}
|
}
|
||||||
|
|
||||||
// win32 url
|
// win32 url
|
||||||
if (name.endsWith("x86-setup.nsis.zip")) {
|
if (name.endsWith("x86-setup.exe")) {
|
||||||
updateData.platforms["windows-x86"].url = browser_download_url;
|
updateData.platforms["windows-x86"].url = browser_download_url;
|
||||||
updateData.platforms["windows-i686"].url = browser_download_url;
|
updateData.platforms["windows-i686"].url = browser_download_url;
|
||||||
}
|
}
|
||||||
// win32 signature
|
// win32 signature
|
||||||
if (name.endsWith("x86-setup.nsis.zip.sig")) {
|
if (name.endsWith("x86-setup.exe.sig")) {
|
||||||
const sig = await getSignature(browser_download_url);
|
const sig = await getSignature(browser_download_url);
|
||||||
updateData.platforms["windows-x86"].signature = sig;
|
updateData.platforms["windows-x86"].signature = sig;
|
||||||
updateData.platforms["windows-i686"].signature = sig;
|
updateData.platforms["windows-i686"].signature = sig;
|
||||||
}
|
}
|
||||||
|
|
||||||
// win arm url
|
// win arm url
|
||||||
if (name.endsWith("arm64-setup.nsis.zip")) {
|
if (name.endsWith("arm64-setup.exe")) {
|
||||||
updateData.platforms["windows-aarch64"].url = browser_download_url;
|
updateData.platforms["windows-aarch64"].url = browser_download_url;
|
||||||
}
|
}
|
||||||
// win arm signature
|
// win arm signature
|
||||||
if (name.endsWith("arm64-setup.nsis.zip.sig")) {
|
if (name.endsWith("arm64-setup.exe.sig")) {
|
||||||
const sig = await getSignature(browser_download_url);
|
const sig = await getSignature(browser_download_url);
|
||||||
updateData.platforms["windows-aarch64"].signature = sig;
|
updateData.platforms["windows-aarch64"].signature = sig;
|
||||||
}
|
}
|
||||||
|
|||||||
2
src-tauri/Cargo.lock
generated
2
src-tauri/Cargo.lock
generated
@@ -999,7 +999,7 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clash-verge"
|
name = "clash-verge"
|
||||||
version = "2.1.1"
|
version = "2.1.2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gcm",
|
"aes-gcm",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "clash-verge"
|
name = "clash-verge"
|
||||||
version = "2.1.1"
|
version = "2.1.2"
|
||||||
description = "clash verge"
|
description = "clash verge"
|
||||||
authors = ["zzzgydi", "wonfen", "MystiPanda"]
|
authors = ["zzzgydi", "wonfen", "MystiPanda"]
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
|
|||||||
@@ -6,6 +6,13 @@
|
|||||||
"permissions": [
|
"permissions": [
|
||||||
"global-shortcut:default",
|
"global-shortcut:default",
|
||||||
"updater:default",
|
"updater:default",
|
||||||
|
"dialog:default",
|
||||||
|
"dialog:allow-ask",
|
||||||
|
"dialog:allow-message",
|
||||||
|
"updater:default",
|
||||||
|
"updater:allow-check",
|
||||||
|
"updater:allow-download-and-install",
|
||||||
|
"process:allow-restart",
|
||||||
"deep-link:default",
|
"deep-link:default",
|
||||||
"window-state:default",
|
"window-state:default",
|
||||||
"window-state:default",
|
"window-state:default",
|
||||||
|
|||||||
@@ -187,24 +187,57 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 在异步操作前完成所有文件操作
|
// 在异步操作前完成所有文件操作
|
||||||
let (file_path, original_content) = {
|
let (file_path, original_content, is_merge_file) = {
|
||||||
let profiles = Config::profiles();
|
let profiles = Config::profiles();
|
||||||
let profiles_guard = profiles.latest();
|
let profiles_guard = profiles.latest();
|
||||||
let item = wrap_err!(profiles_guard.get_item(&index))?;
|
let item = wrap_err!(profiles_guard.get_item(&index))?;
|
||||||
|
// 确定是否为merge类型文件
|
||||||
|
let is_merge = item.itype.as_ref().map_or(false, |t| t == "merge");
|
||||||
let content = wrap_err!(item.read_file())?;
|
let content = wrap_err!(item.read_file())?;
|
||||||
let path = item.file.clone().ok_or("file field is null")?;
|
let path = item.file.clone().ok_or("file field is null")?;
|
||||||
let profiles_dir = wrap_err!(dirs::app_profiles_dir())?;
|
let profiles_dir = wrap_err!(dirs::app_profiles_dir())?;
|
||||||
(profiles_dir.join(path), content)
|
(profiles_dir.join(path), content, is_merge)
|
||||||
};
|
};
|
||||||
|
|
||||||
// 保存新的配置文件
|
// 保存新的配置文件
|
||||||
wrap_err!(fs::write(&file_path, file_data.clone().unwrap()))?;
|
wrap_err!(fs::write(&file_path, file_data.clone().unwrap()))?;
|
||||||
|
|
||||||
let file_path_str = file_path.to_string_lossy();
|
let file_path_str = file_path.to_string_lossy().to_string();
|
||||||
println!("[cmd配置save] 开始验证配置文件: {}", file_path_str);
|
println!("[cmd配置save] 开始验证配置文件: {}, 是否为merge文件: {}", file_path_str, is_merge_file);
|
||||||
|
|
||||||
// 验证配置文件
|
// 对于 merge 文件,只进行语法验证,不进行后续内核验证
|
||||||
match CoreManager::global().validate_config_file(&file_path_str).await {
|
if is_merge_file {
|
||||||
|
println!("[cmd配置save] 检测到merge文件,只进行语法验证");
|
||||||
|
match CoreManager::global().validate_config_file(&file_path_str, Some(true)).await {
|
||||||
|
Ok((true, _)) => {
|
||||||
|
println!("[cmd配置save] merge文件语法验证通过");
|
||||||
|
// 成功后尝试更新整体配置
|
||||||
|
if let Err(e) = CoreManager::global().update_config().await {
|
||||||
|
println!("[cmd配置save] 更新整体配置时发生错误: {}", e);
|
||||||
|
log::warn!(target: "app", "更新整体配置时发生错误: {}", e);
|
||||||
|
}
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Ok((false, error_msg)) => {
|
||||||
|
println!("[cmd配置save] merge文件语法验证失败: {}", error_msg);
|
||||||
|
// 恢复原始配置文件
|
||||||
|
wrap_err!(fs::write(&file_path, original_content))?;
|
||||||
|
// 发送合并文件专用错误通知
|
||||||
|
let result = (false, error_msg.clone());
|
||||||
|
handle_yaml_validation_notice(&result, "合并配置文件");
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
Err(e) => {
|
||||||
|
println!("[cmd配置save] 验证过程发生错误: {}", e);
|
||||||
|
// 恢复原始配置文件
|
||||||
|
wrap_err!(fs::write(&file_path, original_content))?;
|
||||||
|
return Err(e.to_string());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 非merge文件使用完整验证流程
|
||||||
|
match CoreManager::global().validate_config_file(&file_path_str, None).await {
|
||||||
Ok((true, _)) => {
|
Ok((true, _)) => {
|
||||||
println!("[cmd配置save] 验证成功");
|
println!("[cmd配置save] 验证成功");
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -214,18 +247,27 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
|
|||||||
// 恢复原始配置文件
|
// 恢复原始配置文件
|
||||||
wrap_err!(fs::write(&file_path, original_content))?;
|
wrap_err!(fs::write(&file_path, original_content))?;
|
||||||
|
|
||||||
// 智能判断是否为脚本错误
|
// 智能判断错误类型
|
||||||
let is_script_error = file_path_str.ends_with(".js") ||
|
let is_script_error = file_path_str.ends_with(".js") ||
|
||||||
error_msg.contains("Script syntax error") ||
|
error_msg.contains("Script syntax error") ||
|
||||||
error_msg.contains("Script must contain a main function") ||
|
error_msg.contains("Script must contain a main function") ||
|
||||||
error_msg.contains("Failed to read script file");
|
error_msg.contains("Failed to read script file");
|
||||||
|
|
||||||
if is_script_error {
|
if error_msg.contains("YAML syntax error") ||
|
||||||
|
error_msg.contains("Failed to read file:") ||
|
||||||
|
(!file_path_str.ends_with(".js") && !is_script_error) {
|
||||||
|
// 普通YAML错误使用YAML通知处理
|
||||||
|
println!("[cmd配置save] YAML配置文件验证失败,发送通知");
|
||||||
|
let result = (false, error_msg.clone());
|
||||||
|
handle_yaml_validation_notice(&result, "YAML配置文件");
|
||||||
|
} else if is_script_error {
|
||||||
// 脚本错误使用专门的通知处理
|
// 脚本错误使用专门的通知处理
|
||||||
|
println!("[cmd配置save] 脚本文件验证失败,发送通知");
|
||||||
let result = (false, error_msg.clone());
|
let result = (false, error_msg.clone());
|
||||||
handle_script_validation_notice(&result, "脚本文件");
|
handle_script_validation_notice(&result, "脚本文件");
|
||||||
} else {
|
} else {
|
||||||
// 普通配置错误使用一般通知
|
// 普通配置错误使用一般通知
|
||||||
|
println!("[cmd配置save] 其他类型验证失败,发送一般通知");
|
||||||
handle::Handle::notice_message("config_validate::error", &error_msg);
|
handle::Handle::notice_message("config_validate::error", &error_msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -292,7 +334,7 @@ pub fn get_verge_config() -> CmdResult<IVergeResponse> {
|
|||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
pub async fn patch_verge_config(payload: IVerge) -> CmdResult {
|
pub async fn patch_verge_config(payload: IVerge) -> CmdResult {
|
||||||
wrap_err!(feat::patch_verge(payload).await)
|
wrap_err!(feat::patch_verge(payload, false).await)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
@@ -593,7 +635,7 @@ pub fn handle_script_validation_notice(result: &(bool, String), file_type: &str)
|
|||||||
pub async fn validate_script_file(file_path: String) -> CmdResult<bool> {
|
pub async fn validate_script_file(file_path: String) -> CmdResult<bool> {
|
||||||
log::info!(target: "app", "验证脚本文件: {}", file_path);
|
log::info!(target: "app", "验证脚本文件: {}", file_path);
|
||||||
|
|
||||||
match CoreManager::global().validate_config_file(&file_path).await {
|
match CoreManager::global().validate_config_file(&file_path, None).await {
|
||||||
Ok(result) => {
|
Ok(result) => {
|
||||||
handle_script_validation_notice(&result, "脚本文件");
|
handle_script_validation_notice(&result, "脚本文件");
|
||||||
Ok(result.0) // 返回验证结果布尔值
|
Ok(result.0) // 返回验证结果布尔值
|
||||||
@@ -606,3 +648,51 @@ pub async fn validate_script_file(file_path: String) -> CmdResult<bool> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 处理YAML验证相关的所有消息通知
|
||||||
|
/// 统一通知接口,保持消息类型一致性
|
||||||
|
pub fn handle_yaml_validation_notice(result: &(bool, String), file_type: &str) {
|
||||||
|
if !result.0 {
|
||||||
|
let error_msg = &result.1;
|
||||||
|
println!("[通知] 处理{}验证错误: {}", file_type, error_msg);
|
||||||
|
|
||||||
|
// 检查是否为merge文件
|
||||||
|
let is_merge_file = file_type.contains("合并");
|
||||||
|
|
||||||
|
// 根据错误消息内容判断错误类型
|
||||||
|
let status = if error_msg.starts_with("File not found:") {
|
||||||
|
"config_validate::file_not_found"
|
||||||
|
} else if error_msg.starts_with("Failed to read file:") {
|
||||||
|
"config_validate::yaml_read_error"
|
||||||
|
} else if error_msg.starts_with("YAML syntax error:") {
|
||||||
|
if is_merge_file {
|
||||||
|
"config_validate::merge_syntax_error"
|
||||||
|
} else {
|
||||||
|
"config_validate::yaml_syntax_error"
|
||||||
|
}
|
||||||
|
} else if error_msg.contains("mapping values are not allowed") {
|
||||||
|
if is_merge_file {
|
||||||
|
"config_validate::merge_mapping_error"
|
||||||
|
} else {
|
||||||
|
"config_validate::yaml_mapping_error"
|
||||||
|
}
|
||||||
|
} else if error_msg.contains("did not find expected key") {
|
||||||
|
if is_merge_file {
|
||||||
|
"config_validate::merge_key_error"
|
||||||
|
} else {
|
||||||
|
"config_validate::yaml_key_error"
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// 如果是其他类型错误,根据文件类型作为一般错误处理
|
||||||
|
if is_merge_file {
|
||||||
|
"config_validate::merge_error"
|
||||||
|
} else {
|
||||||
|
"config_validate::yaml_error"
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
log::warn!(target: "app", "{} 验证失败: {}", file_type, error_msg);
|
||||||
|
println!("[通知] 发送通知: status={}, msg={}", status, error_msg);
|
||||||
|
handle::Handle::notice_message(status, error_msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -240,7 +240,7 @@ impl CoreManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// 验证指定的配置文件
|
/// 验证指定的配置文件
|
||||||
pub async fn validate_config_file(&self, config_path: &str) -> Result<(bool, String)> {
|
pub async fn validate_config_file(&self, config_path: &str, is_merge_file: Option<bool>) -> Result<(bool, String)> {
|
||||||
// 检查文件是否存在
|
// 检查文件是否存在
|
||||||
if !std::path::Path::new(config_path).exists() {
|
if !std::path::Path::new(config_path).exists() {
|
||||||
let error_msg = format!("File not found: {}", config_path);
|
let error_msg = format!("File not found: {}", config_path);
|
||||||
@@ -248,6 +248,12 @@ impl CoreManager {
|
|||||||
return Ok((false, error_msg));
|
return Ok((false, error_msg));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 如果是合并文件且不是强制验证,执行语法检查但不进行完整验证
|
||||||
|
if is_merge_file.unwrap_or(false) {
|
||||||
|
println!("[core配置验证] 检测到Merge文件,仅进行语法检查: {}", config_path);
|
||||||
|
return self.validate_file_syntax(config_path).await;
|
||||||
|
}
|
||||||
|
|
||||||
// 检查是否为脚本文件
|
// 检查是否为脚本文件
|
||||||
let is_script = if config_path.ends_with(".js") {
|
let is_script = if config_path.ends_with(".js") {
|
||||||
true
|
true
|
||||||
@@ -345,7 +351,8 @@ impl CoreManager {
|
|||||||
Ok(content) => content,
|
Ok(content) => content,
|
||||||
Err(err) => {
|
Err(err) => {
|
||||||
let error_msg = format!("Failed to read script file: {}", err);
|
let error_msg = format!("Failed to read script file: {}", err);
|
||||||
//handle::Handle::notice_message("config_validate::script_error", &error_msg);
|
log::warn!(target: "app", "脚本语法错误: {}", err);
|
||||||
|
//handle::Handle::notice_message("config_validate::script_syntax_error", &error_msg);
|
||||||
return Ok((false, error_msg));
|
return Ok((false, error_msg));
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -440,4 +447,34 @@ impl CoreManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 只进行文件语法检查,不进行完整验证
|
||||||
|
async fn validate_file_syntax(&self, config_path: &str) -> Result<(bool, String)> {
|
||||||
|
println!("[core配置语法检查] 开始检查文件: {}", config_path);
|
||||||
|
|
||||||
|
// 读取文件内容
|
||||||
|
let content = match std::fs::read_to_string(config_path) {
|
||||||
|
Ok(content) => content,
|
||||||
|
Err(err) => {
|
||||||
|
let error_msg = format!("Failed to read file: {}", err);
|
||||||
|
println!("[core配置语法检查] 无法读取文件: {}", error_msg);
|
||||||
|
return Ok((false, error_msg));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// 对YAML文件尝试解析,只检查语法正确性
|
||||||
|
println!("[core配置语法检查] 进行YAML语法检查");
|
||||||
|
match serde_yaml::from_str::<serde_yaml::Value>(&content) {
|
||||||
|
Ok(_) => {
|
||||||
|
println!("[core配置语法检查] YAML语法检查通过");
|
||||||
|
Ok((true, String::new()))
|
||||||
|
},
|
||||||
|
Err(err) => {
|
||||||
|
// 使用标准化的前缀,以便错误处理函数能正确识别
|
||||||
|
let error_msg = format!("YAML syntax error: {}", err);
|
||||||
|
println!("[core配置语法检查] YAML语法错误: {}", error_msg);
|
||||||
|
Ok((false, error_msg))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -140,7 +140,7 @@ impl Hotkey {
|
|||||||
"clash_mode_global" => || feat::change_clash_mode("global".into()),
|
"clash_mode_global" => || feat::change_clash_mode("global".into()),
|
||||||
"clash_mode_direct" => || feat::change_clash_mode("direct".into()),
|
"clash_mode_direct" => || feat::change_clash_mode("direct".into()),
|
||||||
"toggle_system_proxy" => || feat::toggle_system_proxy(),
|
"toggle_system_proxy" => || feat::toggle_system_proxy(),
|
||||||
"toggle_tun_mode" => || feat::toggle_tun_mode(),
|
"toggle_tun_mode" => || feat::toggle_tun_mode(None),
|
||||||
"quit" => || feat::quit(Some(0)),
|
"quit" => || feat::quit(Some(0)),
|
||||||
|
|
||||||
_ => {
|
_ => {
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ use tokio::time::Duration;
|
|||||||
// Windows only
|
// Windows only
|
||||||
|
|
||||||
const SERVICE_URL: &str = "http://127.0.0.1:33211";
|
const SERVICE_URL: &str = "http://127.0.0.1:33211";
|
||||||
|
const REQUIRED_SERVICE_VERSION: &str = "1.0.1"; // 定义所需的服务版本号
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
pub struct ResponseBody {
|
pub struct ResponseBody {
|
||||||
@@ -19,6 +20,12 @@ pub struct ResponseBody {
|
|||||||
pub log_file: String,
|
pub log_file: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
pub struct VersionResponse {
|
||||||
|
pub service: String,
|
||||||
|
pub version: String,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Deserialize, Serialize, Clone)]
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
pub struct JsonResponse {
|
pub struct JsonResponse {
|
||||||
pub code: u64,
|
pub code: u64,
|
||||||
@@ -26,6 +33,13 @@ pub struct JsonResponse {
|
|||||||
pub data: Option<ResponseBody>,
|
pub data: Option<ResponseBody>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Deserialize, Serialize, Clone)]
|
||||||
|
pub struct VersionJsonResponse {
|
||||||
|
pub code: u64,
|
||||||
|
pub msg: String,
|
||||||
|
pub data: Option<VersionResponse>,
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "windows")]
|
#[cfg(target_os = "windows")]
|
||||||
pub async fn reinstall_service() -> Result<()> {
|
pub async fn reinstall_service() -> Result<()> {
|
||||||
log::info!(target:"app", "reinstall service");
|
log::info!(target:"app", "reinstall service");
|
||||||
@@ -177,8 +191,43 @@ pub async fn check_service() -> Result<JsonResponse> {
|
|||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// check the service version
|
||||||
|
pub async fn check_service_version() -> Result<String> {
|
||||||
|
let url = format!("{SERVICE_URL}/version");
|
||||||
|
let response = reqwest::ClientBuilder::new()
|
||||||
|
.no_proxy()
|
||||||
|
.timeout(Duration::from_secs(3))
|
||||||
|
.build()?
|
||||||
|
.get(url)
|
||||||
|
.send()
|
||||||
|
.await
|
||||||
|
.context("failed to connect to the Clash Verge Service")?
|
||||||
|
.json::<VersionJsonResponse>()
|
||||||
|
.await
|
||||||
|
.context("failed to parse the Clash Verge Service version response")?;
|
||||||
|
|
||||||
|
match response.data {
|
||||||
|
Some(data) => Ok(data.version),
|
||||||
|
None => bail!("service version not found in response"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// check if service needs to be reinstalled
|
||||||
|
pub async fn check_service_needs_reinstall() -> bool {
|
||||||
|
match check_service_version().await {
|
||||||
|
Ok(version) => version != REQUIRED_SERVICE_VERSION,
|
||||||
|
Err(_) => true, // 如果无法获取版本或服务未运行,也需要重新安装
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// start the clash by service
|
/// start the clash by service
|
||||||
pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
|
pub(super) async fn run_core_by_service(config_file: &PathBuf) -> Result<()> {
|
||||||
|
// 检查服务版本,如果不匹配则重新安装
|
||||||
|
if check_service_needs_reinstall().await {
|
||||||
|
log::info!(target: "app", "service version mismatch, reinstalling");
|
||||||
|
reinstall_service().await?;
|
||||||
|
}
|
||||||
|
|
||||||
let clash_core = { Config::verge().latest().clash_core.clone() };
|
let clash_core = { Config::verge().latest().clash_core.clone() };
|
||||||
let clash_core = clash_core.unwrap_or("verge-mihomo".into());
|
let clash_core = clash_core.unwrap_or("verge-mihomo".into());
|
||||||
|
|
||||||
|
|||||||
@@ -87,7 +87,7 @@ impl Tray {
|
|||||||
{
|
{
|
||||||
match tray_event.as_str() {
|
match tray_event.as_str() {
|
||||||
"system_proxy" => feat::toggle_system_proxy(),
|
"system_proxy" => feat::toggle_system_proxy(),
|
||||||
"tun_mode" => feat::toggle_tun_mode(),
|
"tun_mode" => feat::toggle_tun_mode(None),
|
||||||
"main_window" => resolve::create_window(),
|
"main_window" => resolve::create_window(),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@@ -102,7 +102,7 @@ impl Tray {
|
|||||||
{
|
{
|
||||||
match tray_event.as_str() {
|
match tray_event.as_str() {
|
||||||
"system_proxy" => feat::toggle_system_proxy(),
|
"system_proxy" => feat::toggle_system_proxy(),
|
||||||
"tun_mode" => feat::toggle_tun_mode(),
|
"tun_mode" => feat::toggle_tun_mode(None),
|
||||||
"main_window" => resolve::create_window(),
|
"main_window" => resolve::create_window(),
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
@@ -594,7 +594,7 @@ fn on_menu_event(_: &AppHandle, event: MenuEvent) {
|
|||||||
}
|
}
|
||||||
"open_window" => resolve::create_window(),
|
"open_window" => resolve::create_window(),
|
||||||
"system_proxy" => feat::toggle_system_proxy(),
|
"system_proxy" => feat::toggle_system_proxy(),
|
||||||
"tun_mode" => feat::toggle_tun_mode(),
|
"tun_mode" => feat::toggle_tun_mode(None),
|
||||||
"copy_env" => feat::copy_clash_env(),
|
"copy_env" => feat::copy_clash_env(),
|
||||||
"open_app_dir" => crate::log_err!(cmds::open_app_dir()),
|
"open_app_dir" => crate::log_err!(cmds::open_app_dir()),
|
||||||
"open_core_dir" => crate::log_err!(cmds::open_core_dir()),
|
"open_core_dir" => crate::log_err!(cmds::open_core_dir()),
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ pub fn toggle_system_proxy() {
|
|||||||
match patch_verge(IVerge {
|
match patch_verge(IVerge {
|
||||||
enable_system_proxy: Some(!enable),
|
enable_system_proxy: Some(!enable),
|
||||||
..IVerge::default()
|
..IVerge::default()
|
||||||
})
|
}, false)
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(_) => handle::Handle::refresh_verge(),
|
Ok(_) => handle::Handle::refresh_verge(),
|
||||||
@@ -176,7 +176,7 @@ pub fn toggle_proxy_profile(profile_index: String) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 切换tun模式
|
// 切换tun模式
|
||||||
pub fn toggle_tun_mode() {
|
pub fn toggle_tun_mode(not_save_file: Option<bool>) {
|
||||||
let enable = Config::verge().data().enable_tun_mode;
|
let enable = Config::verge().data().enable_tun_mode;
|
||||||
let enable = enable.unwrap_or(false);
|
let enable = enable.unwrap_or(false);
|
||||||
|
|
||||||
@@ -184,7 +184,7 @@ pub fn toggle_tun_mode() {
|
|||||||
match patch_verge(IVerge {
|
match patch_verge(IVerge {
|
||||||
enable_tun_mode: Some(!enable),
|
enable_tun_mode: Some(!enable),
|
||||||
..IVerge::default()
|
..IVerge::default()
|
||||||
})
|
}, not_save_file.unwrap_or(false))
|
||||||
.await
|
.await
|
||||||
{
|
{
|
||||||
Ok(_) => handle::Handle::refresh_verge(),
|
Ok(_) => handle::Handle::refresh_verge(),
|
||||||
@@ -196,6 +196,7 @@ pub fn toggle_tun_mode() {
|
|||||||
pub fn quit(code: Option<i32>) {
|
pub fn quit(code: Option<i32>) {
|
||||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
let app_handle = handle::Handle::global().app_handle().unwrap();
|
||||||
handle::Handle::global().set_is_exiting();
|
handle::Handle::global().set_is_exiting();
|
||||||
|
toggle_tun_mode(Some(true));
|
||||||
resolve::resolve_reset();
|
resolve::resolve_reset();
|
||||||
log_err!(handle::Handle::global().get_window().unwrap().close());
|
log_err!(handle::Handle::global().get_window().unwrap().close());
|
||||||
app_handle.exit(code.unwrap_or(0));
|
app_handle.exit(code.unwrap_or(0));
|
||||||
@@ -236,7 +237,7 @@ pub async fn patch_clash(patch: Mapping) -> Result<()> {
|
|||||||
|
|
||||||
/// 修改verge的订阅
|
/// 修改verge的订阅
|
||||||
/// 一般都是一个个的修改
|
/// 一般都是一个个的修改
|
||||||
pub async fn patch_verge(patch: IVerge) -> Result<()> {
|
pub async fn patch_verge(patch: IVerge, not_save_file: bool) -> Result<()> {
|
||||||
Config::verge().draft().patch_config(patch.clone());
|
Config::verge().draft().patch_config(patch.clone());
|
||||||
|
|
||||||
let tun_mode = patch.enable_tun_mode;
|
let tun_mode = patch.enable_tun_mode;
|
||||||
@@ -397,7 +398,9 @@ pub async fn patch_verge(patch: IVerge) -> Result<()> {
|
|||||||
match res {
|
match res {
|
||||||
Ok(()) => {
|
Ok(()) => {
|
||||||
Config::verge().apply();
|
Config::verge().apply();
|
||||||
Config::verge().data().save_file()?;
|
if !not_save_file {
|
||||||
|
Config::verge().data().save_file()?;
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
@@ -617,7 +620,8 @@ pub async fn restore_webdav_backup(filename: String) -> Result<()> {
|
|||||||
webdav_username,
|
webdav_username,
|
||||||
webdav_password,
|
webdav_password,
|
||||||
..IVerge::default()
|
..IVerge::default()
|
||||||
})
|
},
|
||||||
|
false)
|
||||||
.await
|
.await
|
||||||
);
|
);
|
||||||
// 最后删除临时文件
|
// 最后删除临时文件
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
"copyright": "GNU General Public License v3.0",
|
"copyright": "GNU General Public License v3.0",
|
||||||
"category": "DeveloperTool",
|
"category": "DeveloperTool",
|
||||||
"shortDescription": "Clash Verge Rev",
|
"shortDescription": "Clash Verge Rev",
|
||||||
"createUpdaterArtifacts": "v1Compatible"
|
"createUpdaterArtifacts": true
|
||||||
},
|
},
|
||||||
"build": {
|
"build": {
|
||||||
"beforeBuildCommand": "pnpm run web:build",
|
"beforeBuildCommand": "pnpm run web:build",
|
||||||
@@ -25,24 +25,23 @@
|
|||||||
"devUrl": "http://localhost:3000/"
|
"devUrl": "http://localhost:3000/"
|
||||||
},
|
},
|
||||||
"productName": "Clash Verge",
|
"productName": "Clash Verge",
|
||||||
"version": "2.1.1",
|
"version": "2.1.2",
|
||||||
"identifier": "io.github.clash-verge-rev.clash-verge-rev",
|
"identifier": "io.github.clash-verge-rev.clash-verge-rev",
|
||||||
"plugins": {
|
"plugins": {
|
||||||
|
"updater": {
|
||||||
|
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEQyOEMyRjBCQkVGOUJEREYKUldUZnZmbStDeStNMHU5Mmo1N24xQXZwSVRYbXA2NUpzZE5oVzlqeS9Bc0t6RVV4MmtwVjBZaHgK",
|
||||||
|
"endpoints": [
|
||||||
|
"https://download.clashverge.dev/https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update-proxy.json",
|
||||||
|
"https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update.json"
|
||||||
|
],
|
||||||
|
"windows": {
|
||||||
|
"installMode": "basicUi"
|
||||||
|
}
|
||||||
|
},
|
||||||
"deep-link": {
|
"deep-link": {
|
||||||
"desktop": {
|
"desktop": {
|
||||||
"schemes": ["clash", "clash-verge"]
|
"schemes": ["clash", "clash-verge"]
|
||||||
}
|
}
|
||||||
},
|
|
||||||
"updater": {
|
|
||||||
"active": true,
|
|
||||||
"dialog": true,
|
|
||||||
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEQyOEMyRjBCQkVGOUJEREYKUldUZnZmbStDeStNMHU5Mmo1N24xQXZwSVRYbXA2NUpzZE5oVzlqeS9Bc0t6RVV4MmtwVjBZaHgK",
|
|
||||||
"endpoints": [
|
|
||||||
"https://download.clashverge.dev/https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update-proxy.json",
|
|
||||||
"https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update.json",
|
|
||||||
"https://download.clashverge.dev/https://github.com/clash-verge-rev/clash-verge-rev/releases/download/alpha/update-alpha-proxy.json",
|
|
||||||
"https://github.com/clash-verge-rev/clash-verge-rev/releases/download/alpha/update-alpha.json"
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"app": {
|
"app": {
|
||||||
|
|||||||
@@ -111,7 +111,7 @@
|
|||||||
position: absolute;
|
position: absolute;
|
||||||
top: 0;
|
top: 0;
|
||||||
left: 0;
|
left: 0;
|
||||||
right: 0px;
|
right: 1px;
|
||||||
bottom: 0px;
|
bottom: 0px;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,8 +51,11 @@ const InnerConnectionDetail = ({ data, onClose }: InnerProps) => {
|
|||||||
const chains = [...data.chains].reverse().join(" / ");
|
const chains = [...data.chains].reverse().join(" / ");
|
||||||
const rule = rulePayload ? `${data.rule}(${rulePayload})` : data.rule;
|
const rule = rulePayload ? `${data.rule}(${rulePayload})` : data.rule;
|
||||||
const host = metadata.host
|
const host = metadata.host
|
||||||
? `${metadata.host}:${metadata.remoteDestination}`
|
? `${metadata.host}:${metadata.destinationPort}`
|
||||||
: `${metadata.remoteDestination}:${metadata.destinationPort}`;
|
: `${metadata.remoteDestination}:${metadata.destinationPort}`;
|
||||||
|
const Destination = metadata.destinationIP
|
||||||
|
? metadata.destinationIP
|
||||||
|
: metadata.remoteDestination;
|
||||||
|
|
||||||
const information = [
|
const information = [
|
||||||
{ label: t("Host"), value: host },
|
{ label: t("Host"), value: host },
|
||||||
@@ -79,7 +82,7 @@ const InnerConnectionDetail = ({ data, onClose }: InnerProps) => {
|
|||||||
label: t("Source"),
|
label: t("Source"),
|
||||||
value: `${metadata.sourceIP}:${metadata.sourcePort}`,
|
value: `${metadata.sourceIP}:${metadata.sourcePort}`,
|
||||||
},
|
},
|
||||||
{ label: t("Destination"), value: metadata.remoteDestination },
|
{ label: t("Destination"), value: Destination },
|
||||||
{ label: t("DestinationPort"), value: `${metadata.destinationPort}` },
|
{ label: t("DestinationPort"), value: `${metadata.destinationPort}` },
|
||||||
{ label: t("Type"), value: `${metadata.type}(${metadata.network})` },
|
{ label: t("Type"), value: `${metadata.type}(${metadata.network})` },
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -138,6 +138,9 @@ export const ConnectionTable = (props: Props) => {
|
|||||||
const { metadata, rulePayload } = each;
|
const { metadata, rulePayload } = each;
|
||||||
const chains = [...each.chains].reverse().join(" / ");
|
const chains = [...each.chains].reverse().join(" / ");
|
||||||
const rule = rulePayload ? `${each.rule}(${rulePayload})` : each.rule;
|
const rule = rulePayload ? `${each.rule}(${rulePayload})` : each.rule;
|
||||||
|
const Destination = metadata.destinationIP
|
||||||
|
? `${metadata.destinationIP}:${metadata.destinationPort}`
|
||||||
|
: `${metadata.remoteDestination}:${metadata.destinationPort}`;
|
||||||
return {
|
return {
|
||||||
id: each.id,
|
id: each.id,
|
||||||
host: metadata.host
|
host: metadata.host
|
||||||
@@ -152,7 +155,7 @@ export const ConnectionTable = (props: Props) => {
|
|||||||
process: truncateStr(metadata.process || metadata.processPath),
|
process: truncateStr(metadata.process || metadata.processPath),
|
||||||
time: each.start,
|
time: each.start,
|
||||||
source: `${metadata.sourceIP}:${metadata.sourcePort}`,
|
source: `${metadata.sourceIP}:${metadata.sourcePort}`,
|
||||||
remoteDestination: `${metadata.remoteDestination}:${metadata.destinationPort}`,
|
remoteDestination: Destination,
|
||||||
type: `${metadata.type}(${metadata.network})`,
|
type: `${metadata.type}(${metadata.network})`,
|
||||||
connectionData: each,
|
connectionData: each,
|
||||||
};
|
};
|
||||||
|
|||||||
121
src/components/setting/setting-verge-advanced.tsx
Normal file
121
src/components/setting/setting-verge-advanced.tsx
Normal file
@@ -0,0 +1,121 @@
|
|||||||
|
import { useRef } from "react";
|
||||||
|
import { useTranslation } from "react-i18next";
|
||||||
|
import { Typography } from "@mui/material";
|
||||||
|
import {
|
||||||
|
exitApp,
|
||||||
|
openAppDir,
|
||||||
|
openCoreDir,
|
||||||
|
openLogsDir,
|
||||||
|
openDevTools,
|
||||||
|
} from "@/services/cmds";
|
||||||
|
import { check as checkUpdate } from "@tauri-apps/plugin-updater";
|
||||||
|
import { useVerge } from "@/hooks/use-verge";
|
||||||
|
import { version } from "@root/package.json";
|
||||||
|
import { DialogRef, Notice } from "@/components/base";
|
||||||
|
import { SettingList, SettingItem } from "./mods/setting-comp";
|
||||||
|
import { ConfigViewer } from "./mods/config-viewer";
|
||||||
|
import { HotkeyViewer } from "./mods/hotkey-viewer";
|
||||||
|
import { MiscViewer } from "./mods/misc-viewer";
|
||||||
|
import { ThemeViewer } from "./mods/theme-viewer";
|
||||||
|
import { LayoutViewer } from "./mods/layout-viewer";
|
||||||
|
import { UpdateViewer } from "./mods/update-viewer";
|
||||||
|
import { BackupViewer } from "./mods/backup-viewer";
|
||||||
|
import { TooltipIcon } from "@/components/base/base-tooltip-icon";
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
onError?: (err: Error) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const SettingVergeAdvanced = ({ onError }: Props) => {
|
||||||
|
const { t } = useTranslation();
|
||||||
|
|
||||||
|
const { verge, patchVerge, mutateVerge } = useVerge();
|
||||||
|
const configRef = useRef<DialogRef>(null);
|
||||||
|
const hotkeyRef = useRef<DialogRef>(null);
|
||||||
|
const miscRef = useRef<DialogRef>(null);
|
||||||
|
const themeRef = useRef<DialogRef>(null);
|
||||||
|
const layoutRef = useRef<DialogRef>(null);
|
||||||
|
const updateRef = useRef<DialogRef>(null);
|
||||||
|
const backupRef = useRef<DialogRef>(null);
|
||||||
|
|
||||||
|
const onCheckUpdate = async () => {
|
||||||
|
try {
|
||||||
|
const info = await checkUpdate();
|
||||||
|
if (!info?.available) {
|
||||||
|
Notice.success(t("Currently on the Latest Version"));
|
||||||
|
} else {
|
||||||
|
updateRef.current?.open();
|
||||||
|
}
|
||||||
|
} catch (err: any) {
|
||||||
|
Notice.error(err.message || err.toString());
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<SettingList title={t("Verge Advanced Setting")}>
|
||||||
|
<ThemeViewer ref={themeRef} />
|
||||||
|
<ConfigViewer ref={configRef} />
|
||||||
|
<HotkeyViewer ref={hotkeyRef} />
|
||||||
|
<MiscViewer ref={miscRef} />
|
||||||
|
<LayoutViewer ref={layoutRef} />
|
||||||
|
<UpdateViewer ref={updateRef} />
|
||||||
|
<BackupViewer ref={backupRef} />
|
||||||
|
|
||||||
|
<SettingItem
|
||||||
|
onClick={() => backupRef.current?.open()}
|
||||||
|
label={t("Backup Setting")}
|
||||||
|
extra={
|
||||||
|
<TooltipIcon
|
||||||
|
title={t("Backup Setting Info")}
|
||||||
|
sx={{ opacity: "0.7" }}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SettingItem
|
||||||
|
onClick={() => configRef.current?.open()}
|
||||||
|
label={t("Runtime Config")}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SettingItem
|
||||||
|
onClick={openAppDir}
|
||||||
|
label={t("Open Conf Dir")}
|
||||||
|
extra={
|
||||||
|
<TooltipIcon
|
||||||
|
title={t("Open Conf Dir Info")}
|
||||||
|
sx={{ opacity: "0.7" }}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SettingItem onClick={openCoreDir} label={t("Open Core Dir")} />
|
||||||
|
|
||||||
|
<SettingItem onClick={openLogsDir} label={t("Open Logs Dir")} />
|
||||||
|
|
||||||
|
<SettingItem onClick={onCheckUpdate} label={t("Check for Updates")} />
|
||||||
|
|
||||||
|
<SettingItem onClick={openDevTools} label={t("Open Dev Tools")} />
|
||||||
|
|
||||||
|
<SettingItem
|
||||||
|
label={t("Lite Mode")}
|
||||||
|
extra={
|
||||||
|
<TooltipIcon title={t("Lite Mode Info")} sx={{ opacity: "0.7" }} />
|
||||||
|
}
|
||||||
|
onClick={() => patchVerge({ enable_lite_mode: true })}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SettingItem
|
||||||
|
onClick={() => {
|
||||||
|
exitApp();
|
||||||
|
}}
|
||||||
|
label={t("Exit")}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<SettingItem label={t("Verge Version")}>
|
||||||
|
<Typography sx={{ py: "7px", pr: 1 }}>v{version}</Typography>
|
||||||
|
</SettingItem>
|
||||||
|
</SettingList>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SettingVergeAdvanced;
|
||||||
@@ -1,18 +1,9 @@
|
|||||||
import { useCallback, useRef } from "react";
|
import { useCallback, useRef } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { open } from "@tauri-apps/plugin-dialog";
|
import { open } from "@tauri-apps/plugin-dialog";
|
||||||
import { Button, MenuItem, Select, Input, Typography } from "@mui/material";
|
import { Button, MenuItem, Select, Input } from "@mui/material";
|
||||||
import {
|
import { copyClashEnv } from "@/services/cmds";
|
||||||
exitApp,
|
|
||||||
openAppDir,
|
|
||||||
openCoreDir,
|
|
||||||
openLogsDir,
|
|
||||||
openDevTools,
|
|
||||||
copyClashEnv,
|
|
||||||
} from "@/services/cmds";
|
|
||||||
import { check as checkUpdate } from "@tauri-apps/plugin-updater";
|
|
||||||
import { useVerge } from "@/hooks/use-verge";
|
import { useVerge } from "@/hooks/use-verge";
|
||||||
import { version } from "@root/package.json";
|
|
||||||
import { DialogRef, Notice } from "@/components/base";
|
import { DialogRef, Notice } from "@/components/base";
|
||||||
import { SettingList, SettingItem } from "./mods/setting-comp";
|
import { SettingList, SettingItem } from "./mods/setting-comp";
|
||||||
import { ThemeModeSwitch } from "./mods/theme-mode-switch";
|
import { ThemeModeSwitch } from "./mods/theme-mode-switch";
|
||||||
@@ -49,7 +40,7 @@ const languageOptions = Object.entries(languages).map(([code, _]) => {
|
|||||||
return { code, label: labels[code] };
|
return { code, label: labels[code] };
|
||||||
});
|
});
|
||||||
|
|
||||||
const SettingVerge = ({ onError }: Props) => {
|
const SettingVergeBasic = ({ onError }: Props) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
|
|
||||||
const { verge, patchVerge, mutateVerge } = useVerge();
|
const { verge, patchVerge, mutateVerge } = useVerge();
|
||||||
@@ -60,7 +51,6 @@ const SettingVerge = ({ onError }: Props) => {
|
|||||||
env_type,
|
env_type,
|
||||||
startup_script,
|
startup_script,
|
||||||
start_page,
|
start_page,
|
||||||
enable_lite_mode,
|
|
||||||
} = verge ?? {};
|
} = verge ?? {};
|
||||||
const configRef = useRef<DialogRef>(null);
|
const configRef = useRef<DialogRef>(null);
|
||||||
const hotkeyRef = useRef<DialogRef>(null);
|
const hotkeyRef = useRef<DialogRef>(null);
|
||||||
@@ -74,26 +64,13 @@ const SettingVerge = ({ onError }: Props) => {
|
|||||||
mutateVerge({ ...verge, ...patch }, false);
|
mutateVerge({ ...verge, ...patch }, false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const onCheckUpdate = async () => {
|
|
||||||
try {
|
|
||||||
const info = await checkUpdate();
|
|
||||||
if (!info?.available) {
|
|
||||||
Notice.success(t("Currently on the Latest Version"));
|
|
||||||
} else {
|
|
||||||
updateRef.current?.open();
|
|
||||||
}
|
|
||||||
} catch (err: any) {
|
|
||||||
Notice.error(err.message || err.toString());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const onCopyClashEnv = useCallback(async () => {
|
const onCopyClashEnv = useCallback(async () => {
|
||||||
await copyClashEnv();
|
await copyClashEnv();
|
||||||
Notice.success(t("Copy Success"), 1000);
|
Notice.success(t("Copy Success"), 1000);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingList title={t("Verge Setting")}>
|
<SettingList title={t("Verge Basic Setting")}>
|
||||||
<ThemeViewer ref={themeRef} />
|
<ThemeViewer ref={themeRef} />
|
||||||
<ConfigViewer ref={configRef} />
|
<ConfigViewer ref={configRef} />
|
||||||
<HotkeyViewer ref={hotkeyRef} />
|
<HotkeyViewer ref={hotkeyRef} />
|
||||||
@@ -261,62 +238,8 @@ const SettingVerge = ({ onError }: Props) => {
|
|||||||
onClick={() => hotkeyRef.current?.open()}
|
onClick={() => hotkeyRef.current?.open()}
|
||||||
label={t("Hotkey Setting")}
|
label={t("Hotkey Setting")}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<SettingItem
|
|
||||||
onClick={() => backupRef.current?.open()}
|
|
||||||
label={t("Backup Setting")}
|
|
||||||
extra={
|
|
||||||
<TooltipIcon
|
|
||||||
title={t("Backup Setting Info")}
|
|
||||||
sx={{ opacity: "0.7" }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SettingItem
|
|
||||||
onClick={() => configRef.current?.open()}
|
|
||||||
label={t("Runtime Config")}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SettingItem
|
|
||||||
onClick={openAppDir}
|
|
||||||
label={t("Open Conf Dir")}
|
|
||||||
extra={
|
|
||||||
<TooltipIcon
|
|
||||||
title={t("Open Conf Dir Info")}
|
|
||||||
sx={{ opacity: "0.7" }}
|
|
||||||
/>
|
|
||||||
}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SettingItem onClick={openCoreDir} label={t("Open Core Dir")} />
|
|
||||||
|
|
||||||
<SettingItem onClick={openLogsDir} label={t("Open Logs Dir")} />
|
|
||||||
|
|
||||||
<SettingItem onClick={onCheckUpdate} label={t("Check for Updates")} />
|
|
||||||
|
|
||||||
<SettingItem onClick={openDevTools} label={t("Open Dev Tools")} />
|
|
||||||
|
|
||||||
<SettingItem
|
|
||||||
label={t("Lite Mode")}
|
|
||||||
extra={
|
|
||||||
<TooltipIcon title={t("Lite Mode Info")} sx={{ opacity: "0.7" }} />
|
|
||||||
}
|
|
||||||
onClick={() => patchVerge({ enable_lite_mode: true })}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SettingItem
|
|
||||||
onClick={() => {
|
|
||||||
exitApp();
|
|
||||||
}}
|
|
||||||
label={t("Exit")}
|
|
||||||
/>
|
|
||||||
|
|
||||||
<SettingItem label={t("Verge Version")}>
|
|
||||||
<Typography sx={{ py: "7px", pr: 1 }}>v{version}</Typography>
|
|
||||||
</SettingItem>
|
|
||||||
</SettingList>
|
</SettingList>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SettingVerge;
|
export default SettingVergeBasic;
|
||||||
@@ -447,5 +447,7 @@
|
|||||||
"File Not Found": "الملف غير موجود، تم التراجع عن التغييرات",
|
"File Not Found": "الملف غير موجود، تم التراجع عن التغييرات",
|
||||||
"Script File Error": "خطأ في ملف السكريبت، تم التراجع عن التغييرات",
|
"Script File Error": "خطأ في ملف السكريبت، تم التراجع عن التغييرات",
|
||||||
"Core Changed Successfully": "تم تغيير النواة بنجاح",
|
"Core Changed Successfully": "تم تغيير النواة بنجاح",
|
||||||
"Failed to Change Core": "فشل تغيير النواة"
|
"Failed to Change Core": "فشل تغيير النواة",
|
||||||
|
"Verge Basic Setting": "الإعدادات الأساسية Verge",
|
||||||
|
"Verge Advanced Setting": "الإعدادات الأساسية Verge"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -277,7 +277,8 @@
|
|||||||
"Open UWP tool": "Open UWP tool",
|
"Open UWP tool": "Open UWP tool",
|
||||||
"Open UWP tool Info": "Since Windows 8, UWP apps (such as Microsoft Store) are restricted from directly accessing local host network services, and this tool can be used to bypass this restriction",
|
"Open UWP tool Info": "Since Windows 8, UWP apps (such as Microsoft Store) are restricted from directly accessing local host network services, and this tool can be used to bypass this restriction",
|
||||||
"Update GeoData": "Update GeoData",
|
"Update GeoData": "Update GeoData",
|
||||||
"Verge Setting": "Verge Setting",
|
"Verge Basic Setting": "Verge Basic Setting",
|
||||||
|
"Verge Advanced Setting": "Verge Advanced Setting",
|
||||||
"Language": "Language",
|
"Language": "Language",
|
||||||
"Theme Mode": "Theme Mode",
|
"Theme Mode": "Theme Mode",
|
||||||
"theme.light": "Light",
|
"theme.light": "Light",
|
||||||
@@ -451,5 +452,18 @@
|
|||||||
"File Not Found": "File missing, changes reverted",
|
"File Not Found": "File missing, changes reverted",
|
||||||
"Script File Error": "Script file error, changes reverted",
|
"Script File Error": "Script file error, changes reverted",
|
||||||
"Core Changed Successfully": "Core changed successfully",
|
"Core Changed Successfully": "Core changed successfully",
|
||||||
"Failed to Change Core": "Failed to change core"
|
"Failed to Change Core": "Failed to change core",
|
||||||
|
"YAML Syntax Error": "YAML syntax error, changes reverted",
|
||||||
|
"YAML Read Error": "YAML read error, changes reverted",
|
||||||
|
"YAML Mapping Error": "YAML mapping error, changes reverted",
|
||||||
|
"YAML Key Error": "YAML key error, changes reverted",
|
||||||
|
"YAML Error": "YAML error, changes reverted",
|
||||||
|
"Merge File Syntax Error": "Merge file syntax error, changes reverted",
|
||||||
|
"Merge File Mapping Error": "Merge file mapping error, changes reverted",
|
||||||
|
"Merge File Key Error": "Merge file key error, changes reverted",
|
||||||
|
"Merge File Error": "Merge file error, changes reverted",
|
||||||
|
"Validate YAML File": "Validate YAML File",
|
||||||
|
"Validate Merge File": "Validate Merge File",
|
||||||
|
"Validation Success": "Validation Success",
|
||||||
|
"Validation Failed": "Validation Failed"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -444,5 +444,7 @@
|
|||||||
"File Not Found": "فایل یافت نشد، تغییرات برگشت داده شد",
|
"File Not Found": "فایل یافت نشد، تغییرات برگشت داده شد",
|
||||||
"Script File Error": "خطای فایل اسکریپت، تغییرات برگشت داده شد",
|
"Script File Error": "خطای فایل اسکریپت، تغییرات برگشت داده شد",
|
||||||
"Core Changed Successfully": "هسته با موفقیت تغییر کرد",
|
"Core Changed Successfully": "هسته با موفقیت تغییر کرد",
|
||||||
"Failed to Change Core": "تغییر هسته ناموفق بود"
|
"Failed to Change Core": "تغییر هسته ناموفق بود",
|
||||||
|
"Verge Basic Setting": "تنظیمات پایه Verge",
|
||||||
|
"Verge Advanced Setting": "تنظیمات پیشرفته Verge"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -443,5 +443,7 @@
|
|||||||
"File Not Found": "File tidak ditemukan, perubahan dibatalkan",
|
"File Not Found": "File tidak ditemukan, perubahan dibatalkan",
|
||||||
"Script File Error": "Kesalahan file skrip, perubahan dibatalkan",
|
"Script File Error": "Kesalahan file skrip, perubahan dibatalkan",
|
||||||
"Core Changed Successfully": "Inti berhasil diubah",
|
"Core Changed Successfully": "Inti berhasil diubah",
|
||||||
"Failed to Change Core": "Gagal mengubah inti"
|
"Failed to Change Core": "Gagal mengubah inti",
|
||||||
|
"Verge Basic Setting": "Pengaturan Dasar Verge",
|
||||||
|
"Verge Advanced Setting": "Pengaturan Lanjutan Verge"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -444,5 +444,7 @@
|
|||||||
"File Not Found": "Файл не найден, изменения отменены",
|
"File Not Found": "Файл не найден, изменения отменены",
|
||||||
"Script File Error": "Ошибка файла скрипта, изменения отменены",
|
"Script File Error": "Ошибка файла скрипта, изменения отменены",
|
||||||
"Core Changed Successfully": "Ядро успешно сменено",
|
"Core Changed Successfully": "Ядро успешно сменено",
|
||||||
"Failed to Change Core": "Не удалось сменить ядро"
|
"Failed to Change Core": "Не удалось сменить ядро",
|
||||||
|
"Verge Basic Setting": "Основные настройки Verge",
|
||||||
|
"Verge Advanced Setting": "Расширенные настройки Verge"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -443,5 +443,7 @@
|
|||||||
"File Not Found": "Файл табылмады, үзгәрешләр кире кайтарылды",
|
"File Not Found": "Файл табылмады, үзгәрешләр кире кайтарылды",
|
||||||
"Script File Error": "Скрипт файлы хатасы, үзгәрешләр кире кайтарылды",
|
"Script File Error": "Скрипт файлы хатасы, үзгәрешләр кире кайтарылды",
|
||||||
"Core Changed Successfully": "Ядро уңышлы алыштырылды",
|
"Core Changed Successfully": "Ядро уңышлы алыштырылды",
|
||||||
"Failed to Change Core": "Ядро алыштыру уңышсыз булды"
|
"Failed to Change Core": "Ядро алыштыру уңышсыз булды",
|
||||||
|
"Verge Basic Setting": "Verge Төп көйләүләр",
|
||||||
|
"Verge Advanced Setting": "Verge Киңәйтелгән көйләүләр"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,9 +135,9 @@
|
|||||||
"Hidden": "隐藏代理组",
|
"Hidden": "隐藏代理组",
|
||||||
"Group Name Required": "代理组名称不能为空",
|
"Group Name Required": "代理组名称不能为空",
|
||||||
"Group Name Already Exists": "代理组名称已存在",
|
"Group Name Already Exists": "代理组名称已存在",
|
||||||
"Extend Config": "扩展配置",
|
"Extend Config": "扩展覆写配置",
|
||||||
"Extend Script": "扩展脚本",
|
"Extend Script": "扩展脚本",
|
||||||
"Global Merge": "全局扩展配置",
|
"Global Merge": "全局扩展覆写配置",
|
||||||
"Global Script": "全局扩展脚本",
|
"Global Script": "全局扩展脚本",
|
||||||
"Type": "类型",
|
"Type": "类型",
|
||||||
"Name": "名称",
|
"Name": "名称",
|
||||||
@@ -444,5 +444,20 @@
|
|||||||
"File Not Found": "文件丢失,变更已撤销",
|
"File Not Found": "文件丢失,变更已撤销",
|
||||||
"Script File Error": "脚本文件错误,变更已撤销",
|
"Script File Error": "脚本文件错误,变更已撤销",
|
||||||
"Core Changed Successfully": "内核切换成功",
|
"Core Changed Successfully": "内核切换成功",
|
||||||
"Failed to Change Core": "无法切换内核"
|
"Failed to Change Core": "无法切换内核",
|
||||||
|
"YAML Syntax Error": "YAML语法错误,变更已撤销",
|
||||||
|
"YAML Read Error": "YAML读取错误,变更已撤销",
|
||||||
|
"YAML Mapping Error": "YAML映射错误,变更已撤销",
|
||||||
|
"YAML Key Error": "YAML键错误,变更已撤销",
|
||||||
|
"YAML Error": "YAML错误,变更已撤销",
|
||||||
|
"Merge File Syntax Error": "覆写文件语法错误,变更已撤销",
|
||||||
|
"Merge File Mapping Error": "覆写文件映射错误,变更已撤销",
|
||||||
|
"Merge File Key Error": "覆写文件键错误,变更已撤销",
|
||||||
|
"Merge File Error": "覆写文件错误,变更已撤销",
|
||||||
|
"Validate YAML File": "验证YAML文件",
|
||||||
|
"Validate Merge File": "验证覆写文件",
|
||||||
|
"Validation Success": "验证成功",
|
||||||
|
"Validation Failed": "验证失败",
|
||||||
|
"Verge Basic Setting": "Verge 基础设置",
|
||||||
|
"Verge Advanced Setting": "Verge 高级设置"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -84,6 +84,33 @@ const handleNoticeMessage = (
|
|||||||
case "config_validate::file_not_found":
|
case "config_validate::file_not_found":
|
||||||
Notice.error(`${t("File Not Found")} ${msg}`);
|
Notice.error(`${t("File Not Found")} ${msg}`);
|
||||||
break;
|
break;
|
||||||
|
case "config_validate::yaml_syntax_error":
|
||||||
|
Notice.error(`${t("YAML Syntax Error")} ${msg}`);
|
||||||
|
break;
|
||||||
|
case "config_validate::yaml_read_error":
|
||||||
|
Notice.error(`${t("YAML Read Error")} ${msg}`);
|
||||||
|
break;
|
||||||
|
case "config_validate::yaml_mapping_error":
|
||||||
|
Notice.error(`${t("YAML Mapping Error")} ${msg}`);
|
||||||
|
break;
|
||||||
|
case "config_validate::yaml_key_error":
|
||||||
|
Notice.error(`${t("YAML Key Error")} ${msg}`);
|
||||||
|
break;
|
||||||
|
case "config_validate::yaml_error":
|
||||||
|
Notice.error(`${t("YAML Error")} ${msg}`);
|
||||||
|
break;
|
||||||
|
case "config_validate::merge_syntax_error":
|
||||||
|
Notice.error(`${t("Merge File Syntax Error")} ${msg}`);
|
||||||
|
break;
|
||||||
|
case "config_validate::merge_mapping_error":
|
||||||
|
Notice.error(`${t("Merge File Mapping Error")} ${msg}`);
|
||||||
|
break;
|
||||||
|
case "config_validate::merge_key_error":
|
||||||
|
Notice.error(`${t("Merge File Key Error")} ${msg}`);
|
||||||
|
break;
|
||||||
|
case "config_validate::merge_error":
|
||||||
|
Notice.error(`${t("Merge File Error")} ${msg}`);
|
||||||
|
break;
|
||||||
case "config_core::change_success":
|
case "config_core::change_success":
|
||||||
Notice.success(`${t("Core Changed Successfully")}: ${msg}`);
|
Notice.success(`${t("Core Changed Successfully")}: ${msg}`);
|
||||||
break;
|
break;
|
||||||
@@ -202,12 +229,14 @@ const Layout = () => {
|
|||||||
}}
|
}}
|
||||||
sx={[
|
sx={[
|
||||||
({ palette }) => ({ bgcolor: palette.background.paper }),
|
({ palette }) => ({ bgcolor: palette.background.paper }),
|
||||||
{
|
OS === "linux"
|
||||||
borderRadius: "8px",
|
? {
|
||||||
border: "0px solid var(--divider-color)",
|
borderRadius: "8px",
|
||||||
width: "calc(100vw - 1px)",
|
border: "1px solid var(--divider-color)",
|
||||||
height: "calc(100vh - 1px)",
|
width: "calc(100vw - 0px)",
|
||||||
},
|
height: "calc(100vh - 0px)",
|
||||||
|
}
|
||||||
|
: {},
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<div className="layout__left">
|
<div className="layout__left">
|
||||||
|
|||||||
@@ -11,7 +11,8 @@ import { useTranslation } from "react-i18next";
|
|||||||
import { BasePage, Notice } from "@/components/base";
|
import { BasePage, Notice } from "@/components/base";
|
||||||
import { GitHub, HelpOutlineRounded, Telegram } from "@mui/icons-material";
|
import { GitHub, HelpOutlineRounded, Telegram } from "@mui/icons-material";
|
||||||
import { openWebUrl } from "@/services/cmds";
|
import { openWebUrl } from "@/services/cmds";
|
||||||
import SettingVerge from "@/components/setting/setting-verge";
|
import SettingVergeBasic from "@/components/setting/setting-verge-basic";
|
||||||
|
import SettingVergeAdvanced from "@/components/setting/setting-verge-advanced";
|
||||||
import SettingClash from "@/components/setting/setting-clash";
|
import SettingClash from "@/components/setting/setting-clash";
|
||||||
import SettingSystem from "@/components/setting/setting-system";
|
import SettingSystem from "@/components/setting/setting-system";
|
||||||
import { useThemeMode } from "@/services/states";
|
import { useThemeMode } from "@/services/states";
|
||||||
@@ -95,10 +96,19 @@ const SettingPage = () => {
|
|||||||
<Box
|
<Box
|
||||||
sx={{
|
sx={{
|
||||||
borderRadius: 2,
|
borderRadius: 2,
|
||||||
|
marginBottom: 1.5,
|
||||||
backgroundColor: isDark ? "#282a36" : "#ffffff",
|
backgroundColor: isDark ? "#282a36" : "#ffffff",
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<SettingVerge onError={onError} />
|
<SettingVergeBasic onError={onError} />
|
||||||
|
</Box>
|
||||||
|
<Box
|
||||||
|
sx={{
|
||||||
|
borderRadius: 2,
|
||||||
|
backgroundColor: isDark ? "#282a36" : "#ffffff",
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<SettingVergeAdvanced onError={onError} />
|
||||||
</Box>
|
</Box>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
|||||||
Reference in New Issue
Block a user