mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-11-09 03:42:06 +08:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a00c67e8ae | ||
|
|
6ebdaaa7ff | ||
|
|
15de400f2f | ||
|
|
6f36613103 |
@@ -17,3 +17,4 @@ CLAUDE.md
|
|||||||
.vfox.toml
|
.vfox.toml
|
||||||
.vfox/
|
.vfox/
|
||||||
.claude
|
.claude
|
||||||
|
AGENTS.md
|
||||||
|
|||||||
Generated
+44
-5
@@ -1150,7 +1150,7 @@ checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "clash-verge"
|
name = "clash-verge"
|
||||||
version = "2.5.0"
|
version = "2.5.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"aes-gcm",
|
"aes-gcm",
|
||||||
"anyhow",
|
"anyhow",
|
||||||
@@ -1665,6 +1665,19 @@ dependencies = [
|
|||||||
"once_cell",
|
"once_cell",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam"
|
||||||
|
version = "0.8.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "1137cd7e7fc0fb5d3c5a8678be38ec56e819125d8d7907411fe24ccb943faca8"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-channel",
|
||||||
|
"crossbeam-deque",
|
||||||
|
"crossbeam-epoch",
|
||||||
|
"crossbeam-queue",
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-channel"
|
name = "crossbeam-channel"
|
||||||
version = "0.5.15"
|
version = "0.5.15"
|
||||||
@@ -1693,6 +1706,15 @@ dependencies = [
|
|||||||
"crossbeam-utils",
|
"crossbeam-utils",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "crossbeam-queue"
|
||||||
|
version = "0.3.12"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115"
|
||||||
|
dependencies = [
|
||||||
|
"crossbeam-utils",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "crossbeam-utils"
|
name = "crossbeam-utils"
|
||||||
version = "0.8.21"
|
version = "0.8.21"
|
||||||
@@ -3560,7 +3582,7 @@ dependencies = [
|
|||||||
"tokio",
|
"tokio",
|
||||||
"tower-service",
|
"tower-service",
|
||||||
"tracing",
|
"tracing",
|
||||||
"windows-registry",
|
"windows-registry 0.6.1",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -7785,7 +7807,7 @@ dependencies = [
|
|||||||
"thiserror 2.0.18",
|
"thiserror 2.0.18",
|
||||||
"tracing",
|
"tracing",
|
||||||
"url",
|
"url",
|
||||||
"windows-registry",
|
"windows-registry 0.5.3",
|
||||||
"windows-result 0.3.4",
|
"windows-result 0.3.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -7899,12 +7921,18 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri-plugin-mihomo"
|
name = "tauri-plugin-mihomo"
|
||||||
version = "0.1.9"
|
version = "0.1.5"
|
||||||
source = "git+https://github.com/clash-verge-rev/tauri-plugin-mihomo#a185fe1a8802367d0b3d50d402cb823759d2c57e"
|
source = "git+https://github.com/clash-verge-rev/tauri-plugin-mihomo?branch=revert#1cc80bc0fbe1245315617f4cecd93710a152325b"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
|
"bytes",
|
||||||
|
"crossbeam",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"http 1.4.0",
|
"http 1.4.0",
|
||||||
|
"http-body-util",
|
||||||
"httparse",
|
"httparse",
|
||||||
|
"hyper 1.9.0",
|
||||||
|
"libc",
|
||||||
"log",
|
"log",
|
||||||
"pin-project",
|
"pin-project",
|
||||||
"rand 0.9.4",
|
"rand 0.9.4",
|
||||||
@@ -9803,6 +9831,17 @@ dependencies = [
|
|||||||
"windows-strings 0.4.2",
|
"windows-strings 0.4.2",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-registry"
|
||||||
|
version = "0.6.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "02752bf7fbdcce7f2a27a742f798510f3e5ad88dbe84871e5168e2120c3d5720"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link 0.2.1",
|
||||||
|
"windows-result 0.4.1",
|
||||||
|
"windows-strings 0.5.1",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "windows-result"
|
name = "windows-result"
|
||||||
version = "0.3.4"
|
version = "0.3.4"
|
||||||
|
|||||||
+4
-31
@@ -1,35 +1,8 @@
|
|||||||
## v2.5.0
|
## v2.5.1
|
||||||
|
|
||||||
> [!IMPORTANT]
|
|
||||||
> 关于版本的说明:Clash Verge 版本号遵循 x.y.z:x 为重大架构变更,y 为功能新增,z 为 Bug 修复。
|
|
||||||
|
|
||||||
- **Mihomo(Meta) 内核升级至 v1.19.25**
|
|
||||||
|
|
||||||
### 🐞 修复问题
|
### 🐞 修复问题
|
||||||
|
|
||||||
- 修复系统代理关闭后在 PAC 模式下未完全关闭
|
- 备份设置功能异常
|
||||||
- 修复 macOS 开关代理时可能的卡死
|
- 修复 Windows 节点交互异常
|
||||||
- 修复修改定时自动更新后记时未及时刷新
|
|
||||||
- 修复 Linux 关闭 TUN 不立即生效
|
|
||||||
- 修复系统代理关闭序列逻辑(防止快速退出时系统代理关闭状态没有保存)
|
|
||||||
- 修复 Linux 快捷键映射错误
|
|
||||||
- 修复 Linux Wayland 兼容性导致渲染错误
|
|
||||||
- 修复 Linux 边缘情况无法加载页面
|
|
||||||
|
|
||||||
### ✨ 新增功能
|
</details>
|
||||||
|
|
||||||
- 订阅 QR code 分享
|
|
||||||
- 新增 macOS 托盘速率显示
|
|
||||||
- 快捷键操作通知操作结果
|
|
||||||
- 软件自动更新(后台下载,下次启动自动安装)
|
|
||||||
|
|
||||||
### 🚀 优化改进
|
|
||||||
|
|
||||||
- 优化 macOS 读取系统代理性能
|
|
||||||
- 优化前端 CPU 性能
|
|
||||||
- 更健壮的服务模式与边缘状况内核恢复
|
|
||||||
- 优化白名单网络下的订阅 TLS 更新兼容性
|
|
||||||
|
|
||||||
### 👙 界面样式
|
|
||||||
|
|
||||||
- 代理组实现 Sticky Scroll 效果
|
|
||||||
|
|||||||
@@ -1,3 +1,40 @@
|
|||||||
|
## v2.5.0
|
||||||
|
|
||||||
|
> [!IMPORTANT]
|
||||||
|
> 关于版本的说明:Clash Verge 版本号遵循 x.y.z:x 为重大架构变更,y 为功能新增,z 为 Bug 修复。
|
||||||
|
|
||||||
|
- **Mihomo(Meta) 内核升级至 v1.19.25**
|
||||||
|
|
||||||
|
### 🐞 修复问题
|
||||||
|
|
||||||
|
- 修复系统代理关闭后在 PAC 模式下未完全关闭
|
||||||
|
- 修复 macOS 开关代理时可能的卡死
|
||||||
|
- 修复修改定时自动更新后记时未及时刷新
|
||||||
|
- 修复 Linux 关闭 TUN 不立即生效
|
||||||
|
- 修复系统代理关闭序列逻辑(防止快速退出时系统代理关闭状态没有保存)
|
||||||
|
- 修复 Linux 快捷键映射错误
|
||||||
|
- 修复 Linux Wayland 兼容性导致渲染错误
|
||||||
|
- 修复 Linux 边缘情况无法加载页面
|
||||||
|
|
||||||
|
### ✨ 新增功能
|
||||||
|
|
||||||
|
- 订阅 QR code 分享
|
||||||
|
- 新增 macOS 托盘速率显示
|
||||||
|
- 快捷键操作通知操作结果
|
||||||
|
- 软件自动更新(后台下载,下次启动自动安装)
|
||||||
|
|
||||||
|
### 🚀 优化改进
|
||||||
|
|
||||||
|
- 优化 macOS 读取系统代理性能
|
||||||
|
- 优化前端 CPU 性能
|
||||||
|
- 更健壮的服务模式与边缘状况内核恢复
|
||||||
|
- 优化白名单网络下的订阅 TLS 更新兼容性
|
||||||
|
|
||||||
|
### 👙 界面样式
|
||||||
|
|
||||||
|
- 代理组实现 Sticky Scroll 效果
|
||||||
|
|
||||||
|
|
||||||
## v2.4.7
|
## v2.4.7
|
||||||
|
|
||||||
### 🐞 修复问题
|
### 🐞 修复问题
|
||||||
|
|||||||
+1
-2
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "clash-verge",
|
"name": "clash-verge",
|
||||||
"version": "2.5.0",
|
"version": "2.5.1",
|
||||||
"license": "GPL-3.0-only",
|
"license": "GPL-3.0-only",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"prepare": "husky || true",
|
"prepare": "husky || true",
|
||||||
@@ -42,7 +42,6 @@
|
|||||||
"@juggle/resize-observer": "^3.4.0",
|
"@juggle/resize-observer": "^3.4.0",
|
||||||
"@monaco-editor/react": "^4.7.0",
|
"@monaco-editor/react": "^4.7.0",
|
||||||
"@mui/icons-material": "^9.0.0",
|
"@mui/icons-material": "^9.0.0",
|
||||||
"@mui/lab": "9.0.0-beta.2",
|
|
||||||
"@mui/material": "^9.0.0",
|
"@mui/material": "^9.0.0",
|
||||||
"@tanstack/react-query": "^5.96.1",
|
"@tanstack/react-query": "^5.96.1",
|
||||||
"@tanstack/react-table": "^8.21.3",
|
"@tanstack/react-table": "^8.21.3",
|
||||||
|
|||||||
Generated
-40
@@ -32,9 +32,6 @@ importers:
|
|||||||
'@mui/icons-material':
|
'@mui/icons-material':
|
||||||
specifier: ^9.0.0
|
specifier: ^9.0.0
|
||||||
version: 9.0.0(@mui/material@9.0.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/react@19.2.14)(react@19.2.5)
|
version: 9.0.0(@mui/material@9.0.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/react@19.2.14)(react@19.2.5)
|
||||||
'@mui/lab':
|
|
||||||
specifier: 9.0.0-beta.2
|
|
||||||
version: 9.0.0-beta.2(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@mui/material@9.0.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
|
||||||
'@mui/material':
|
'@mui/material':
|
||||||
specifier: ^9.0.0
|
specifier: ^9.0.0
|
||||||
version: 9.0.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
version: 9.0.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||||
@@ -1068,27 +1065,6 @@ packages:
|
|||||||
'@types/react':
|
'@types/react':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@mui/lab@9.0.0-beta.2':
|
|
||||||
resolution: {integrity: sha512-UqykXQCn7HNSExyYBvrFpaaEEmUwHbEgjFDBApEkawVPcBILyYNFhpXoqkwkafiZy+WsvcxIeRF0z4tCgisTRg==}
|
|
||||||
engines: {node: '>=14.0.0'}
|
|
||||||
peerDependencies:
|
|
||||||
'@emotion/react': ^11.5.0
|
|
||||||
'@emotion/styled': ^11.3.0
|
|
||||||
'@mui/material': ^9.0.0
|
|
||||||
'@mui/material-pigment-css': ^9.0.0
|
|
||||||
'@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
|
|
||||||
react: ^17.0.0 || ^18.0.0 || ^19.0.0
|
|
||||||
react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
|
|
||||||
peerDependenciesMeta:
|
|
||||||
'@emotion/react':
|
|
||||||
optional: true
|
|
||||||
'@emotion/styled':
|
|
||||||
optional: true
|
|
||||||
'@mui/material-pigment-css':
|
|
||||||
optional: true
|
|
||||||
'@types/react':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@mui/material@9.0.0':
|
'@mui/material@9.0.0':
|
||||||
resolution: {integrity: sha512-+VP/oQCDhDR87NQQgXnNBG8dwy6GNuQLnenS1pZvkbn2dKFSxRSRMybTpH9xUxXP+316mlYDy5CSbYtusnCWtw==}
|
resolution: {integrity: sha512-+VP/oQCDhDR87NQQgXnNBG8dwy6GNuQLnenS1pZvkbn2dKFSxRSRMybTpH9xUxXP+316mlYDy5CSbYtusnCWtw==}
|
||||||
engines: {node: '>=14.0.0'}
|
engines: {node: '>=14.0.0'}
|
||||||
@@ -4651,22 +4627,6 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.2.14
|
'@types/react': 19.2.14
|
||||||
|
|
||||||
'@mui/lab@9.0.0-beta.2(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@mui/material@9.0.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.29.2
|
|
||||||
'@mui/material': 9.0.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
|
||||||
'@mui/system': 9.0.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5)
|
|
||||||
'@mui/types': 9.0.0(@types/react@19.2.14)
|
|
||||||
'@mui/utils': 9.0.0(@types/react@19.2.14)(react@19.2.5)
|
|
||||||
clsx: 2.1.1
|
|
||||||
prop-types: 15.8.1
|
|
||||||
react: 19.2.5
|
|
||||||
react-dom: 19.2.5(react@19.2.5)
|
|
||||||
optionalDependencies:
|
|
||||||
'@emotion/react': 11.14.0(@types/react@19.2.14)(react@19.2.5)
|
|
||||||
'@emotion/styled': 11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5)
|
|
||||||
'@types/react': 19.2.14
|
|
||||||
|
|
||||||
'@mui/material@9.0.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
'@mui/material@9.0.0(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@emotion/styled@11.14.1(@emotion/react@11.14.0(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react@19.2.5))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.29.2
|
'@babel/runtime': 7.29.2
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "clash-verge"
|
name = "clash-verge"
|
||||||
version = "2.5.0"
|
version = "2.5.1"
|
||||||
description = "clash verge"
|
description = "clash verge"
|
||||||
authors = ["zzzgydi", "Tunglies", "wonfen", "MystiPanda"]
|
authors = ["zzzgydi", "Tunglies", "wonfen", "MystiPanda"]
|
||||||
license = "GPL-3.0-only"
|
license = "GPL-3.0-only"
|
||||||
@@ -95,7 +95,7 @@ backon = { version = "1.6.0", features = ["tokio-sleep"] }
|
|||||||
tauri-plugin-http = "2.5.7"
|
tauri-plugin-http = "2.5.7"
|
||||||
console-subscriber = { version = "0.5.0", optional = true }
|
console-subscriber = { version = "0.5.0", optional = true }
|
||||||
tauri-plugin-devtools = { version = "2.0.1" }
|
tauri-plugin-devtools = { version = "2.0.1" }
|
||||||
tauri-plugin-mihomo = { git = "https://github.com/clash-verge-rev/tauri-plugin-mihomo" }
|
tauri-plugin-mihomo = { git = "https://github.com/clash-verge-rev/tauri-plugin-mihomo", branch = "revert" }
|
||||||
clash_verge_logger = { git = "https://github.com/clash-verge-rev/clash-verge-logger" }
|
clash_verge_logger = { git = "https://github.com/clash-verge-rev/clash-verge-logger" }
|
||||||
async-trait = "0.1.89"
|
async-trait = "0.1.89"
|
||||||
clash_verge_service_ipc = { version = "2.3.0", features = [
|
clash_verge_service_ipc = { version = "2.3.0", features = [
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ use parking_lot::Mutex;
|
|||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tauri::async_runtime::JoinHandle;
|
use tauri::async_runtime::JoinHandle;
|
||||||
use tauri_plugin_mihomo::models::WebSocketConnectionId;
|
use tauri_plugin_mihomo::models::ConnectionId;
|
||||||
|
|
||||||
/// 托盘速率流异常后的重连间隔。
|
/// 托盘速率流异常后的重连间隔。
|
||||||
const TRAY_SPEED_RETRY_DELAY: Duration = Duration::from_secs(1);
|
const TRAY_SPEED_RETRY_DELAY: Duration = Duration::from_secs(1);
|
||||||
@@ -19,7 +19,7 @@ const TRAY_SPEED_STALE_TIMEOUT: Duration = Duration::from_secs(5);
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct TraySpeedController {
|
pub struct TraySpeedController {
|
||||||
speed_task: Arc<Mutex<Option<JoinHandle<()>>>>,
|
speed_task: Arc<Mutex<Option<JoinHandle<()>>>>,
|
||||||
speed_connection_id: Arc<Mutex<Option<WebSocketConnectionId>>>,
|
speed_connection_id: Arc<Mutex<Option<ConnectionId>>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for TraySpeedController {
|
impl Default for TraySpeedController {
|
||||||
@@ -162,19 +162,17 @@ impl TraySpeedController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn set_speed_connection_id(
|
fn set_speed_connection_id(
|
||||||
speed_connection_id: &Arc<Mutex<Option<WebSocketConnectionId>>>,
|
speed_connection_id: &Arc<Mutex<Option<ConnectionId>>>,
|
||||||
connection_id: Option<WebSocketConnectionId>,
|
connection_id: Option<ConnectionId>,
|
||||||
) {
|
) {
|
||||||
*speed_connection_id.lock() = connection_id;
|
*speed_connection_id.lock() = connection_id;
|
||||||
}
|
}
|
||||||
|
|
||||||
fn take_speed_connection_id(
|
fn take_speed_connection_id(speed_connection_id: &Arc<Mutex<Option<ConnectionId>>>) -> Option<ConnectionId> {
|
||||||
speed_connection_id: &Arc<Mutex<Option<WebSocketConnectionId>>>,
|
|
||||||
) -> Option<WebSocketConnectionId> {
|
|
||||||
speed_connection_id.lock().take()
|
speed_connection_id.lock().take()
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn disconnect_speed_connection(speed_connection_id: &Arc<Mutex<Option<WebSocketConnectionId>>>) {
|
async fn disconnect_speed_connection(speed_connection_id: &Arc<Mutex<Option<ConnectionId>>>) {
|
||||||
if let Some(connection_id) = Self::take_speed_connection_id(speed_connection_id) {
|
if let Some(connection_id) = Self::take_speed_connection_id(speed_connection_id) {
|
||||||
connections_stream::disconnect_connection(connection_id).await;
|
connections_stream::disconnect_connection(connection_id).await;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ use tauri::{AppHandle, Manager as _};
|
|||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
use tauri_plugin_autostart::MacosLauncher;
|
use tauri_plugin_autostart::MacosLauncher;
|
||||||
use tauri_plugin_deep_link::DeepLinkExt as _;
|
use tauri_plugin_deep_link::DeepLinkExt as _;
|
||||||
|
use tauri_plugin_mihomo::RejectPolicy;
|
||||||
|
|
||||||
pub static APP_HANDLE: OnceCell<AppHandle> = OnceCell::new();
|
pub static APP_HANDLE: OnceCell<AppHandle> = OnceCell::new();
|
||||||
/// Application initialization helper functions
|
/// Application initialization helper functions
|
||||||
@@ -58,6 +59,15 @@ mod app_init {
|
|||||||
tauri_plugin_mihomo::Builder::new()
|
tauri_plugin_mihomo::Builder::new()
|
||||||
.protocol(tauri_plugin_mihomo::models::Protocol::LocalSocket)
|
.protocol(tauri_plugin_mihomo::models::Protocol::LocalSocket)
|
||||||
.socket_path(crate::config::IClashTemp::guard_external_controller_ipc())
|
.socket_path(crate::config::IClashTemp::guard_external_controller_ipc())
|
||||||
|
.pool_config(
|
||||||
|
tauri_plugin_mihomo::IpcPoolConfigBuilder::new()
|
||||||
|
.min_connections(3)
|
||||||
|
.max_connections(32)
|
||||||
|
.idle_timeout(std::time::Duration::from_secs(60))
|
||||||
|
.health_check_interval(std::time::Duration::from_secs(60))
|
||||||
|
.reject_policy(RejectPolicy::Wait)
|
||||||
|
.build(),
|
||||||
|
)
|
||||||
.build(),
|
.build(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ use anyhow::Result;
|
|||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_json::Value;
|
use serde_json::Value;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use tauri_plugin_mihomo::models::{WebSocketConnectionId, WebSocketMessage};
|
use tauri_plugin_mihomo::models::{ConnectionId, WebSocketMessage};
|
||||||
use tokio::sync::mpsc;
|
use tokio::sync::mpsc;
|
||||||
use tokio::time::Instant;
|
use tokio::time::Instant;
|
||||||
|
|
||||||
@@ -39,7 +39,7 @@ enum InternalWsEvent<T> {
|
|||||||
/// Mihomo WebSocket 订阅句柄(通用事件流)。
|
/// Mihomo WebSocket 订阅句柄(通用事件流)。
|
||||||
pub struct MihomoWsEventStream<T> {
|
pub struct MihomoWsEventStream<T> {
|
||||||
/// 当前订阅连接 ID,用于主动断开。
|
/// 当前订阅连接 ID,用于主动断开。
|
||||||
pub connection_id: WebSocketConnectionId,
|
pub connection_id: ConnectionId,
|
||||||
/// 当前订阅消息接收器。
|
/// 当前订阅消息接收器。
|
||||||
receiver: mpsc::Receiver<InternalWsEvent<T>>,
|
receiver: mpsc::Receiver<InternalWsEvent<T>>,
|
||||||
/// 最近一次收到有效事件的时间戳。
|
/// 最近一次收到有效事件的时间戳。
|
||||||
@@ -162,7 +162,7 @@ impl<T> MihomoWsEventStream<T> {
|
|||||||
///
|
///
|
||||||
/// # Arguments
|
/// # Arguments
|
||||||
/// * `connection_id` - 目标连接 ID
|
/// * `connection_id` - 目标连接 ID
|
||||||
pub async fn disconnect_connection(connection_id: WebSocketConnectionId) {
|
pub async fn disconnect_connection(connection_id: ConnectionId) {
|
||||||
if let Err(err) = handle::Handle::mihomo()
|
if let Err(err) = handle::Handle::mihomo()
|
||||||
.await
|
.await
|
||||||
.disconnect(connection_id, Some(MIHOMO_WS_STREAM_CLOSE_CODE))
|
.disconnect(connection_id, Some(MIHOMO_WS_STREAM_CLOSE_CODE))
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "2.5.0",
|
"version": "2.5.1",
|
||||||
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
|
||||||
"bundle": {
|
"bundle": {
|
||||||
"active": true,
|
"active": true,
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { LoadingButton } from '@mui/lab'
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
Dialog,
|
Dialog,
|
||||||
@@ -66,9 +65,9 @@ export const BaseDialog: React.FC<Props> = ({
|
|||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{!disableOk && (
|
{!disableOk && (
|
||||||
<LoadingButton loading={loading} variant="contained" onClick={onOk}>
|
<Button loading={loading} variant="contained" onClick={onOk}>
|
||||||
{okBtn}
|
{okBtn}
|
||||||
</LoadingButton>
|
</Button>
|
||||||
)}
|
)}
|
||||||
</DialogActions>
|
</DialogActions>
|
||||||
)}
|
)}
|
||||||
|
|||||||
@@ -1,46 +0,0 @@
|
|||||||
import {
|
|
||||||
Button,
|
|
||||||
Dialog,
|
|
||||||
DialogActions,
|
|
||||||
DialogContent,
|
|
||||||
DialogTitle,
|
|
||||||
} from '@mui/material'
|
|
||||||
import { useEffect } from 'react'
|
|
||||||
import { useTranslation } from 'react-i18next'
|
|
||||||
|
|
||||||
interface Props {
|
|
||||||
open: boolean
|
|
||||||
title: string
|
|
||||||
message: string
|
|
||||||
onClose: () => void
|
|
||||||
onConfirm: () => void
|
|
||||||
}
|
|
||||||
|
|
||||||
export const ConfirmViewer = (props: Props) => {
|
|
||||||
const { open, title, message, onClose, onConfirm } = props
|
|
||||||
|
|
||||||
const { t } = useTranslation()
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!open) return
|
|
||||||
}, [open])
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Dialog open={open} onClose={onClose} maxWidth="xs" fullWidth>
|
|
||||||
<DialogTitle>{title}</DialogTitle>
|
|
||||||
|
|
||||||
<DialogContent sx={{ pb: 1, userSelect: 'text' }}>
|
|
||||||
{message}
|
|
||||||
</DialogContent>
|
|
||||||
|
|
||||||
<DialogActions>
|
|
||||||
<Button onClick={onClose} variant="outlined">
|
|
||||||
{t('shared.actions.cancel')}
|
|
||||||
</Button>
|
|
||||||
<Button onClick={onConfirm} variant="contained">
|
|
||||||
{t('shared.actions.confirm')}
|
|
||||||
</Button>
|
|
||||||
</DialogActions>
|
|
||||||
</Dialog>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -22,7 +22,7 @@ import dayjs from 'dayjs'
|
|||||||
import { useCallback, useEffect, useReducer, useRef, useState } from 'react'
|
import { useCallback, useEffect, useReducer, useRef, useState } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
import { ConfirmViewer } from '@/components/profile/confirm-viewer'
|
import { BaseDialog } from '@/components/base'
|
||||||
import { EditorViewer } from '@/components/profile/editor-viewer'
|
import { EditorViewer } from '@/components/profile/editor-viewer'
|
||||||
import { GroupsEditorViewer } from '@/components/profile/groups-editor-viewer'
|
import { GroupsEditorViewer } from '@/components/profile/groups-editor-viewer'
|
||||||
import { RulesEditorViewer } from '@/components/profile/rules-editor-viewer'
|
import { RulesEditorViewer } from '@/components/profile/rules-editor-viewer'
|
||||||
@@ -990,16 +990,23 @@ export const ProfileItem = (props: Props) => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<ConfirmViewer
|
<BaseDialog
|
||||||
title={t('profiles.modals.confirmDelete.title')}
|
title={t('profiles.modals.confirmDelete.title')}
|
||||||
message={t('profiles.modals.confirmDelete.message')}
|
|
||||||
open={confirmOpen}
|
open={confirmOpen}
|
||||||
|
okBtn={t('shared.actions.confirm')}
|
||||||
|
cancelBtn={t('shared.actions.cancel')}
|
||||||
|
contentSx={{ width: { xs: 320, sm: 420 }, userSelect: 'text' }}
|
||||||
|
onCancel={() => setConfirmOpen(false)}
|
||||||
onClose={() => setConfirmOpen(false)}
|
onClose={() => setConfirmOpen(false)}
|
||||||
onConfirm={() => {
|
onOk={() => {
|
||||||
onDelete()
|
onDelete()
|
||||||
setConfirmOpen(false)
|
setConfirmOpen(false)
|
||||||
}}
|
}}
|
||||||
/>
|
>
|
||||||
|
<Typography variant="body2" sx={{ wordBreak: 'break-word' }}>
|
||||||
|
{t('profiles.modals.confirmDelete.message')}
|
||||||
|
</Typography>
|
||||||
|
</BaseDialog>
|
||||||
{qrOpen && itemData.url && (
|
{qrOpen && itemData.url && (
|
||||||
<QrViewer
|
<QrViewer
|
||||||
open={true}
|
open={true}
|
||||||
|
|||||||
@@ -49,6 +49,11 @@ const DATE_FORMAT = 'YYYY-MM-DD_HH-mm-ss'
|
|||||||
const FILENAME_PATTERN = /\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}/
|
const FILENAME_PATTERN = /\d{4}-\d{2}-\d{2}_\d{2}-\d{2}-\d{2}/
|
||||||
|
|
||||||
type BackupSource = 'local' | 'webdav'
|
type BackupSource = 'local' | 'webdav'
|
||||||
|
type PendingConfirmation = {
|
||||||
|
action: 'delete' | 'restore'
|
||||||
|
filename: string
|
||||||
|
source: BackupSource
|
||||||
|
} | null
|
||||||
|
|
||||||
interface BackupHistoryViewerProps {
|
interface BackupHistoryViewerProps {
|
||||||
open: boolean
|
open: boolean
|
||||||
@@ -67,11 +72,6 @@ interface BackupRow {
|
|||||||
sort_value: number
|
sort_value: number
|
||||||
}
|
}
|
||||||
|
|
||||||
const confirmAsync = async (message: string) => {
|
|
||||||
const fn = window.confirm as (msg?: string) => boolean
|
|
||||||
return fn(message)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const BackupHistoryViewer = ({
|
export const BackupHistoryViewer = ({
|
||||||
open,
|
open,
|
||||||
source,
|
source,
|
||||||
@@ -86,6 +86,9 @@ export const BackupHistoryViewer = ({
|
|||||||
const [loading, setLoading] = useState(false)
|
const [loading, setLoading] = useState(false)
|
||||||
const [isRestoring, setIsRestoring] = useState(false)
|
const [isRestoring, setIsRestoring] = useState(false)
|
||||||
const [isRestarting, setIsRestarting] = useState(false)
|
const [isRestarting, setIsRestarting] = useState(false)
|
||||||
|
const [isConfirming, setIsConfirming] = useState(false)
|
||||||
|
const [pendingConfirmation, setPendingConfirmation] =
|
||||||
|
useState<PendingConfirmation>(null)
|
||||||
const isLocal = source === 'local'
|
const isLocal = source === 'local'
|
||||||
const isWebDavConfigured = Boolean(
|
const isWebDavConfigured = Boolean(
|
||||||
verge?.webdav_url && verge?.webdav_username && verge?.webdav_password,
|
verge?.webdav_url && verge?.webdav_username && verge?.webdav_password,
|
||||||
@@ -94,7 +97,7 @@ export const BackupHistoryViewer = ({
|
|||||||
const webdavStatus = getWebdavStatus(webdavSignature)
|
const webdavStatus = getWebdavStatus(webdavSignature)
|
||||||
const shouldSkipWebDav = !isLocal && !isWebDavConfigured
|
const shouldSkipWebDav = !isLocal && !isWebDavConfigured
|
||||||
const pageSize = 8
|
const pageSize = 8
|
||||||
const isBusy = loading || isRestoring || isRestarting
|
const isBusy = loading || isRestoring || isRestarting || isConfirming
|
||||||
|
|
||||||
const buildRow = useCallback(
|
const buildRow = useCallback(
|
||||||
(item: ILocalBackupFile | IWebDavFile): BackupRow | null => {
|
(item: ILocalBackupFile | IWebDavFile): BackupRow | null => {
|
||||||
@@ -204,45 +207,54 @@ export const BackupHistoryViewer = ({
|
|||||||
})
|
})
|
||||||
}, [isLocal, rows, shouldSkipWebDav, t, total, webdavStatus])
|
}, [isLocal, rows, shouldSkipWebDav, t, total, webdavStatus])
|
||||||
|
|
||||||
const handleDelete = useLockFn(async (filename: string) => {
|
const handleDelete = (filename: string) => {
|
||||||
if (isRestarting) return
|
if (isRestarting) return
|
||||||
if (
|
setPendingConfirmation({ action: 'delete', filename, source })
|
||||||
!(await confirmAsync(t('settings.modals.backup.messages.confirmDelete')))
|
}
|
||||||
)
|
|
||||||
return
|
|
||||||
if (isLocal) {
|
|
||||||
await deleteLocalBackup(filename)
|
|
||||||
} else {
|
|
||||||
await deleteWebdavBackup(filename)
|
|
||||||
}
|
|
||||||
await fetchRows()
|
|
||||||
})
|
|
||||||
|
|
||||||
const handleRestore = useLockFn(async (filename: string) => {
|
const handleRestore = (filename: string) => {
|
||||||
if (isRestoring || isRestarting) return
|
if (isRestoring || isRestarting) return
|
||||||
if (
|
setPendingConfirmation({ action: 'restore', filename, source })
|
||||||
!(await confirmAsync(t('settings.modals.backup.messages.confirmRestore')))
|
}
|
||||||
)
|
|
||||||
return
|
const handleConfirmAction = useLockFn(async () => {
|
||||||
setIsRestoring(true)
|
if (!pendingConfirmation) return
|
||||||
|
const { action, filename, source: actionSource } = pendingConfirmation
|
||||||
|
const actionIsLocal = actionSource === 'local'
|
||||||
|
setIsConfirming(true)
|
||||||
|
if (action === 'restore') {
|
||||||
|
setIsRestoring(true)
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
if (isLocal) {
|
if (action === 'delete') {
|
||||||
await restoreLocalBackup(filename)
|
if (actionIsLocal) {
|
||||||
|
await deleteLocalBackup(filename)
|
||||||
|
} else {
|
||||||
|
await deleteWebdavBackup(filename)
|
||||||
|
}
|
||||||
|
setPendingConfirmation(null)
|
||||||
|
await fetchRows()
|
||||||
} else {
|
} else {
|
||||||
await restoreWebDavBackup(filename)
|
if (actionIsLocal) {
|
||||||
|
await restoreLocalBackup(filename)
|
||||||
|
} else {
|
||||||
|
await restoreWebDavBackup(filename)
|
||||||
|
}
|
||||||
|
setPendingConfirmation(null)
|
||||||
|
showNotice.success('settings.modals.backup.messages.restoreSuccess')
|
||||||
|
setIsRestarting(true)
|
||||||
|
window.setTimeout(() => {
|
||||||
|
void restartApp().catch((err: unknown) => {
|
||||||
|
setIsRestarting(false)
|
||||||
|
showNotice.error(err)
|
||||||
|
})
|
||||||
|
}, 1000)
|
||||||
}
|
}
|
||||||
showNotice.success('settings.modals.backup.messages.restoreSuccess')
|
|
||||||
setIsRestarting(true)
|
|
||||||
window.setTimeout(() => {
|
|
||||||
void restartApp().catch((err: unknown) => {
|
|
||||||
setIsRestarting(false)
|
|
||||||
showNotice.error(err)
|
|
||||||
})
|
|
||||||
}, 1000)
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error(error)
|
console.error(error)
|
||||||
showNotice.error(error)
|
showNotice.error(error)
|
||||||
} finally {
|
} finally {
|
||||||
|
setIsConfirming(false)
|
||||||
setIsRestoring(false)
|
setIsRestoring(false)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
@@ -267,6 +279,20 @@ export const BackupHistoryViewer = ({
|
|||||||
void fetchRows({ force: true })
|
void fetchRows({ force: true })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const closeConfirmDialog = () => {
|
||||||
|
if (isConfirming) return
|
||||||
|
setPendingConfirmation(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
const confirmTitle =
|
||||||
|
pendingConfirmation?.action === 'delete'
|
||||||
|
? t('settings.modals.backup.actions.deleteBackup')
|
||||||
|
: t('settings.modals.backup.actions.restoreBackup')
|
||||||
|
const confirmMessage =
|
||||||
|
pendingConfirmation?.action === 'delete'
|
||||||
|
? t('settings.modals.backup.messages.confirmDelete')
|
||||||
|
: t('settings.modals.backup.messages.confirmRestore')
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<BaseDialog
|
<BaseDialog
|
||||||
open={open}
|
open={open}
|
||||||
@@ -423,6 +449,30 @@ export const BackupHistoryViewer = ({
|
|||||||
)}
|
)}
|
||||||
</Stack>
|
</Stack>
|
||||||
</Box>
|
</Box>
|
||||||
|
<BaseDialog
|
||||||
|
open={pendingConfirmation !== null}
|
||||||
|
title={confirmTitle}
|
||||||
|
okBtn={t('shared.actions.confirm')}
|
||||||
|
cancelBtn={t('shared.actions.cancel')}
|
||||||
|
contentSx={{ width: { xs: 320, sm: 420 } }}
|
||||||
|
loading={isConfirming}
|
||||||
|
onCancel={closeConfirmDialog}
|
||||||
|
onClose={closeConfirmDialog}
|
||||||
|
onOk={handleConfirmAction}
|
||||||
|
>
|
||||||
|
<Typography variant="body2" sx={{ wordBreak: 'break-word' }}>
|
||||||
|
{confirmMessage}
|
||||||
|
</Typography>
|
||||||
|
{pendingConfirmation?.filename && (
|
||||||
|
<Typography
|
||||||
|
variant="caption"
|
||||||
|
color="text.secondary"
|
||||||
|
sx={{ display: 'block', mt: 1, wordBreak: 'break-all' }}
|
||||||
|
>
|
||||||
|
{pendingConfirmation.filename}
|
||||||
|
</Typography>
|
||||||
|
)}
|
||||||
|
</BaseDialog>
|
||||||
</BaseDialog>
|
</BaseDialog>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,3 @@
|
|||||||
import { LoadingButton } from '@mui/lab'
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
List,
|
List,
|
||||||
@@ -156,7 +155,7 @@ export function BackupViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
|||||||
title: t('settings.modals.backup.tabs.local'),
|
title: t('settings.modals.backup.tabs.local'),
|
||||||
description: t('settings.modals.backup.manual.local'),
|
description: t('settings.modals.backup.manual.local'),
|
||||||
actions: [
|
actions: [
|
||||||
<LoadingButton
|
<Button
|
||||||
key="backup"
|
key="backup"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
size="small"
|
size="small"
|
||||||
@@ -165,7 +164,7 @@ export function BackupViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
|||||||
onClick={() => handleBackup('local')}
|
onClick={() => handleBackup('local')}
|
||||||
>
|
>
|
||||||
{t('settings.modals.backup.actions.backup')}
|
{t('settings.modals.backup.actions.backup')}
|
||||||
</LoadingButton>,
|
</Button>,
|
||||||
<Button
|
<Button
|
||||||
key="history"
|
key="history"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
@@ -175,7 +174,7 @@ export function BackupViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
|||||||
>
|
>
|
||||||
{t('settings.modals.backup.actions.viewHistory')}
|
{t('settings.modals.backup.actions.viewHistory')}
|
||||||
</Button>,
|
</Button>,
|
||||||
<LoadingButton
|
<Button
|
||||||
key="import"
|
key="import"
|
||||||
variant="text"
|
variant="text"
|
||||||
size="small"
|
size="small"
|
||||||
@@ -184,7 +183,7 @@ export function BackupViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
|||||||
onClick={() => handleImport()}
|
onClick={() => handleImport()}
|
||||||
>
|
>
|
||||||
{t('settings.modals.backup.actions.importBackup')}
|
{t('settings.modals.backup.actions.importBackup')}
|
||||||
</LoadingButton>,
|
</Button>,
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -192,7 +191,7 @@ export function BackupViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
|||||||
title: t('settings.modals.backup.tabs.webdav'),
|
title: t('settings.modals.backup.tabs.webdav'),
|
||||||
description: t('settings.modals.backup.manual.webdav'),
|
description: t('settings.modals.backup.manual.webdav'),
|
||||||
actions: [
|
actions: [
|
||||||
<LoadingButton
|
<Button
|
||||||
key="backup"
|
key="backup"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
size="small"
|
size="small"
|
||||||
@@ -200,7 +199,7 @@ export function BackupViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
|||||||
onClick={() => handleBackup('webdav')}
|
onClick={() => handleBackup('webdav')}
|
||||||
>
|
>
|
||||||
{t('settings.modals.backup.actions.backup')}
|
{t('settings.modals.backup.actions.backup')}
|
||||||
</LoadingButton>,
|
</Button>,
|
||||||
<Button
|
<Button
|
||||||
key="history"
|
key="history"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
|
|||||||
@@ -2,9 +2,9 @@ import {
|
|||||||
RestartAltRounded,
|
RestartAltRounded,
|
||||||
SwitchAccessShortcutRounded,
|
SwitchAccessShortcutRounded,
|
||||||
} from '@mui/icons-material'
|
} from '@mui/icons-material'
|
||||||
import { LoadingButton } from '@mui/lab'
|
|
||||||
import {
|
import {
|
||||||
Box,
|
Box,
|
||||||
|
Button,
|
||||||
Chip,
|
Chip,
|
||||||
CircularProgress,
|
CircularProgress,
|
||||||
List,
|
List,
|
||||||
@@ -120,7 +120,7 @@ export function ClashCoreViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
|||||||
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
<Box sx={{ display: 'flex', justifyContent: 'space-between' }}>
|
||||||
{t('settings.sections.clash.form.fields.clashCore')}
|
{t('settings.sections.clash.form.fields.clashCore')}
|
||||||
<Box>
|
<Box>
|
||||||
<LoadingButton
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
size="small"
|
size="small"
|
||||||
startIcon={<SwitchAccessShortcutRounded />}
|
startIcon={<SwitchAccessShortcutRounded />}
|
||||||
@@ -131,8 +131,8 @@ export function ClashCoreViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
|||||||
onClick={onUpgrade}
|
onClick={onUpgrade}
|
||||||
>
|
>
|
||||||
{t('shared.actions.upgrade')}
|
{t('shared.actions.upgrade')}
|
||||||
</LoadingButton>
|
</Button>
|
||||||
<LoadingButton
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
size="small"
|
size="small"
|
||||||
startIcon={<RestartAltRounded />}
|
startIcon={<RestartAltRounded />}
|
||||||
@@ -142,7 +142,7 @@ export function ClashCoreViewer({ ref }: { ref?: Ref<DialogRef> }) {
|
|||||||
onClick={onRestart}
|
onClick={onRestart}
|
||||||
>
|
>
|
||||||
{t('shared.actions.restart')}
|
{t('shared.actions.restart')}
|
||||||
</LoadingButton>
|
</Button>
|
||||||
</Box>
|
</Box>
|
||||||
</Box>
|
</Box>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import {
|
|||||||
RefreshRounded,
|
RefreshRounded,
|
||||||
TextSnippetOutlined,
|
TextSnippetOutlined,
|
||||||
} from '@mui/icons-material'
|
} from '@mui/icons-material'
|
||||||
import { LoadingButton } from '@mui/lab'
|
|
||||||
import { Box, Button, Divider, Grid, IconButton, Stack } from '@mui/material'
|
import { Box, Button, Divider, Grid, IconButton, Stack } from '@mui/material'
|
||||||
import { useQuery } from '@tanstack/react-query'
|
import { useQuery } from '@tanstack/react-query'
|
||||||
import { listen, TauriEvent } from '@tauri-apps/api/event'
|
import { listen, TauriEvent } from '@tauri-apps/api/event'
|
||||||
@@ -970,7 +969,7 @@ const ProfilePage = () => {
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<LoadingButton
|
<Button
|
||||||
disabled={!url || disabled}
|
disabled={!url || disabled}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
variant="contained"
|
variant="contained"
|
||||||
@@ -979,7 +978,7 @@ const ProfilePage = () => {
|
|||||||
onClick={onImport}
|
onClick={onImport}
|
||||||
>
|
>
|
||||||
{t('profiles.page.actions.import')}
|
{t('profiles.page.actions.import')}
|
||||||
</LoadingButton>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="contained"
|
variant="contained"
|
||||||
size="small"
|
size="small"
|
||||||
|
|||||||
Reference in New Issue
Block a user