Compare commits

...

13 Commits

29 changed files with 294 additions and 244 deletions

View File

@@ -41,18 +41,18 @@ A Clash Meta GUI based on <a href="https://github.com/tauri-apps/tauri">Tauri</a
Download from [release](https://github.com/clash-verge-rev/clash-verge-rev/releases). Supports Windows (x64/x86), Linux (x64/arm64) and macOS 10.15+ (intel/apple).
- [Windows x64](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.4/Clash.Verge_1.4.4_x64-setup.exe)
- [Windows x86](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.4/Clash.Verge_1.4.4_x86-setup.exe)
- [Windows arm64](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.4/Clash.Verge_1.4.4_arm64-setup.exe)
- [Windows x64](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/Clash.Verge_1.4.5_x64-setup.exe)
- [Windows x86](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/Clash.Verge_1.4.5_x86-setup.exe)
- [Windows arm64](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/Clash.Verge_1.4.5_arm64-setup.exe)
- [macOS intel](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.4/Clash.Verge_1.4.4_x64.dmg)
- [macOS apple](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.4/Clash.Verge_1.4.4_aarch64.dmg)
- [macOS intel](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/Clash.Verge_1.4.5_x64.dmg)
- [macOS apple](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/Clash.Verge_1.4.5_aarch64.dmg)
- [Linux x64 AppImage](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.4/clash-verge_1.4.4_amd64.AppImage)
- [Linux x64 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.4/clash-verge_1.4.4_amd64.deb)
- [Linux x86 AppImage](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.4/clash-verge_1.4.4_i386.AppImage)
- [Linux x86 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.4/clash-verge_1.4.4_i386.deb)
- [Linux arm64 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.4/clash-verge_1.4.4_arm64.deb)
- [Linux x64 AppImage](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/clash-verge_1.4.5_amd64.AppImage)
- [Linux x64 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/clash-verge_1.4.5_amd64.deb)
- [Linux x86 AppImage](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/clash-verge_1.4.5_i386.AppImage)
- [Linux x86 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/clash-verge_1.4.5_i386.deb)
- [Linux arm64 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.5/clash-verge_1.4.5_arm64.deb)
Or you can build it yourself. Supports Windows, Linux and macOS 10.15+
@@ -112,11 +112,10 @@ Issue and PR welcome!
Clash Verge rev was based on or inspired by these projects and so on:
- [keiko233/clash-nyanpasu](https://github.com/keiko233/clash-nyanpasu): A Clash Verge variant.
- [zzzgydi/clash-verge](https://github.com/zzzgydi/clash-verge): A Clash GUI based on tauri. Supports Windows, macOS and Linux.
- [tauri-apps/tauri](https://github.com/tauri-apps/tauri): Build smaller, faster, and more secure desktop applications with a web frontend.
- [Dreamacro/clash](https://github.com/Dreamacro/clash): A rule-based tunnel in Go.
- [MetaCubeX/Clash.Meta](https://github.com/MetaCubeX/Clash.Meta): A rule-based tunnel in Go.
- [MetaCubeX/mihomo](https://github.com/MetaCubeX/mihomo): A rule-based tunnel in Go.
- [Fndroid/clash_for_windows_pkg](https://github.com/Fndroid/clash_for_windows_pkg): A Windows/macOS GUI based on Clash.
- [vitejs/vite](https://github.com/vitejs/vite): Next generation frontend tooling. It's fast!

View File

@@ -1,3 +1,20 @@
## v1.4.5
### Features
- 更新 MacOS 托盘图标样式(@gxx2778 贡献)
### Bugs Fixes
- Windows 下更新时无法覆盖`clash-verge-service.exe`的问题(需要卸载重装一次服务,下次更新生效)
- 窗口最大化按钮变化问题
- 窗口尺寸保存错误问题
- 复制环境变量类型无法切换问题
- 某些情况下闪退的问题
- 某些订阅无法导入的问题
---
## v1.4.4
### Features

View File

@@ -1,6 +1,6 @@
{
"name": "clash-verge",
"version": "1.4.4",
"version": "1.4.5",
"license": "GPL-3.0",
"scripts": {
"dev": "tauri dev",

89
src-tauri/Cargo.lock generated
View File

@@ -559,7 +559,7 @@ dependencies = [
[[package]]
name = "clash-verge"
version = "1.4.4"
version = "1.4.5"
dependencies = [
"anyhow",
"auto-launch",
@@ -1954,6 +1954,20 @@ dependencies = [
"want",
]
[[package]]
name = "hyper-rustls"
version = "0.24.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590"
dependencies = [
"futures-util",
"http",
"hyper",
"rustls",
"tokio",
"tokio-rustls",
]
[[package]]
name = "hyper-tls"
version = "0.5.0"
@@ -3602,6 +3616,7 @@ dependencies = [
"http",
"http-body",
"hyper",
"hyper-rustls",
"hyper-tls",
"ipnet",
"js-sys",
@@ -3611,12 +3626,15 @@ dependencies = [
"once_cell",
"percent-encoding",
"pin-project-lite",
"rustls",
"rustls-pemfile",
"serde",
"serde_json",
"serde_urlencoded",
"system-configuration",
"tokio",
"tokio-native-tls",
"tokio-rustls",
"tokio-util",
"tower-service",
"url",
@@ -3624,6 +3642,7 @@ dependencies = [
"wasm-bindgen-futures",
"wasm-streams",
"web-sys",
"webpki-roots",
"winreg 0.50.0",
]
@@ -3651,6 +3670,20 @@ dependencies = [
"windows 0.37.0",
]
[[package]]
name = "ring"
version = "0.17.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "688c63d65483050968b2a8937f7995f443e27041a0f7700aa59b0822aedebb74"
dependencies = [
"cc",
"getrandom 0.2.11",
"libc",
"spin",
"untrusted",
"windows-sys 0.48.0",
]
[[package]]
name = "rquickjs"
version = "0.3.1"
@@ -3745,6 +3778,18 @@ dependencies = [
"windows-sys 0.52.0",
]
[[package]]
name = "rustls"
version = "0.21.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "629648aced5775d558af50b2b4c7b02983a04b312126d45eeead26e7caa498b9"
dependencies = [
"log 0.4.20",
"ring",
"rustls-webpki",
"sct",
]
[[package]]
name = "rustls-pemfile"
version = "1.0.4"
@@ -3754,6 +3799,16 @@ dependencies = [
"base64 0.21.5",
]
[[package]]
name = "rustls-webpki"
version = "0.101.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "rustversion"
version = "1.0.14"
@@ -3802,6 +3857,16 @@ version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]]
name = "sct"
version = "0.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414"
dependencies = [
"ring",
"untrusted",
]
[[package]]
name = "security-framework"
version = "2.9.2"
@@ -4862,6 +4927,16 @@ dependencies = [
"tokio",
]
[[package]]
name = "tokio-rustls"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081"
dependencies = [
"rustls",
"tokio",
]
[[package]]
name = "tokio-stream"
version = "0.1.14"
@@ -5147,6 +5222,12 @@ version = "0.2.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f28467d3e1d3c6586d8f25fa243f544f5800fec42d97032474e17222c2b75cfa"
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
[[package]]
name = "url"
version = "2.5.0"
@@ -5449,6 +5530,12 @@ dependencies = [
"system-deps 6.2.0",
]
[[package]]
name = "webpki-roots"
version = "0.25.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1778a42e8b3b90bff8d0f5032bf22250792889a5cdc752aa0020c84abe3aaf10"
[[package]]
name = "webview2-com"
version = "0.19.1"

View File

@@ -1,6 +1,6 @@
[package]
name = "clash-verge"
version = "1.4.4"
version = "1.4.5"
description = "clash verge"
authors = ["zzzgydi", "wonfen", "MystiPanda"]
license = "GPL-3.0"
@@ -37,7 +37,7 @@ percent-encoding = "2.3.1"
window-shadows = { version = "0.2" }
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
reqwest = { version = "0.11", features = ["json"] }
reqwest = { version = "0.11", features = ["json", "rustls-tls"] }
sysproxy = { git="https://github.com/zzzgydi/sysproxy-rs", branch = "main" }
tauri = { version = "1.5", features = ["icon-png", "clipboard-all", "global-shortcut-all", "process-all", "shell-all", "system-tray", "updater", "window-all"] }

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

View File

@@ -190,7 +190,7 @@ impl PrfItem {
let self_proxy = opt_ref.map_or(false, |o| o.self_proxy.unwrap_or(false));
let user_agent = opt_ref.map_or(None, |o| o.user_agent.clone());
let mut builder = reqwest::ClientBuilder::new().no_proxy();
let mut builder = reqwest::ClientBuilder::new().use_rustls_tls().no_proxy();
// 使用软件自己的代理
if self_proxy {
@@ -231,8 +231,7 @@ impl PrfItem {
};
}
let version = unsafe { dirs::APP_VERSION };
let version = format!("clash-verge/{version}");
let version = format!("clash-verge-rev");
builder = builder.user_agent(user_agent.unwrap_or(version));
let resp = builder.build()?.get(url).send().await?;

View File

@@ -55,7 +55,12 @@ impl IProfiles {
pub fn template() -> Self {
Self {
valid: Some(vec!["dns".into()]),
valid: Some(vec![
"dns".into(),
"sub-rules".into(),
"unified-delay".into(),
"tcp-concurrent".into(),
]),
items: Some(vec![]),
..Self::default()
}

View File

@@ -152,7 +152,7 @@ impl IVerge {
proxy_guard_duration: Some(30),
auto_close_connection: Some(true),
enable_builtin_enhanced: Some(true),
enable_clash_fields: Some(false),
enable_clash_fields: Some(true),
auto_log_clean: Some(3),
..Self::default()
}

View File

@@ -1,5 +1,3 @@
use std::borrow::Cow;
/// 给clash内核的tun模式授权
#[cfg(any(target_os = "macos", target_os = "linux"))]
pub fn grant_permission(core: String) -> anyhow::Result<()> {
@@ -13,9 +11,6 @@ pub fn grant_permission(core: String) -> anyhow::Result<()> {
#[cfg(target_os = "macos")]
let output = {
// the path of clash /Applications/Clash Verge.app/Contents/MacOS/clash
// https://apple.stackexchange.com/questions/82967/problem-with-empty-spaces-when-executing-shell-commands-in-applescript
// let path = escape(&path);
let path = path.replace(' ', "\\\\ ");
let shell = format!("chown root:admin {path}\nchmod +sx {path}");
let command = format!(r#"do shell script "{shell}" with administrator privileges"#);
@@ -50,33 +45,3 @@ pub fn grant_permission(core: String) -> anyhow::Result<()> {
anyhow::bail!("{stderr}");
}
}
#[allow(unused)]
pub fn escape<'a>(text: &'a str) -> Cow<'a, str> {
let bytes = text.as_bytes();
let mut owned = None;
for pos in 0..bytes.len() {
let special = match bytes[pos] {
b' ' => Some(b' '),
_ => None,
};
if let Some(s) = special {
if owned.is_none() {
owned = Some(bytes[0..pos].to_owned());
}
owned.as_mut().unwrap().push(b'\\');
owned.as_mut().unwrap().push(b'\\');
owned.as_mut().unwrap().push(s);
} else if let Some(owned) = owned.as_mut() {
owned.push(bytes[pos]);
}
}
if let Some(owned) = owned {
unsafe { Cow::Owned(String::from_utf8_unchecked(owned)) }
} else {
unsafe { Cow::Borrowed(std::str::from_utf8_unchecked(bytes)) }
}
}

View File

@@ -140,7 +140,7 @@ impl Tray {
#[cfg(not(target_os = "macos"))]
let icon = include_bytes!("../../icons/tray-icon.png").to_vec();
#[cfg(target_os = "macos")]
let icon = include_bytes!("../../icons/icon.png").to_vec();
let icon = include_bytes!("../../icons/mac-tray-icon.png").to_vec();
icon
};

View File

@@ -7,8 +7,8 @@ use runas::Command as RunasCommand;
use std::process::Command as StdCommand;
pub async fn invoke_uwptools() -> Result<()> {
let binary_path = dirs::service_path()?;
let tool_path = binary_path.with_file_name("enableLoopback.exe");
let resource_dir = dirs::app_resources_dir()?;
let tool_path = resource_dir.join("enableLoopback.exe");
if !tool_path.exists() {
bail!("enableLoopback exe not found");
@@ -17,10 +17,9 @@ pub async fn invoke_uwptools() -> Result<()> {
let token = Token::with_current_process()?;
let level = token.privilege_level()?;
match level {
match level {
PrivilegeLevel::NotPrivileged => RunasCommand::new(tool_path).status()?,
_ => StdCommand::new(tool_path)
.status()?,
_ => StdCommand::new(tool_path).status()?,
};
Ok(())

View File

@@ -131,6 +131,9 @@ fn main() -> std::io::Result<()> {
tauri::RunEvent::WindowEvent { label, event, .. } => {
if label == "main" {
match event {
tauri::WindowEvent::Destroyed => {
let _ = resolve::save_window_size_position(&app_handle, true);
}
tauri::WindowEvent::CloseRequested { .. } => {
let _ = resolve::save_window_size_position(&app_handle, true);
}

View File

@@ -1,8 +1,9 @@
use crate::core::handle;
use anyhow::Result;
use std::path::PathBuf;
use tauri::{
api::path::{data_dir, resource_dir},
Env, PackageInfo,
Env,
};
#[cfg(not(feature = "verge-dev"))]
@@ -14,73 +15,38 @@ static CLASH_CONFIG: &str = "config.yaml";
static VERGE_CONFIG: &str = "verge.yaml";
static PROFILE_YAML: &str = "profiles.yaml";
static mut RESOURCE_DIR: Option<PathBuf> = None;
static mut APP_HOME_DIR: Option<PathBuf> = None;
/// portable flag
#[allow(unused)]
static mut PORTABLE_FLAG: bool = false;
pub static mut APP_VERSION: &str = "v1.2.0";
/// initialize portable flag
#[cfg(target_os = "windows")]
pub unsafe fn init_portable_flag() -> Result<()> {
use tauri::utils::platform::current_exe;
let exe = current_exe()?;
if let Some(dir) = exe.parent() {
let dir = PathBuf::from(dir).join(".config/PORTABLE");
if dir.exists() {
PORTABLE_FLAG = true;
}
}
Ok(())
}
/// get the verge app home dir
pub fn app_home_dir() -> Result<PathBuf> {
use tauri::utils::platform::current_exe;
let app_exe = current_exe()?;
let app_exe = dunce::canonicalize(app_exe)?;
let app_dir = app_exe
.parent()
.ok_or(anyhow::anyhow!("failed to get the portable app dir"))?;
if let Some(dir) = app_exe.parent() {
let dir = PathBuf::from(dir).join(".config/PORTABLE");
let portable_home_dir = PathBuf::from(app_dir).join(".config").join(APP_ID);
let home_dir = data_dir()
.ok_or(anyhow::anyhow!("failed to get app home dir"))?
.join(APP_ID);
unsafe {
if PORTABLE_FLAG {
APP_HOME_DIR = Some(portable_home_dir.clone());
Ok(portable_home_dir)
} else {
APP_HOME_DIR = Some(home_dir.clone());
Ok(home_dir)
if dir.exists() {
let app_exe = dunce::canonicalize(app_exe)?;
let app_dir = app_exe
.parent()
.ok_or(anyhow::anyhow!("failed to get the portable app dir"))?;
return Ok(PathBuf::from(app_dir).join(".config").join(APP_ID));
}
}
Ok(data_dir()
.ok_or(anyhow::anyhow!("failed to get app home dir"))?
.join(APP_ID))
}
/// get the resources dir
pub fn app_resources_dir(package_info: &PackageInfo) -> Result<PathBuf> {
let res_dir = resource_dir(package_info, &Env::default())
.ok_or(anyhow::anyhow!("failed to get the resource dir"))?
.join("resources");
unsafe {
RESOURCE_DIR = Some(res_dir.clone());
let ver = package_info.version.to_string();
let ver_str = format!("v{ver}");
APP_VERSION = Box::leak(Box::new(ver_str));
}
Ok(res_dir)
pub fn app_resources_dir() -> Result<PathBuf> {
let handle = handle::Handle::global();
let app_handle = handle.app_handle.lock();
if let Some(app_handle) = app_handle.as_ref() {
let res_dir = resource_dir(app_handle.package_info(), &Env::default())
.ok_or(anyhow::anyhow!("failed to get the resource dir"))?
.join("resources");
return Ok(res_dir);
};
Err(anyhow::anyhow!("failed to get the resource dir"))
}
/// profiles dir
@@ -105,32 +71,18 @@ pub fn profiles_path() -> Result<PathBuf> {
Ok(app_home_dir()?.join(PROFILE_YAML))
}
#[allow(unused)]
pub fn app_res_dir() -> Result<PathBuf> {
unsafe {
Ok(RESOURCE_DIR
.clone()
.ok_or(anyhow::anyhow!("failed to get the resource dir"))?)
}
pub fn clash_pid_path() -> Result<PathBuf> {
Ok(app_home_dir()?.join("clash.pid"))
}
pub fn clash_pid_path() -> Result<PathBuf> {
unsafe {
Ok(APP_HOME_DIR
.clone()
.ok_or(anyhow::anyhow!("failed to get the app home dir"))?
.join("clash.pid"))
}
#[cfg(windows)]
pub fn service_dir() -> Result<PathBuf> {
Ok(app_home_dir()?.join("service"))
}
#[cfg(windows)]
pub fn service_path() -> Result<PathBuf> {
unsafe {
let res_dir = RESOURCE_DIR
.clone()
.ok_or(anyhow::anyhow!("failed to get the resource dir"))?;
Ok(res_dir.join("clash-verge-service.exe"))
}
Ok(service_dir()?.join("clash-verge-service.exe"))
}
#[cfg(windows)]

View File

@@ -9,7 +9,6 @@ use log4rs::config::{Appender, Logger, Root};
use log4rs::encode::pattern::PatternEncoder;
use std::fs::{self, DirEntry};
use std::str::FromStr;
use tauri::PackageInfo;
/// initialize this instance's log file
fn init_log() -> Result<()> {
@@ -116,7 +115,10 @@ pub fn delete_log() -> Result<()> {
if file_name.ends_with(".log") {
let now = Local::now();
let created_time = parse_time_str(&file_name[0..file_name.len() - 4])?;
let file_time = Local.from_local_datetime(&created_time).single().ok_or(anyhow::anyhow!("invalid local datetime"))?;
let file_time = Local
.from_local_datetime(&created_time)
.single()
.ok_or(anyhow::anyhow!("invalid local datetime"))?;
let duration = now.signed_duration_since(file_time);
if duration.num_days() > day {
@@ -139,11 +141,6 @@ pub fn delete_log() -> Result<()> {
/// Initialize all the config files
/// before tauri setup
pub fn init_config() -> Result<()> {
#[cfg(target_os = "windows")]
unsafe {
let _ = dirs::init_portable_flag();
}
let _ = init_log();
let _ = delete_log();
@@ -185,9 +182,9 @@ pub fn init_config() -> Result<()> {
/// initialize app resources
/// after tauri setup
pub fn init_resources(package_info: &PackageInfo) -> Result<()> {
pub fn init_resources() -> Result<()> {
let app_dir = dirs::app_home_dir()?;
let res_dir = dirs::app_resources_dir(package_info)?;
let res_dir = dirs::app_resources_dir()?;
if !app_dir.exists() {
let _ = fs::create_dir_all(&app_dir);
@@ -241,3 +238,64 @@ pub fn init_resources(package_info: &PackageInfo) -> Result<()> {
Ok(())
}
/// initialize service resources
/// after tauri setup
#[cfg(target_os = "windows")]
pub fn init_service() -> Result<()> {
let service_dir = dirs::service_dir()?;
let res_dir = dirs::app_resources_dir()?;
if !service_dir.exists() {
let _ = fs::create_dir_all(&service_dir);
}
if !res_dir.exists() {
let _ = fs::create_dir_all(&res_dir);
}
let file_list = [
"clash-verge-service.exe",
"install-service.exe",
"uninstall-service.exe",
];
// copy the resource file
// if the source file is newer than the destination file, copy it over
for file in file_list.iter() {
let src_path = res_dir.join(file);
let dest_path = service_dir.join(file);
let handle_copy = || {
match fs::copy(&src_path, &dest_path) {
Ok(_) => log::debug!(target: "app", "resources copied '{file}'"),
Err(err) => {
log::error!(target: "app", "failed to copy resources '{file}', {err}")
}
};
};
if src_path.exists() && !dest_path.exists() {
handle_copy();
continue;
}
let src_modified = fs::metadata(&src_path).and_then(|m| m.modified());
let dest_modified = fs::metadata(&dest_path).and_then(|m| m.modified());
match (src_modified, dest_modified) {
(Ok(src_modified), Ok(dest_modified)) => {
if src_modified > dest_modified {
handle_copy();
} else {
log::debug!(target: "app", "skipping resource copy '{file}'");
}
}
_ => {
log::debug!(target: "app", "failed to get modified '{file}'");
handle_copy();
}
};
}
Ok(())
}

View File

@@ -4,4 +4,3 @@ pub mod init;
pub mod resolve;
pub mod server;
pub mod tmpl;
// mod winhelp;

View File

@@ -30,8 +30,9 @@ pub fn resolve_setup(app: &mut App) {
handle::Handle::global().init(app.app_handle());
log_err!(init::init_resources(app.package_info()));
log_err!(init::init_resources());
#[cfg(target_os = "windows")]
log_err!(init::init_service());
// 处理随机端口
let enable_random_port = Config::verge().latest().enable_random_port.unwrap_or(false);
@@ -195,6 +196,13 @@ pub fn create_window(app_handle: &AppHandle) {
/// save window size and position
pub fn save_window_size_position(app_handle: &AppHandle, save_to_file: bool) -> Result<()> {
let verge = Config::verge();
let mut verge = verge.latest();
if save_to_file {
verge.save_file()?;
}
let win = app_handle
.get_window("main")
.ok_or(anyhow::anyhow!("failed to get window"))?;
@@ -205,12 +213,8 @@ pub fn save_window_size_position(app_handle: &AppHandle, save_to_file: bool) ->
let pos = win.outer_position()?;
let pos = pos.to_logical::<f64>(scale);
let verge = Config::verge();
let mut verge = verge.latest();
verge.window_size_position = Some(vec![size.width, size.height, pos.x, pos.y]);
if save_to_file {
verge.save_file()?;
if size.width >= 600.0 && size.height >= 520.0 {
verge.window_size_position = Some(vec![size.width, size.height, pos.x, pos.y]);
}
Ok(())

View File

@@ -1,69 +0,0 @@
#![cfg(target_os = "windows")]
#![allow(non_snake_case)]
#![allow(non_camel_case_types)]
//!
//! From https://github.com/tauri-apps/window-vibrancy/blob/dev/src/windows.rs
//!
use windows_sys::Win32::{
Foundation::*,
System::{LibraryLoader::*, SystemInformation::*},
};
fn get_function_impl(library: &str, function: &str) -> Option<FARPROC> {
assert_eq!(library.chars().last(), Some('\0'));
assert_eq!(function.chars().last(), Some('\0'));
let module = unsafe { LoadLibraryA(library.as_ptr()) };
if module == 0 {
return None;
}
Some(unsafe { GetProcAddress(module, function.as_ptr()) })
}
macro_rules! get_function {
($lib:expr, $func:ident) => {
get_function_impl(concat!($lib, '\0'), concat!(stringify!($func), '\0')).map(|f| unsafe {
std::mem::transmute::<::windows_sys::Win32::Foundation::FARPROC, $func>(f)
})
};
}
/// Returns a tuple of (major, minor, buildnumber)
fn get_windows_ver() -> Option<(u32, u32, u32)> {
type RtlGetVersion = unsafe extern "system" fn(*mut OSVERSIONINFOW) -> i32;
let handle = get_function!("ntdll.dll", RtlGetVersion);
if let Some(rtl_get_version) = handle {
unsafe {
let mut vi = OSVERSIONINFOW {
dwOSVersionInfoSize: 0,
dwMajorVersion: 0,
dwMinorVersion: 0,
dwBuildNumber: 0,
dwPlatformId: 0,
szCSDVersion: [0; 128],
};
let status = (rtl_get_version)(&mut vi as _);
if status >= 0 {
Some((vi.dwMajorVersion, vi.dwMinorVersion, vi.dwBuildNumber))
} else {
None
}
}
} else {
None
}
}
pub fn is_win11() -> bool {
let v = get_windows_ver().unwrap_or_default();
v.2 >= 22000
}
#[test]
fn test_version() {
dbg!(get_windows_ver().unwrap_or_default());
}

View File

@@ -1,7 +1,7 @@
{
"package": {
"productName": "Clash Verge",
"version": "1.4.4"
"version": "1.4.5"
},
"build": {
"distDir": "../dist",

View File

@@ -1,7 +1,7 @@
{
"tauri": {
"systemTray": {
"iconPath": "icons/icon.png",
"iconPath": "icons/mac-tray-icon.png",
"iconAsTemplate": true
},
"bundle": {

View File

@@ -18,7 +18,7 @@ export const BasePage: React.FC<Props> = (props) => {
return (
<BaseErrorBoundary>
<div className="base-page" data-windrag>
<div className="base-page">
<header data-windrag style={{ userSelect: "none" }}>
<Typography variant="h4" component="h1" data-windrag>
{title}
@@ -38,7 +38,7 @@ export const BasePage: React.FC<Props> = (props) => {
: "",
}}
>
<div className="base-content" style={contentStyle} data-windrag>
<div className="base-content" style={contentStyle}>
{children}
</div>
</section>

View File

@@ -3,12 +3,19 @@ import { appWindow } from "@tauri-apps/api/window";
import {
CloseRounded,
CropSquareRounded,
FilterNoneRounded,
HorizontalRuleRounded,
} from "@mui/icons-material";
import { useState } from "react";
export const LayoutControl = () => {
const minWidth = 40;
const [isMaximized, setIsMaximized] = useState(false);
appWindow.isMaximized().then((isMaximized) => {
setIsMaximized(() => isMaximized);
});
return (
<>
<Button
@@ -22,9 +29,21 @@ export const LayoutControl = () => {
<Button
size="small"
sx={{ minWidth, svg: { transform: "scale(0.9)" } }}
onClick={() => appWindow.toggleMaximize()}
onClick={() => {
setIsMaximized((isMaximized) => !isMaximized);
appWindow.toggleMaximize();
}}
>
<CropSquareRounded fontSize="small" />
{isMaximized ? (
<FilterNoneRounded
fontSize="small"
style={{
transform: "rotate(180deg) scale(0.7)",
}}
/>
) : (
<CropSquareRounded fontSize="small" />
)}
</Button>
<Button

View File

@@ -209,7 +209,7 @@ export const ProfileViewer = forwardRef<ProfileViewerRef, Props>(
<TextField
{...text}
{...field}
placeholder={`clash-verge/v${version}`}
placeholder={`clash-verge-rev`}
label="User Agent"
/>
)}

View File

@@ -4,7 +4,8 @@ import { BaseDialog, DialogRef, Notice } from "@/components/base";
import { useTranslation } from "react-i18next";
import { useVerge } from "@/hooks/use-verge";
import { useLockFn } from "ahooks";
import { Lock } from "@mui/icons-material";
import { LoadingButton } from "@mui/lab";
import { SwitchAccessShortcut, RestartAlt } from "@mui/icons-material";
import {
Box,
Button,
@@ -31,6 +32,7 @@ export const ClashCoreViewer = forwardRef<DialogRef>((props, ref) => {
const { verge, mutateVerge } = useVerge();
const [open, setOpen] = useState(false);
const [upgrading, setUpgrading] = useState(false);
useImperativeHandle(ref, () => ({
open: () => setOpen(true),
@@ -78,9 +80,12 @@ export const ClashCoreViewer = forwardRef<DialogRef>((props, ref) => {
const onUpgrade = useLockFn(async () => {
try {
setUpgrading(true);
await upgradeCore();
setUpgrading(false);
Notice.success(`Successfully upgrade core`, 1000);
} catch (err: any) {
setUpgrading(false);
Notice.error(err?.response.data.message || err.toString());
}
});
@@ -93,16 +98,24 @@ export const ClashCoreViewer = forwardRef<DialogRef>((props, ref) => {
{t("Clash Core")}
<Box>
{clash_core !== "clash-meta" && (
<Button
<LoadingButton
variant="contained"
size="small"
startIcon={<SwitchAccessShortcut />}
loadingPosition="start"
loading={upgrading}
sx={{ marginRight: "8px" }}
onClick={onUpgrade}
>
{t("Upgrade")}
</Button>
</LoadingButton>
)}
<Button variant="contained" size="small" onClick={onRestart}>
<Button
variant="contained"
size="small"
onClick={onRestart}
startIcon={<RestartAlt />}
>
{t("Restart")}
</Button>
</Box>
@@ -110,7 +123,7 @@ export const ClashCoreViewer = forwardRef<DialogRef>((props, ref) => {
}
contentSx={{
pb: 0,
width: 320,
width: 400,
height: 180,
overflowY: "auto",
userSelect: "text",

View File

@@ -20,8 +20,8 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
const [open, setOpen] = useState(false);
const [values, setValues] = useState({
appLogLevel: "info",
autoCloseConnection: false,
enableClashFields: false,
autoCloseConnection: true,
enableClashFields: true,
enableBuiltinEnhanced: true,
proxyLayoutColumn: 6,
defaultLatencyTest: "",
@@ -34,7 +34,7 @@ export const MiscViewer = forwardRef<DialogRef>((props, ref) => {
setValues({
appLogLevel: verge?.app_log_level ?? "info",
autoCloseConnection: verge?.auto_close_connection ?? true,
enableClashFields: verge?.enable_clash_fields ?? false,
enableClashFields: verge?.enable_clash_fields ?? true,
enableBuiltinEnhanced: verge?.enable_builtin_enhanced ?? true,
proxyLayoutColumn: verge?.proxy_layout_column || 6,
defaultLatencyTest: verge?.default_latency_test || "",

View File

@@ -42,7 +42,7 @@ const SettingClash = ({ onError }: Props) => {
const {
enable_random_port = false,
verge_mixed_port,
enable_clash_fields = false,
enable_clash_fields = true,
} = verge ?? {};
const webRef = useRef<DialogRef>(null);

View File

@@ -111,7 +111,7 @@ const SettingVerge = ({ onError }: Props) => {
<SettingItem label={t("Copy Env Type")}>
<GuardState
value={env_type ?? OS === "windows" ? "powershell" : "bash"}
value={env_type ?? (OS === "windows" ? "powershell" : "bash")}
onCatch={onError}
onFormat={(e: any) => e.target.value}
onChange={(e) => onChangeData({ env_type: e })}