Compare commits

..

13 Commits

38 changed files with 207 additions and 192 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.6/Clash.Verge_1.4.6_x64-setup.exe)
- [Windows x86](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.6/Clash.Verge_1.4.6_x86-setup.exe)
- [Windows arm64](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.6/Clash.Verge_1.4.6_arm64-setup.exe)
- [Windows x64](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.7/Clash.Verge_1.4.7_x64-setup.exe)
- [Windows x86](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.7/Clash.Verge_1.4.7_x86-setup.exe)
- [Windows arm64](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.7/Clash.Verge_1.4.7_arm64-setup.exe)
- [macOS intel](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.6/Clash.Verge_1.4.6_x64.dmg)
- [macOS apple](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.6/Clash.Verge_1.4.6_aarch64.dmg)
- [macOS intel](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.7/Clash.Verge_1.4.7_x64.dmg)
- [macOS apple](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.7/Clash.Verge_1.4.7_aarch64.dmg)
- [Linux x64 AppImage](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.6/clash-verge_1.4.6_amd64.AppImage)
- [Linux x64 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.6/clash-verge_1.4.6_amd64.deb)
- [Linux x86 AppImage](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.6/clash-verge_1.4.6_i386.AppImage)
- [Linux x86 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.6/clash-verge_1.4.6_i386.deb)
- [Linux arm64 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.6/clash-verge_1.4.6_arm64.deb)
- [Linux x64 AppImage](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.7/clash-verge_1.4.7_amd64.AppImage)
- [Linux x64 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.7/clash-verge_1.4.7_amd64.deb)
- [Linux x86 AppImage](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.7/clash-verge_1.4.7_i386.AppImage)
- [Linux x86 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.7/clash-verge_1.4.7_i386.deb)
- [Linux arm64 deb](https://github.com/clash-verge-rev/clash-verge-rev/releases/download/v1.4.7/clash-verge_1.4.7_arm64.deb)
Or you can build it yourself. Supports Windows, Linux and macOS 10.15+
@@ -72,7 +72,7 @@ You should install Rust and Nodejs, see [here](https://tauri.app/v1/guides/getti
pnpm i
```
Then download the clash binary... Or you can download it from [clash meta release](https://github.com/MetaCubeX/Clash.Meta/releases) and rename it according to [tauri config](https://tauri.studio/docs/api/config/#tauri.bundle.externalBin).
Then download the clash binary... Or you can download it from [clash meta release](https://github.com/MetaCubeX/Clash.Meta/releases) and rename it according to [tauri config](https://tauri.app/v1/api/config#bundleconfig.externalbin).
```shell
# force update to latest version

View File

@@ -1,3 +1,13 @@
## v1.4.7
### Features
- Windows 便携版禁用应用内更新
- 支持代理组 Hidden 选项
- 支持 URL Scheme(MacOS & Linux)
---
## v1.4.6
### Features

View File

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

View File

@@ -123,7 +123,7 @@ index 6c207d9..d47dc33 100644
- if result == "\"\"" {
- anyhow::bail!("main function should return object");
- }
- return Ok(serde_json::from_str::<Mapping>(result.as_str())?);
- Ok(serde_json::from_str::<Mapping>(result.as_str())?)
- });
-
- let mut out = outputs.lock().unwrap();

View File

@@ -57,7 +57,7 @@ const META_ALPHA_MAP = {
"win32-x64": "mihomo-windows-amd64-compatible",
"win32-ia32": "mihomo-windows-386",
"win32-arm64": "mihomo-windows-arm64",
"darwin-x64": "mihomo-darwin-amd64-cgo",
"darwin-x64": "mihomo-darwin-amd64",
"darwin-arm64": "mihomo-darwin-arm64",
"linux-x64": "mihomo-linux-amd64-compatible",
"linux-ia32": "mihomo-linux-386",
@@ -102,7 +102,7 @@ const META_MAP = {
"win32-x64": "mihomo-windows-amd64-compatible",
"win32-ia32": "mihomo-windows-386",
"win32-arm64": "mihomo-windows-arm64",
"darwin-x64": "mihomo-darwin-amd64-cgo",
"darwin-x64": "mihomo-darwin-amd64",
"darwin-arm64": "mihomo-darwin-arm64",
"linux-x64": "mihomo-linux-amd64-compatible",
"linux-ia32": "mihomo-linux-386",

54
src-tauri/Cargo.lock generated
View File

@@ -28,19 +28,6 @@ dependencies = [
"version_check",
]
[[package]]
name = "ahash"
version = "0.8.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "91429305e9f0a25f6205c5b8e0d2db09e0708a7a6df0f42212bb56c32c8ac97a"
dependencies = [
"cfg-if",
"getrandom 0.2.11",
"once_cell",
"version_check",
"zerocopy",
]
[[package]]
name = "aho-corasick"
version = "0.6.10"
@@ -580,7 +567,7 @@ dependencies = [
[[package]]
name = "clash-verge"
version = "1.4.6"
version = "1.4.7"
dependencies = [
"anyhow",
"auto-launch",
@@ -1828,7 +1815,7 @@ version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
dependencies = [
"ahash 0.7.7",
"ahash",
]
[[package]]
@@ -2182,17 +2169,6 @@ version = "2.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3"
[[package]]
name = "iptools"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c03bfb870879ce6a141b644653d63b203d290ec5f3b6919cf7b30cba06a164a5"
dependencies = [
"ahash 0.8.6",
"once_cell",
"regex 1.10.2",
]
[[package]]
name = "is-docker"
version = "0.2.0"
@@ -4492,12 +4468,12 @@ dependencies = [
[[package]]
name = "sysproxy"
version = "0.3.0"
source = "git+https://github.com/zzzgydi/sysproxy-rs?branch=main#f8fab6a542c41cf3b973f4fe77d399756e3efff1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9707a79d3b95683aa5a9521e698ffd878b8fb289727c25a69157fb85d529ffff"
dependencies = [
"interfaces",
"iptools",
"thiserror",
"windows 0.52.0",
"winapi",
"winreg 0.10.1",
]
@@ -6342,26 +6318,6 @@ dependencies = [
"zvariant",
]
[[package]]
name = "zerocopy"
version = "0.7.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d6f15f7ade05d2a4935e34a457b936c23dc70a05cc1d97133dc99e7a3fe0f0e"
dependencies = [
"zerocopy-derive",
]
[[package]]
name = "zerocopy-derive"
version = "0.7.28"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbbad221e3f78500350ecbd7dfa4e63ef945c05f4c61cb7f4d3f84cd0bba649b"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.39",
]
[[package]]
name = "zip"
version = "0.6.6"

View File

@@ -1,6 +1,6 @@
[package]
name = "clash-verge"
version = "1.4.6"
version = "1.4.7"
description = "clash verge"
authors = ["zzzgydi", "wonfen", "MystiPanda"]
license = "GPL-3.0"
@@ -14,6 +14,7 @@ tauri-build = { version = "1", features = [] }
[dependencies]
warp = "0.3"
sysproxy = "0.3.0"
which = "5.0.0"
anyhow = "1.0"
dirs = "5.0"
@@ -38,7 +39,6 @@ window-shadows = { version = "0.2" }
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
reqwest = { version = "0.11", features = ["json", "rustls-tls"] }
sysproxy = { git="https://github.com/zzzgydi/sysproxy-rs", branch = "main" }
tauri = { version = "1.5", features = [ "notification-all", "icon-png", "clipboard-all", "global-shortcut-all", "process-all", "shell-all", "system-tray", "updater", "window-all"] }
[target.'cfg(windows)'.dependencies]

17
src-tauri/Info.plist Normal file
View File

@@ -0,0 +1,17 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLName</key>
<string>io.github.clash-verge-rev.clash-verge-rev</string>
<key>CFBundleURLSchemes</key>
<array>
<string>clash</string>
</array>
</dict>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,9 @@
[Desktop Entry]
Categories={{{categories}}}
Comment={{{comment}}}
Exec={{{exec}}} %u
Icon={{{icon}}}
Name={{{name}}}
Terminal=false
Type=Application
MimeType=x-scheme-handler/clash;

View File

@@ -219,7 +219,7 @@ pub fn open_app_dir() -> CmdResult<()> {
#[tauri::command]
pub fn open_core_dir() -> CmdResult<()> {
let core_dir = wrap_err!(tauri::utils::platform::current_exe())?;
let core_dir = core_dir.parent().ok_or(format!("failed to get core dir"))?;
let core_dir = core_dir.parent().ok_or("failed to get core dir")?;
wrap_err!(open::that(core_dir))
}
@@ -252,7 +252,7 @@ pub async fn clash_api_get_proxy_delay(
) -> CmdResult<clash_api::DelayRes> {
match clash_api::get_proxy_delay(name, url).await {
Ok(res) => Ok(res),
Err(err) => Err(format!("{}", err.to_string())),
Err(err) => Err(err.to_string()),
}
}

View File

@@ -65,8 +65,8 @@ impl IClashTemp {
let config = &self.0;
ClashInfo {
port: Self::guard_mixed_port(&config),
server: Self::guard_client_ctrl(&config),
port: Self::guard_mixed_port(config),
server: Self::guard_client_ctrl(config),
secret: config.get("secret").and_then(|value| match value {
Value::String(val_str) => Some(val_str.clone()),
Value::Bool(val_bool) => Some(val_bool.to_string()),
@@ -98,7 +98,7 @@ impl IClashTemp {
Some(val_str) => {
let val_str = val_str.trim();
let val = match val_str.starts_with(":") {
let val = match val_str.starts_with(':') {
true => format!("127.0.0.1{val_str}"),
false => val_str.to_owned(),
};

View File

@@ -8,7 +8,7 @@ use sysproxy::Sysproxy;
use super::Config;
#[derive(Debug, Clone, Deserialize, Serialize)]
#[derive(Debug, Clone, Deserialize, Serialize, Default)]
pub struct PrfItem {
pub uid: Option<String>,
@@ -101,24 +101,6 @@ impl PrfOption {
}
}
impl Default for PrfItem {
fn default() -> Self {
PrfItem {
uid: None,
itype: None,
name: None,
desc: None,
file: None,
url: None,
selected: None,
extra: None,
updated: None,
option: None,
file_data: None,
}
}
}
impl PrfItem {
/// From partial item
/// must contain `itype`
@@ -188,7 +170,7 @@ impl PrfItem {
let opt_ref = option.as_ref();
let with_proxy = opt_ref.map_or(false, |o| o.with_proxy.unwrap_or(false));
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 user_agent = opt_ref.and_then(|o| o.user_agent.clone());
let mut builder = reqwest::ClientBuilder::new().use_rustls_tls().no_proxy();
@@ -213,27 +195,24 @@ impl PrfItem {
}
// 使用系统代理
else if with_proxy {
match Sysproxy::get_system_proxy() {
Ok(p @ Sysproxy { enable: true, .. }) => {
let proxy_scheme = format!("http://{}:{}", p.host, p.port);
if let Ok(p @ Sysproxy { enable: true, .. }) = Sysproxy::get_system_proxy() {
let proxy_scheme = format!("http://{}:{}", p.host, p.port);
if let Ok(proxy) = reqwest::Proxy::http(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = reqwest::Proxy::https(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = reqwest::Proxy::all(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = reqwest::Proxy::http(&proxy_scheme) {
builder = builder.proxy(proxy);
}
_ => {}
};
if let Ok(proxy) = reqwest::Proxy::https(&proxy_scheme) {
builder = builder.proxy(proxy);
}
if let Ok(proxy) = reqwest::Proxy::all(&proxy_scheme) {
builder = builder.proxy(proxy);
}
}
}
let version = match VERSION.get() {
Some(v) => format!("clash-verge/v{}", v),
None => format!("clash-verge/unknown"),
None => "clash-verge/unknown".to_string(),
};
builder = builder.user_agent(user_agent.unwrap_or(version));

View File

@@ -37,13 +37,13 @@ impl IProfiles {
profiles.items = Some(vec![]);
}
// compatible with the old old old version
profiles.items.as_mut().map(|items| {
if let Some(items) = profiles.items.as_mut() {
for item in items.iter_mut() {
if item.uid.is_none() {
item.uid = Some(help::get_uid("d"));
}
}
});
}
profiles
}
Err(err) => {
@@ -152,17 +152,19 @@ impl IProfiles {
self.items = Some(vec![]);
}
self.items.as_mut().map(|items| items.push(item));
if let Some(items) = self.items.as_mut() {
items.push(item)
}
self.save_file()
}
/// reorder items
pub fn reorder(&mut self, active_id: String, over_id: String) -> Result<()> {
let mut items = self.items.take().unwrap_or(vec![]);
let mut items = self.items.take().unwrap_or_default();
let mut old_index = None;
let mut new_index = None;
for i in 0..items.len() {
for (i, _) in items.iter().enumerate() {
if items[i].uid == Some(active_id.clone()) {
old_index = Some(i);
}
@@ -182,7 +184,7 @@ impl IProfiles {
/// update the item value
pub fn patch_item(&mut self, uid: String, item: PrfItem) -> Result<()> {
let mut items = self.items.take().unwrap_or(vec![]);
let mut items = self.items.take().unwrap_or_default();
for each in items.iter_mut() {
if each.uid == Some(uid.clone()) {
@@ -255,11 +257,11 @@ impl IProfiles {
let current = self.current.as_ref().unwrap_or(&uid);
let current = current.clone();
let mut items = self.items.take().unwrap_or(vec![]);
let mut items = self.items.take().unwrap_or_default();
let mut index = None;
// get the index
for i in 0..items.len() {
for (i, _) in items.iter().enumerate() {
if items[i].uid == Some(uid.clone()) {
index = Some(i);
break;
@@ -267,19 +269,19 @@ impl IProfiles {
}
if let Some(index) = index {
items.remove(index).file.map(|file| {
if let Some(file) = items.remove(index).file {
let _ = dirs::app_profiles_dir().map(|path| {
let path = path.join(file);
if path.exists() {
let _ = fs::remove_file(path);
}
});
});
}
}
// delete the original uid
if current == uid {
self.current = match items.len() > 0 {
self.current = match !items.is_empty() {
true => items[0].uid.clone(),
false => None,
};
@@ -299,7 +301,7 @@ impl IProfiles {
Some(file) => dirs::app_profiles_dir()?.join(file),
None => bail!("failed to get the file field"),
};
return Ok(help::read_merge_mapping(&file_path)?);
return help::read_merge_mapping(&file_path);
}
bail!("failed to find the current profile \"uid:{current}\"");
}

View File

@@ -83,12 +83,12 @@ fn clash_client_info() -> Result<(String, HeaderMap)> {
/// 缩短clash的日志
pub fn parse_log(log: String) -> String {
if log.starts_with("time=") && log.len() > 33 {
return (&log[33..]).to_owned();
return (log[33..]).to_owned();
}
if log.len() > 9 {
return (&log[9..]).to_owned();
return (log[9..]).to_owned();
}
return log;
log
}
/// 缩短clash -t的错误输出
@@ -105,7 +105,7 @@ pub fn parse_check_output(log: String) -> String {
};
if mr > m {
return (&log[e..mr]).to_owned();
return (log[e..mr]).to_owned();
}
}
@@ -113,7 +113,7 @@ pub fn parse_check_output(log: String) -> String {
let r = log.find("path=").or(Some(log.len()));
if let (Some(l), Some(r)) = (l, r) {
return (&log[(l + 6)..(r - 1)]).to_owned();
return (log[(l + 6)..(r - 1)]).to_owned();
}
log

View File

@@ -35,12 +35,12 @@ impl CoreManager {
.map(|pid| {
let mut system = System::new();
system.refresh_all();
system.process(Pid::from_u32(pid)).map(|proc| {
if let Some(proc) = system.process(Pid::from_u32(pid)) {
if proc.name().contains("clash") {
log::debug!(target: "app", "kill old clash process");
proc.kill();
}
});
}
});
tauri::async_runtime::spawn(async {
@@ -68,7 +68,7 @@ impl CoreManager {
if !output.status.success() {
let error = clash_api::parse_check_output(output.stdout.clone());
let error = match error.len() > 0 {
let error = match !error.is_empty() {
true => error,
false => output.stdout.clone(),
};
@@ -110,7 +110,7 @@ impl CoreManager {
use super::win_service;
// 服务模式
let enable = { Config::verge().latest().enable_service_mode.clone() };
let enable = { Config::verge().latest().enable_service_mode };
let enable = enable.unwrap_or(false);
*self.use_service_mode.lock() = enable;

View File

@@ -28,7 +28,7 @@ impl Handle {
self.app_handle
.lock()
.as_ref()
.map_or(None, |a| a.get_window("main"))
.and_then(|a| a.get_window("main"))
}
pub fn refresh_clash() {

View File

@@ -65,17 +65,17 @@ impl Hotkey {
}
let f = match func.trim() {
"open_dashboard" => || feat::open_dashboard(),
"open_dashboard" => feat::open_dashboard,
"clash_mode_rule" => || feat::change_clash_mode("rule".into()),
"clash_mode_global" => || feat::change_clash_mode("global".into()),
"clash_mode_direct" => || feat::change_clash_mode("direct".into()),
"clash_mode_script" => || feat::change_clash_mode("script".into()),
"toggle_system_proxy" => || feat::toggle_system_proxy(),
"enable_system_proxy" => || feat::enable_system_proxy(),
"disable_system_proxy" => || feat::disable_system_proxy(),
"toggle_tun_mode" => || feat::toggle_tun_mode(),
"enable_tun_mode" => || feat::enable_tun_mode(),
"disable_tun_mode" => || feat::disable_tun_mode(),
"toggle_system_proxy" => feat::toggle_system_proxy,
"enable_system_proxy" => feat::enable_system_proxy,
"disable_system_proxy" => feat::disable_system_proxy,
"toggle_tun_mode" => feat::toggle_tun_mode,
"enable_tun_mode" => feat::enable_tun_mode,
"disable_tun_mode" => feat::disable_tun_mode,
_ => bail!("invalid function \"{func}\""),
};
@@ -86,7 +86,7 @@ impl Hotkey {
}
fn unregister(&self, hotkey: &str) -> Result<()> {
self.get_manager()?.unregister(&hotkey)?;
self.get_manager()?.unregister(hotkey)?;
log::info!(target: "app", "unregister hotkey {hotkey}");
Ok(())
}
@@ -110,7 +110,7 @@ impl Hotkey {
Ok(())
}
fn get_map_from_vec<'a>(hotkeys: &'a Vec<String>) -> HashMap<&'a str, &'a str> {
fn get_map_from_vec(hotkeys: &Vec<String>) -> HashMap<&str, &str> {
let mut map = HashMap::new();
hotkeys.iter().for_each(|hotkey| {

View File

@@ -53,7 +53,7 @@ impl Sysopt {
let verge = Config::verge();
let verge = verge.latest();
(
verge.enable_system_proxy.clone().unwrap_or(false),
verge.enable_system_proxy.unwrap_or(false),
verge.system_proxy_bypass.clone(),
)
};
@@ -66,7 +66,7 @@ impl Sysopt {
};
if enable {
let old = Sysproxy::get_system_proxy().map_or(None, |p| Some(p));
let old = Sysproxy::get_system_proxy().ok();
current.set_system_proxy()?;
*self.old_sysproxy.lock() = old;
@@ -93,7 +93,7 @@ impl Sysopt {
let verge = Config::verge();
let verge = verge.latest();
(
verge.enable_system_proxy.clone().unwrap_or(false),
verge.enable_system_proxy.unwrap_or(false),
verge.system_proxy_bypass.clone(),
)
};
@@ -142,7 +142,7 @@ impl Sysopt {
/// init the auto launch
pub fn init_launch(&self) -> Result<()> {
let enable = { Config::verge().latest().enable_auto_launch.clone() };
let enable = { Config::verge().latest().enable_auto_launch };
let enable = enable.unwrap_or(false);
let app_exe = current_exe()?;
@@ -233,7 +233,7 @@ impl Sysopt {
drop(auto_launch);
return self.init_launch();
}
let enable = { Config::verge().latest().enable_auto_launch.clone() };
let enable = { Config::verge().latest().enable_auto_launch };
let enable = enable.unwrap_or(false);
let auto_launch = auto_launch.as_ref().unwrap();
@@ -271,9 +271,9 @@ impl Sysopt {
let verge = Config::verge();
let verge = verge.latest();
(
verge.enable_system_proxy.clone().unwrap_or(false),
verge.enable_proxy_guard.clone().unwrap_or(false),
verge.proxy_guard_duration.clone().unwrap_or(10),
verge.enable_system_proxy.unwrap_or(false),
verge.enable_proxy_guard.unwrap_or(false),
verge.proxy_guard_duration.unwrap_or(10),
verge.system_proxy_bypass.clone(),
)
};

View File

@@ -40,7 +40,7 @@ impl Timer {
let timer_map = self.timer_map.lock();
let delay_timer = self.delay_timer.lock();
Config::profiles().latest().get_items().map(|items| {
if let Some(items) = Config::profiles().latest().get_items() {
items
.iter()
.filter_map(|item| {
@@ -61,7 +61,7 @@ impl Timer {
}
}
})
});
}
Ok(())
}

View File

@@ -114,9 +114,9 @@ pub fn use_sort(config: Mapping, enable_filter: bool) -> Mapping {
.chain(DEFAULT_FIELDS)
.for_each(|key| {
let key = Value::from(key);
config.get(&key).map(|value| {
if let Some(value) = config.get(&key) {
ret.insert(key, value.clone());
});
}
});
if !enable_filter {
@@ -134,9 +134,9 @@ pub fn use_sort(config: Mapping, enable_filter: bool) -> Mapping {
config_keys.difference(&supported_keys).for_each(|&key| {
let key = Value::from(key);
config.get(&key).map(|value| {
if let Some(value) = config.get(&key) {
ret.insert(key, value.clone());
});
}
});
}
@@ -150,7 +150,7 @@ pub fn use_keys(config: &Mapping) -> Vec<String> {
.map(|s| {
let mut s = s.to_string();
s.make_ascii_lowercase();
return s;
s
})
.collect()
}

View File

@@ -27,9 +27,9 @@ pub fn enhance() -> (Mapping, Vec<String>, HashMap<String, ResultLog>) {
let verge = verge.latest();
(
verge.clash_core.clone(),
verge.enable_tun_mode.clone().unwrap_or(false),
verge.enable_builtin_enhanced.clone().unwrap_or(true),
verge.enable_clash_fields.clone().unwrap_or(true),
verge.enable_tun_mode.unwrap_or(false),
verge.enable_builtin_enhanced.unwrap_or(true),
verge.enable_clash_fields.unwrap_or(true),
)
};
@@ -38,18 +38,18 @@ pub fn enhance() -> (Mapping, Vec<String>, HashMap<String, ResultLog>) {
let profiles = Config::profiles();
let profiles = profiles.latest();
let current = profiles.current_mapping().unwrap_or(Mapping::new());
let current = profiles.current_mapping().unwrap_or_default();
let chain = match profiles.chain.as_ref() {
Some(chain) => chain
.iter()
.filter_map(|uid| profiles.get_item(uid).ok())
.filter_map(|item| <Option<ChainItem>>::from(item))
.filter_map(<Option<ChainItem>>::from)
.collect::<Vec<ChainItem>>(),
None => vec![],
};
let valid = profiles.valid.clone().unwrap_or(vec![]);
let valid = profiles.valid.clone().unwrap_or_default();
(current, chain, valid)
};

View File

@@ -47,7 +47,7 @@ pub fn use_script(script: String, config: Mapping) -> Result<(Mapping, Vec<(Stri
if result == "\"\"" {
anyhow::bail!("main function should return object");
}
return Ok(serde_json::from_str::<Mapping>(result.as_str())?);
Ok(serde_json::from_str::<Mapping>(result.as_str())?)
});
let mut out = outputs.lock().unwrap();

View File

@@ -62,7 +62,7 @@ pub fn change_clash_mode(mode: String) {
// 切换系统代理
pub fn toggle_system_proxy() {
let enable = Config::verge().draft().enable_system_proxy.clone();
let enable = Config::verge().draft().enable_system_proxy;
let enable = enable.unwrap_or(false);
tauri::async_runtime::spawn(async move {
@@ -110,7 +110,7 @@ pub fn disable_system_proxy() {
// 切换tun模式
pub fn toggle_tun_mode() {
let enable = Config::verge().data().enable_tun_mode.clone();
let enable = Config::verge().data().enable_tun_mode;
let enable = enable.unwrap_or(false);
tauri::async_runtime::spawn(async move {
@@ -164,14 +164,14 @@ pub async fn patch_clash(patch: Mapping) -> Result<()> {
let mixed_port = patch.get("mixed-port");
let enable_random_port = Config::verge().latest().enable_random_port.unwrap_or(false);
if mixed_port.is_some() && !enable_random_port {
let changed = mixed_port.clone().unwrap()
let changed = mixed_port.unwrap()
!= Config::verge()
.latest()
.verge_mixed_port
.unwrap_or(Config::clash().data().get_mixed_port());
// 检查端口占用
if changed {
if let Some(port) = mixed_port.clone().unwrap().as_u64() {
if let Some(port) = mixed_port.unwrap().as_u64() {
if !port_scanner::local_port_available(port as u16) {
Config::clash().discard();
bail!("port already in use");

View File

@@ -25,7 +25,10 @@ fn main() -> std::io::Result<()> {
#[allow(unused_mut)]
let mut builder = tauri::Builder::default()
.system_tray(SystemTray::new())
.setup(|app| Ok(resolve::resolve_setup(app)))
.setup(|app| {
resolve::resolve_setup(app);
Ok(())
})
.on_system_tray_event(core::tray::Tray::on_system_tray_event)
.invoke_handler(tauri::generate_handler![
// common
@@ -133,13 +136,13 @@ fn main() -> std::io::Result<()> {
if label == "main" {
match event {
tauri::WindowEvent::Destroyed => {
let _ = resolve::save_window_size_position(&app_handle, true);
let _ = resolve::save_window_size_position(app_handle, true);
}
tauri::WindowEvent::CloseRequested { .. } => {
let _ = resolve::save_window_size_position(&app_handle, true);
let _ = resolve::save_window_size_position(app_handle, true);
}
tauri::WindowEvent::Moved(_) | tauri::WindowEvent::Resized(_) => {
let _ = resolve::save_window_size_position(&app_handle, false);
let _ = resolve::save_window_size_position(app_handle, false);
}
_ => {}
}

View File

@@ -14,7 +14,7 @@ pub fn read_yaml<T: DeserializeOwned>(path: &PathBuf) -> Result<T> {
bail!("file not found \"{}\"", path.display());
}
let yaml_str = fs::read_to_string(&path)
let yaml_str = fs::read_to_string(path)
.with_context(|| format!("failed to read the file \"{}\"", path.display()))?;
serde_yaml::from_str::<T>(&yaml_str).with_context(|| {
@@ -89,11 +89,11 @@ pub fn open_file(app: tauri::AppHandle, path: PathBuf) -> Result<()> {
let code = "code";
let _ = match Program::from_str(code) {
Ok(code) => open(&app.shell_scope(), &path.to_string_lossy(), Some(code)),
Ok(code) => open(&app.shell_scope(), path.to_string_lossy(), Some(code)),
Err(err) => {
log::error!(target: "app", "Can't find VScode `{err}`");
// default open
open(&app.shell_scope(), &path.to_string_lossy(), None)
open(&app.shell_scope(), path.to_string_lossy(), None)
}
};

View File

@@ -79,7 +79,7 @@ pub fn delete_log() -> Result<()> {
let auto_log_clean = {
let verge = Config::verge();
let verge = verge.data();
verge.auto_log_clean.clone().unwrap_or(0)
verge.auto_log_clean.unwrap_or(0)
};
let day = match auto_log_clean {
@@ -130,10 +130,8 @@ pub fn delete_log() -> Result<()> {
Ok(())
};
for file in fs::read_dir(&log_dir)? {
if let Ok(file) = file {
let _ = process_file(file);
}
for file in fs::read_dir(&log_dir)?.flatten() {
let _ = process_file(file);
}
Ok(())
}
@@ -310,14 +308,14 @@ pub fn init_scheme() -> Result<()> {
let app_exe = current_exe()?;
let app_exe = dunce::canonicalize(app_exe)?;
let app_exe = app_exe.to_string_lossy().to_owned();
let app_exe = app_exe.to_string_lossy().into_owned();
let hkcu = RegKey::predef(HKEY_CURRENT_USER);
let (clash, _) = hkcu.create_subkey("Software\\Classes\\Clash")?;
clash.set_value("", &"Clash Verge")?;
clash.set_value("URL Protocol", &"Clash Verge URL Scheme Protocol")?;
let (default_icon, _) = hkcu.create_subkey("Software\\Classes\\Clash\\DefaultIcon")?;
default_icon.set_value("", &format!("{app_exe}"))?;
default_icon.set_value("", &app_exe)?;
let (command, _) = hkcu.create_subkey("Software\\Classes\\Clash\\Shell\\Open\\Command")?;
command.set_value("", &format!("{app_exe} \"%1\""))?;
@@ -325,6 +323,17 @@ pub fn init_scheme() -> Result<()> {
}
#[cfg(target_os = "linux")]
pub fn init_scheme() -> Result<()> {
let output = std::process::Command::new("xdg-mime")
.arg("default")
.arg("clash-verge.desktop")
.arg("x-scheme-handler/clash")
.output()?;
if !output.status.success() {
return Err(anyhow::anyhow!(
"failed to set clash scheme, {}",
String::from_utf8_lossy(&output.stderr)
));
}
Ok(())
}
#[cfg(target_os = "macos")]

View File

@@ -86,7 +86,7 @@ pub fn resolve_setup(app: &mut App) {
log::trace!("init system tray");
log_err!(tray::Tray::update_systray(&app.app_handle()));
let silent_start = { Config::verge().data().enable_silent_start.clone() };
let silent_start = { Config::verge().data().enable_silent_start };
if !silent_start.unwrap_or(false) {
create_window(&app.app_handle());
}
@@ -240,15 +240,17 @@ pub fn save_window_size_position(app_handle: &AppHandle, save_to_file: bool) ->
}
pub async fn resolve_scheme(param: String) {
let url = param.trim_start_matches("clash://install-config/?url=");
let url = param
.trim_start_matches("clash://install-config/?url=")
.trim_start_matches("clash://install-config?url=");
let option = PrfOption {
user_agent: None,
with_proxy: Some(true),
self_proxy: None,
update_interval: None,
};
if let Ok(item) = PrfItem::from_url(&url, None, None, Some(option)).await {
if let Ok(_) = Config::profiles().data().append_item(item) {
if let Ok(item) = PrfItem::from_url(url, None, None, Some(option)).await {
if Config::profiles().data().append_item(item).is_ok() {
notification::Notification::new(crate::utils::dirs::APP_ID)
.title("Clash Verge")
.body("Import profile success")

View File

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

View File

@@ -6,7 +6,8 @@
"bundle": {
"targets": ["deb", "appimage", "updater"],
"deb": {
"depends": ["openssl"]
"depends": ["openssl"],
"desktopTemplate": "./clash-verge.desktop"
}
}
}

View File

@@ -98,6 +98,8 @@ export const ProxyItemMini = (props: Props) => {
)}
<TypeBox component="span">{proxy.type}</TypeBox>
{proxy.udp && <TypeBox component="span">UDP</TypeBox>}
{proxy.xudp && <TypeBox component="span">XUDP</TypeBox>}
{proxy.tfo && <TypeBox component="span">TFO</TypeBox>}
</Box>
)}
</Box>

View File

@@ -104,6 +104,10 @@ export const ProxyItem = (props: Props) => {
)}
{showType && <TypeBox component="span">{proxy.type}</TypeBox>}
{showType && proxy.udp && <TypeBox component="span">UDP</TypeBox>}
{showType && proxy.xudp && (
<TypeBox component="span">XUDP</TypeBox>
)}
{showType && proxy.tfo && <TypeBox component="span">TFO</TypeBox>}
</>
}
/>

View File

@@ -31,7 +31,7 @@ export const ProxyRender = (props: RenderProps) => {
props;
const { type, group, headState, proxy, proxyCol } = item;
if (type === 0) {
if (type === 0 && !group.hidden) {
return (
<ListItemButton
dense
@@ -61,7 +61,7 @@ export const ProxyRender = (props: RenderProps) => {
);
}
if (type === 1) {
if (type === 1 && !group.hidden) {
return (
<ProxyHead
sx={{ pl: indent ? 4.5 : 2.5, pr: 3, mt: indent ? 1 : 0.5, mb: 1 }}
@@ -74,7 +74,7 @@ export const ProxyRender = (props: RenderProps) => {
);
}
if (type === 2) {
if (type === 2 && !group.hidden) {
return (
<ProxyItem
groupName={group.name}
@@ -87,7 +87,7 @@ export const ProxyRender = (props: RenderProps) => {
);
}
if (type === 3) {
if (type === 3 && !group.hidden) {
return (
<Box
sx={{
@@ -105,7 +105,7 @@ export const ProxyRender = (props: RenderProps) => {
);
}
if (type === 4) {
if (type === 4 && !group.hidden) {
return (
<Box
sx={{

View File

@@ -10,6 +10,7 @@ import { checkUpdate, installUpdate } from "@tauri-apps/api/updater";
import { BaseDialog, DialogRef, Notice } from "@/components/base";
import { atomUpdateState } from "@/services/states";
import { listen, Event, UnlistenFn } from "@tauri-apps/api/event";
import { portableFlag } from "@/pages/_layout";
const UpdateLog = styled(Box)(() => ({
"h1,h2,h3,ul,ol,p": { margin: "0.5em 0", color: "inherit" },
@@ -46,6 +47,10 @@ export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
}, [updateInfo]);
const onUpdate = useLockFn(async () => {
if (portableFlag) {
Notice.error(t("Portable Updater Error"));
return;
}
if (updateState) return;
setUpdateState(true);
if (eventListener !== null) {

View File

@@ -145,5 +145,7 @@
"Never Clean": "Never Clean",
"Retain 7 Days": "Retain 7 Days",
"Retain 30 Days": "Retain 30 Days",
"Retain 90 Days": "Retain 90 Days"
"Retain 90 Days": "Retain 90 Days",
"Portable Updater Error": "The portable version does not support in-app updates. Please manually download and replace it"
}

View File

@@ -115,5 +115,7 @@
"disable_system_proxy": "Отключить системный прокси",
"toggle_tun_mode": "Переключить режим туннеля",
"enable_tun_mode": "Включить режим туннеля",
"disable_tun_mode": "Отключить режим туннеля"
"disable_tun_mode": "Отключить режим туннеля",
"Portable Updater Error": "Портативная версия не поддерживает обновление внутри приложения, пожалуйста, скачайте и замените вручную"
}

View File

@@ -145,5 +145,7 @@
"Never Clean": "不清理",
"Retain 7 Days": "保留7天",
"Retain 30 Days": "保留30天",
"Retain 90 Days": "保留90天"
"Retain 90 Days": "保留90天",
"Portable Updater Error": "便携版不支持应用内更新,请手动下载替换"
}

View File

@@ -119,7 +119,14 @@ export const getProxies = async () => {
const generateItem = (name: string) => {
if (proxyRecord[name]) return proxyRecord[name];
if (providerMap[name]) return providerMap[name];
return { name, type: "unknown", udp: false, history: [] };
return {
name,
type: "unknown",
udp: false,
xudp: false,
tfo: false,
history: [],
};
};
const { GLOBAL: global, DIRECT: direct, REJECT: reject } = proxyRecord;

View File

@@ -44,12 +44,15 @@ interface IProxyItem {
name: string;
type: string;
udp: boolean;
xudp: boolean;
tfo: boolean;
history: {
time: string;
delay: number;
}[];
all?: string[];
now?: string;
hidden?: boolean;
provider?: string; // 记录是否来自provider
}