chore: update

This commit is contained in:
huzibaca 2024-09-02 19:33:17 +08:00
parent d45929c604
commit 507d52bcb9
No known key found for this signature in database
GPG Key ID: D4364EE4851DC302
42 changed files with 16683 additions and 2013 deletions

View File

@ -28,7 +28,15 @@
"@mui/lab": "5.0.0-alpha.149", "@mui/lab": "5.0.0-alpha.149",
"@mui/material": "^5.16.0", "@mui/material": "^5.16.0",
"@mui/x-data-grid": "^7.9.0", "@mui/x-data-grid": "^7.9.0",
"@tauri-apps/api": "^1.6.0", "@tauri-apps/api": "2.0.0-rc.4",
"@tauri-apps/plugin-clipboard-manager": "2.0.0-rc.0",
"@tauri-apps/plugin-dialog": "^2.0.0-rc",
"@tauri-apps/plugin-fs": "^2.0.0-rc",
"@tauri-apps/plugin-global-shortcut": "^2.0.0-rc",
"@tauri-apps/plugin-notification": "^2.0.0-rc",
"@tauri-apps/plugin-process": "^2.0.0-rc",
"@tauri-apps/plugin-shell": "^2.0.0-rc",
"@tauri-apps/plugin-updater": "^2.0.0-rc",
"@types/json-schema": "^7.0.15", "@types/json-schema": "^7.0.15",
"ahooks": "^3.8.0", "ahooks": "^3.8.0",
"axios": "^1.7.2", "axios": "^1.7.2",
@ -60,7 +68,7 @@
}, },
"devDependencies": { "devDependencies": {
"@actions/github": "^5.1.1", "@actions/github": "^5.1.1",
"@tauri-apps/cli": "^1.6.0", "@tauri-apps/cli": "2.0.0-rc.8",
"@types/fs-extra": "^9.0.13", "@types/fs-extra": "^9.0.13",
"@types/js-cookie": "^3.0.6", "@types/js-cookie": "^3.0.6",
"@types/js-yaml": "^4.0.9", "@types/js-yaml": "^4.0.9",

View File

@ -38,8 +38,32 @@ importers:
specifier: ^7.9.0 specifier: ^7.9.0
version: 7.9.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/material@5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) version: 7.9.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/material@5.16.0(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.11.5(@emotion/react@11.11.4(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
"@tauri-apps/api": "@tauri-apps/api":
specifier: ^1.6.0 specifier: 2.0.0-rc.4
version: 1.6.0 version: 2.0.0-rc.4
"@tauri-apps/plugin-clipboard-manager":
specifier: 2.0.0-rc.0
version: 2.0.0-rc.0
"@tauri-apps/plugin-dialog":
specifier: ^2.0.0-rc
version: 2.0.0-rc.0
"@tauri-apps/plugin-fs":
specifier: ^2.0.0-rc
version: 2.0.0-rc.1
"@tauri-apps/plugin-global-shortcut":
specifier: ^2.0.0-rc
version: 2.0.0-rc.0
"@tauri-apps/plugin-notification":
specifier: ^2.0.0-rc
version: 2.0.0-rc.0
"@tauri-apps/plugin-process":
specifier: ^2.0.0-rc
version: 2.0.0-rc.0
"@tauri-apps/plugin-shell":
specifier: ^2.0.0-rc
version: 2.0.0-rc.0
"@tauri-apps/plugin-updater":
specifier: ^2.0.0-rc
version: 2.0.0-rc.0
"@types/json-schema": "@types/json-schema":
specifier: ^7.0.15 specifier: ^7.0.15
version: 7.0.15 version: 7.0.15
@ -129,8 +153,8 @@ importers:
specifier: ^5.1.1 specifier: ^5.1.1
version: 5.1.1 version: 5.1.1
"@tauri-apps/cli": "@tauri-apps/cli":
specifier: ^1.6.0 specifier: 2.0.0-rc.8
version: 1.6.0 version: 2.0.0-rc.8
"@types/fs-extra": "@types/fs-extra":
specifier: ^9.0.13 specifier: ^9.0.13
version: 9.0.13 version: 9.0.13
@ -2076,111 +2100,158 @@ packages:
peerDependencies: peerDependencies:
"@svgr/core": "*" "@svgr/core": "*"
"@tauri-apps/api@1.6.0": "@tauri-apps/api@2.0.0-rc.4":
resolution: resolution:
{ {
integrity: sha512-rqI++FWClU5I2UBp4HXFvl+sBWkdigBkxnpJDQUWttNyG7IZP4FwQGhTNL5EOw0vI8i6eSAJ5frLqO7n7jbJdg==, integrity: sha512-UNiIhhKG08j4ooss2oEEVexffmWkgkYlC2M3GcX3VPtNsqFgVNL8Mcw/4Y7rO9M9S+ffAMnLOF5ypzyuyb8tyg==,
} }
engines: { node: ">= 14.6.0", npm: ">= 6.6.0", yarn: ">= 1.19.1" }
"@tauri-apps/cli-darwin-arm64@1.6.0": "@tauri-apps/cli-darwin-arm64@2.0.0-rc.8":
resolution: resolution:
{ {
integrity: sha512-SNRwUD9nqGxY47mbY1CGTt/jqyQOU7Ps7Mx/mpgahL0FVUDiCEY/5L9QfEPPhEgccgcelEVn7i6aQHIkHyUtCA==, integrity: sha512-XDjFLfCz7gOsKg9qFyK7S9EbrdcLhWE2bSTvHDpnFdMc+DcjMR4O3HuCO7SrPmmFUf86OknYFiuMPEUoHf0Fiw==,
} }
engines: { node: ">= 10" } engines: { node: ">= 10" }
cpu: [arm64] cpu: [arm64]
os: [darwin] os: [darwin]
"@tauri-apps/cli-darwin-x64@1.6.0": "@tauri-apps/cli-darwin-x64@2.0.0-rc.8":
resolution: resolution:
{ {
integrity: sha512-g2/uDR/eeH2arvuawA4WwaEOqv/7jDO/ZLNI3JlBjP5Pk8GGb3Kdy0ro1xQzF94mtk2mOnOXa4dMgAet4sUJ1A==, integrity: sha512-vYuITao7qq45jzTXRFdAcB+rVBULmofksWsKzoi+cgI8R0kcfB3bts+FRBdV3j+fkbpBQKpriW6Y6gdOzbf6Ow==,
} }
engines: { node: ">= 10" } engines: { node: ">= 10" }
cpu: [x64] cpu: [x64]
os: [darwin] os: [darwin]
"@tauri-apps/cli-linux-arm-gnueabihf@1.6.0": "@tauri-apps/cli-linux-arm-gnueabihf@2.0.0-rc.8":
resolution: resolution:
{ {
integrity: sha512-EVwf4oRkQyG8BpSrk0gqO7oA0sDM2MdNDtJpMfleYFEgCxLIOGZKNqaOW3M7U+0Y4qikmG3TtRK+ngc8Ymtrjg==, integrity: sha512-iW1gJyRBvwxCeBEu+ain18v8FtFBhrsqepzuUyOy7hZJWUMPdKHJhbFVz6xp3RGZf5ycsLUr3ZhqMB3vKtv7iQ==,
} }
engines: { node: ">= 10" } engines: { node: ">= 10" }
cpu: [arm] cpu: [arm]
os: [linux] os: [linux]
"@tauri-apps/cli-linux-arm64-gnu@1.6.0": "@tauri-apps/cli-linux-arm64-gnu@2.0.0-rc.8":
resolution: resolution:
{ {
integrity: sha512-YdpY17cAySrhK9dX4BUVEmhAxE2o+6skIEFg8iN/xrDwRxhaNPI9I80YXPatUTX54Kx55T5++25VJG9+3iw83A==, integrity: sha512-k1HQHpu59B8JteXALCSdCQBUVb+ytsg1u/QEWPzCm7mFhzr7jqdfBjQVAKt0T5rdQQTcNtK341oB7izuXmJE2A==,
} }
engines: { node: ">= 10" } engines: { node: ">= 10" }
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
"@tauri-apps/cli-linux-arm64-musl@1.6.0": "@tauri-apps/cli-linux-arm64-musl@2.0.0-rc.8":
resolution: resolution:
{ {
integrity: sha512-4U628tuf2U8pMr4tIBJhEkrFwt+46dwhXrDlpdyWSZtnop5RJAVKHODm0KbWns4xGKfTW1F3r6sSv+2ZxLcISA==, integrity: sha512-tMotP7J/dMqCL8zPCwtMwjeO3SK1yxU9bUMZ3mwdAtf+3xr2d2hpmGo4KXk5r2jQee9+dIC3IBXexFhrVJGRHA==,
} }
engines: { node: ">= 10" } engines: { node: ">= 10" }
cpu: [arm64] cpu: [arm64]
os: [linux] os: [linux]
"@tauri-apps/cli-linux-x64-gnu@1.6.0": "@tauri-apps/cli-linux-x64-gnu@2.0.0-rc.8":
resolution: resolution:
{ {
integrity: sha512-AKRzp76fVUaJyXj5KRJT9bJyhwZyUnRQU0RqIRqOtZCT5yr6qGP8rjtQ7YhCIzWrseBlOllc3Qvbgw3Yl0VQcA==, integrity: sha512-AnLghADQgt4oyp4rQyjpKfGpdAqofd+SRO9kht5d4c0RSViqwHBjIxrCt9FDhBpBwwkC6T6NNuQqaofZSnOH6Q==,
} }
engines: { node: ">= 10" } engines: { node: ">= 10" }
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
"@tauri-apps/cli-linux-x64-musl@1.6.0": "@tauri-apps/cli-linux-x64-musl@2.0.0-rc.8":
resolution: resolution:
{ {
integrity: sha512-0edIdq6aMBTaRMIXddHfyAFL361JqulLLd2Wi2aoOie7DkQ2MYh6gv3hA7NB9gqFwNIGE+xtJ4BkXIP2tSGPlg==, integrity: sha512-jv/uiXI2K41/XMA2vnKMcpdv1tKvez70HHvBoIwHhelmXV5V6Tpu5fTMEAXbA6WbJNfcFRi0kd55YRBOS0DZcw==,
} }
engines: { node: ">= 10" } engines: { node: ">= 10" }
cpu: [x64] cpu: [x64]
os: [linux] os: [linux]
"@tauri-apps/cli-win32-arm64-msvc@1.6.0": "@tauri-apps/cli-win32-arm64-msvc@2.0.0-rc.8":
resolution: resolution:
{ {
integrity: sha512-QwWpWk4ubcwJ1rljsRAmINgB2AwkyzZhpYbalA+MmzyYMREcdXWGkyixWbRZgqc6fEWEBmq5UG73qz5eBJiIKg==, integrity: sha512-wctgwSfuyJ0lXBzQSzGq5TAy2m9VZichZPPJ7GitYzdHbqIRLmvxM1abRen7HikP1lpjdmjl96/kWQOebEV0qw==,
} }
engines: { node: ">= 10" } engines: { node: ">= 10" }
cpu: [arm64] cpu: [arm64]
os: [win32] os: [win32]
"@tauri-apps/cli-win32-ia32-msvc@1.6.0": "@tauri-apps/cli-win32-ia32-msvc@2.0.0-rc.8":
resolution: resolution:
{ {
integrity: sha512-Vtw0yxO9+aEFuhuxQ57ALG43tjECopRimRuKGbtZYDCriB/ty5TrT3QWMdy0dxBkpDTu3Rqsz30sbDzw6tlP3Q==, integrity: sha512-BBjoAezNpHscGhBICHrt+X+IyJIh9jYnaEIGvbCIMcGB6sSDxvtttw4ef9+4jNh3JixotOR2mwIudTc1H9HffQ==,
} }
engines: { node: ">= 10" } engines: { node: ">= 10" }
cpu: [ia32] cpu: [ia32]
os: [win32] os: [win32]
"@tauri-apps/cli-win32-x64-msvc@1.6.0": "@tauri-apps/cli-win32-x64-msvc@2.0.0-rc.8":
resolution: resolution:
{ {
integrity: sha512-h54FHOvGi7+LIfRchzgZYSCHB1HDlP599vWXQQJ/XnwJY+6Rwr2E5bOe/EhqoG8rbGkfK0xX3KPAvXPbUlmggg==, integrity: sha512-1pgDw2woeyDMvXctmqc53LBxsaAYqiRNUVgwvzlmzJDbUHv4+Pj4B1OGKQh1YKlsmks8LK3mK9uSbk+k1OLZhw==,
} }
engines: { node: ">= 10" } engines: { node: ">= 10" }
cpu: [x64] cpu: [x64]
os: [win32] os: [win32]
"@tauri-apps/cli@1.6.0": "@tauri-apps/cli@2.0.0-rc.8":
resolution: resolution:
{ {
integrity: sha512-DBBpBl6GhTzm8ImMbKkfaZ4fDTykWrC7Q5OXP4XqD91recmDEn2LExuvuiiS3HYe7uP8Eb5B9NPHhqJb+Zo7qQ==, integrity: sha512-hB6Pa0IaJs/As3Hc5IOiiK4Ilh9jspEA+2uPURCRQg4BkzQWwQzNdGQ4gjAzFNFAWPzy/4uuIXdnd1a+jC4wLA==,
} }
engines: { node: ">= 10" } engines: { node: ">= 10" }
hasBin: true hasBin: true
"@tauri-apps/plugin-clipboard-manager@2.0.0-rc.0":
resolution:
{
integrity: sha512-2fS3wbRQEtorkk3Np2msJUeKCXRqLQ9sSo2FzlFdUPYNzThsu43uWCF55McGLAfltNOvXQIcQLUBf05jbBL/5w==,
}
"@tauri-apps/plugin-dialog@2.0.0-rc.0":
resolution:
{
integrity: sha512-DPOXYe8SQ6Radk/67EOdaomlxL7oF99JO/ZUaPp1IBEs3Wro7lhlz63CfdKIBfKIZTLJLzP1R7/EiPL/GTA3Bg==,
}
"@tauri-apps/plugin-fs@2.0.0-rc.1":
resolution:
{
integrity: sha512-r6N5dqBNXy9YAK9XbiEqAq3ZKBIN+TWevE7ZFmSRdvdSB1urrLweYu4wxycY2kDaDPzSeeOIJhCmqdNrFT9OSA==,
}
"@tauri-apps/plugin-global-shortcut@2.0.0-rc.0":
resolution:
{
integrity: sha512-rcefV4AG+vnbTtGc07hDn4Uj8u//NpuqHVg1CShACyiCS0q2sZrDVC3qPN3RGhzQyeIOPq4bY1kQRHODffKXZA==,
}
"@tauri-apps/plugin-notification@2.0.0-rc.0":
resolution:
{
integrity: sha512-0qsT/kvxQ6Ky4g6eQ4SUiHXzM4szTVc6thjz9vnGPYaApLoZrCJ9GdG+vEqeB+cT2dvE+wmxUFETh3ZXYVw8UA==,
}
"@tauri-apps/plugin-process@2.0.0-rc.0":
resolution:
{
integrity: sha512-Z12D/kmQzG1vCVf+jLXPhPDUA0pEjFrsg4p0uwO2sotVLM9287IuTM+aIz9cuAYOxFLKcsnDG7amSCL9IfA1gw==,
}
"@tauri-apps/plugin-shell@2.0.0-rc.0":
resolution:
{
integrity: sha512-bhUcQcrqZoK8H1DFXapr5r1Z75oh6Kd5Tltz97XpZFLREEqp+KhN2Fvyh8r/fKAyenYsTYUIsDsyGdjdueuF9g==,
}
"@tauri-apps/plugin-updater@2.0.0-rc.0":
resolution:
{
integrity: sha512-EKajf/sBpFif0cwXhTo3BmNvTZ2t2DDLRyhA8FFKugZNoOeqU97bHhPT5DIqMUPRE1tyDk9o7sXm8dKf7oz+EA==,
}
"@types/babel__core@7.20.5": "@types/babel__core@7.20.5":
resolution: resolution:
{ {
@ -5959,50 +6030,82 @@ snapshots:
transitivePeerDependencies: transitivePeerDependencies:
- supports-color - supports-color
"@tauri-apps/api@1.6.0": {} "@tauri-apps/api@2.0.0-rc.4": {}
"@tauri-apps/cli-darwin-arm64@1.6.0": "@tauri-apps/cli-darwin-arm64@2.0.0-rc.8":
optional: true optional: true
"@tauri-apps/cli-darwin-x64@1.6.0": "@tauri-apps/cli-darwin-x64@2.0.0-rc.8":
optional: true optional: true
"@tauri-apps/cli-linux-arm-gnueabihf@1.6.0": "@tauri-apps/cli-linux-arm-gnueabihf@2.0.0-rc.8":
optional: true optional: true
"@tauri-apps/cli-linux-arm64-gnu@1.6.0": "@tauri-apps/cli-linux-arm64-gnu@2.0.0-rc.8":
optional: true optional: true
"@tauri-apps/cli-linux-arm64-musl@1.6.0": "@tauri-apps/cli-linux-arm64-musl@2.0.0-rc.8":
optional: true optional: true
"@tauri-apps/cli-linux-x64-gnu@1.6.0": "@tauri-apps/cli-linux-x64-gnu@2.0.0-rc.8":
optional: true optional: true
"@tauri-apps/cli-linux-x64-musl@1.6.0": "@tauri-apps/cli-linux-x64-musl@2.0.0-rc.8":
optional: true optional: true
"@tauri-apps/cli-win32-arm64-msvc@1.6.0": "@tauri-apps/cli-win32-arm64-msvc@2.0.0-rc.8":
optional: true optional: true
"@tauri-apps/cli-win32-ia32-msvc@1.6.0": "@tauri-apps/cli-win32-ia32-msvc@2.0.0-rc.8":
optional: true optional: true
"@tauri-apps/cli-win32-x64-msvc@1.6.0": "@tauri-apps/cli-win32-x64-msvc@2.0.0-rc.8":
optional: true optional: true
"@tauri-apps/cli@1.6.0": "@tauri-apps/cli@2.0.0-rc.8":
optionalDependencies: optionalDependencies:
"@tauri-apps/cli-darwin-arm64": 1.6.0 "@tauri-apps/cli-darwin-arm64": 2.0.0-rc.8
"@tauri-apps/cli-darwin-x64": 1.6.0 "@tauri-apps/cli-darwin-x64": 2.0.0-rc.8
"@tauri-apps/cli-linux-arm-gnueabihf": 1.6.0 "@tauri-apps/cli-linux-arm-gnueabihf": 2.0.0-rc.8
"@tauri-apps/cli-linux-arm64-gnu": 1.6.0 "@tauri-apps/cli-linux-arm64-gnu": 2.0.0-rc.8
"@tauri-apps/cli-linux-arm64-musl": 1.6.0 "@tauri-apps/cli-linux-arm64-musl": 2.0.0-rc.8
"@tauri-apps/cli-linux-x64-gnu": 1.6.0 "@tauri-apps/cli-linux-x64-gnu": 2.0.0-rc.8
"@tauri-apps/cli-linux-x64-musl": 1.6.0 "@tauri-apps/cli-linux-x64-musl": 2.0.0-rc.8
"@tauri-apps/cli-win32-arm64-msvc": 1.6.0 "@tauri-apps/cli-win32-arm64-msvc": 2.0.0-rc.8
"@tauri-apps/cli-win32-ia32-msvc": 1.6.0 "@tauri-apps/cli-win32-ia32-msvc": 2.0.0-rc.8
"@tauri-apps/cli-win32-x64-msvc": 1.6.0 "@tauri-apps/cli-win32-x64-msvc": 2.0.0-rc.8
"@tauri-apps/plugin-clipboard-manager@2.0.0-rc.0":
dependencies:
"@tauri-apps/api": 2.0.0-rc.4
"@tauri-apps/plugin-dialog@2.0.0-rc.0":
dependencies:
"@tauri-apps/api": 2.0.0-rc.4
"@tauri-apps/plugin-fs@2.0.0-rc.1":
dependencies:
"@tauri-apps/api": 2.0.0-rc.4
"@tauri-apps/plugin-global-shortcut@2.0.0-rc.0":
dependencies:
"@tauri-apps/api": 2.0.0-rc.4
"@tauri-apps/plugin-notification@2.0.0-rc.0":
dependencies:
"@tauri-apps/api": 2.0.0-rc.4
"@tauri-apps/plugin-process@2.0.0-rc.0":
dependencies:
"@tauri-apps/api": 2.0.0-rc.4
"@tauri-apps/plugin-shell@2.0.0-rc.0":
dependencies:
"@tauri-apps/api": 2.0.0-rc.4
"@tauri-apps/plugin-updater@2.0.0-rc.0":
dependencies:
"@tauri-apps/api": 2.0.0-rc.4
"@types/babel__core@7.20.5": "@types/babel__core@7.20.5":
dependencies: dependencies:

2819
src-tauri/Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@ edition = "2021"
build = "build.rs" build = "build.rs"
[build-dependencies] [build-dependencies]
tauri-build = { version="1", features = [] } tauri-build = { version= "2.0.0-rc", features = [] }
[dependencies] [dependencies]
warp = "0.3" warp = "0.3"
@ -32,13 +32,19 @@ delay_timer = "0.11"
parking_lot = "0.12" parking_lot = "0.12"
auto-launch = "0.5.0" auto-launch = "0.5.0"
percent-encoding = "2.3.1" percent-encoding = "2.3.1"
window-shadows = { version = "0.2" } window-shadows = { version = "0.2.2" }
tokio = { version = "1", features = ["full"] } tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
reqwest = { version = "0.12", features = ["json", "rustls-tls"] } reqwest = { version = "0.12", features = ["json", "rustls-tls"] }
sysproxy = { git="https://github.com/zzzgydi/sysproxy-rs", branch = "main" } sysproxy = { git="https://github.com/zzzgydi/sysproxy-rs", branch = "main" }
tauri = { version="1", features = [ "fs-read-file", "fs-exists", "path-all", "protocol-asset", "dialog-open", "notification-all", "icon-png", "icon-ico", "clipboard-all", "global-shortcut-all", "process-all", "shell-all", "system-tray", "updater", "window-all", "devtools"] } tauri = { version= "2.0.0-rc", features = [ "protocol-asset", "devtools", "tray-icon", "image-ico", "image-png"] }
network-interface = { version = "2.0.0", features = ["serde"] } network-interface = { version = "2.0.0", features = ["serde"] }
tauri-plugin-shell = "2.0.0-rc"
tauri-plugin-dialog = "2.0.0-rc"
tauri-plugin-fs = "2.0.0-rc"
tauri-plugin-notification = "2.0.0-rc"
tauri-plugin-process = "2.0.0-rc"
tauri-plugin-clipboard-manager = "2.1.0-beta.7"
[target.'cfg(windows)'.dependencies] [target.'cfg(windows)'.dependencies]
runas = "=1.2.0" runas = "=1.2.0"
deelevate = "0.2.0" deelevate = "0.2.0"
@ -46,6 +52,10 @@ winreg = "0.52.0"
[target.'cfg(target_os = "linux")'.dependencies] [target.'cfg(target_os = "linux")'.dependencies]
users = "0.11.0" users = "0.11.0"
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
tauri-plugin-global-shortcut = "2.0.0-rc"
tauri-plugin-updater = "2.0.0-rc"
#openssl #openssl
[features] [features]

View File

@ -0,0 +1,5 @@
{
"identifier": "desktop-capability",
"platforms": ["macOS", "windows", "linux"],
"permissions": ["global-shortcut:default", "updater:default"]
}

View File

@ -0,0 +1,65 @@
{
"identifier": "migrated",
"description": "permissions that were migrated from v1",
"local": true,
"windows": ["main"],
"permissions": [
"core:default",
"fs:allow-read-file",
"fs:allow-exists",
{
"identifier": "fs:scope",
"allow": ["$APPDATA/**", "$RESOURCE/../**", "**"]
},
"core:window:allow-create",
"core:window:allow-center",
"core:window:allow-request-user-attention",
"core:window:allow-set-resizable",
"core:window:allow-set-maximizable",
"core:window:allow-set-minimizable",
"core:window:allow-set-closable",
"core:window:allow-set-title",
"core:window:allow-maximize",
"core:window:allow-unmaximize",
"core:window:allow-minimize",
"core:window:allow-unminimize",
"core:window:allow-show",
"core:window:allow-hide",
"core:window:allow-close",
"core:window:allow-set-decorations",
"core:window:allow-set-always-on-top",
"core:window:allow-set-content-protected",
"core:window:allow-set-size",
"core:window:allow-set-min-size",
"core:window:allow-set-max-size",
"core:window:allow-set-position",
"core:window:allow-set-fullscreen",
"core:window:allow-set-focus",
"core:window:allow-set-icon",
"core:window:allow-set-skip-taskbar",
"core:window:allow-set-cursor-grab",
"core:window:allow-set-cursor-visible",
"core:window:allow-set-cursor-icon",
"core:window:allow-set-cursor-position",
"core:window:allow-set-ignore-cursor-events",
"core:window:allow-start-dragging",
"core:webview:allow-print",
"shell:allow-execute",
"shell:allow-open",
"dialog:allow-open",
"notification:default",
"global-shortcut:allow-is-registered",
"global-shortcut:allow-register",
"global-shortcut:allow-register-all",
"global-shortcut:allow-unregister",
"global-shortcut:allow-unregister-all",
"process:allow-restart",
"process:allow-exit",
"clipboard-manager:allow-read-text",
"clipboard-manager:allow-write-text",
"shell:default",
"dialog:default",
"fs:default",
"notification:default"
]
}

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,74 @@
{
"desktop-capability": {
"identifier": "desktop-capability",
"description": "",
"local": true,
"permissions": ["global-shortcut:default", "updater:default"],
"platforms": ["macOS", "windows", "linux"]
},
"migrated": {
"identifier": "migrated",
"description": "permissions that were migrated from v1",
"local": true,
"windows": ["main"],
"permissions": [
"core:default",
"fs:allow-read-file",
"fs:allow-exists",
{
"identifier": "fs:scope",
"allow": ["$APPDATA/**", "$RESOURCE/../**", "**"]
},
"core:window:allow-create",
"core:window:allow-center",
"core:window:allow-request-user-attention",
"core:window:allow-set-resizable",
"core:window:allow-set-maximizable",
"core:window:allow-set-minimizable",
"core:window:allow-set-closable",
"core:window:allow-set-title",
"core:window:allow-maximize",
"core:window:allow-unmaximize",
"core:window:allow-minimize",
"core:window:allow-unminimize",
"core:window:allow-show",
"core:window:allow-hide",
"core:window:allow-close",
"core:window:allow-set-decorations",
"core:window:allow-set-always-on-top",
"core:window:allow-set-content-protected",
"core:window:allow-set-size",
"core:window:allow-set-min-size",
"core:window:allow-set-max-size",
"core:window:allow-set-position",
"core:window:allow-set-fullscreen",
"core:window:allow-set-focus",
"core:window:allow-set-icon",
"core:window:allow-set-skip-taskbar",
"core:window:allow-set-cursor-grab",
"core:window:allow-set-cursor-visible",
"core:window:allow-set-cursor-icon",
"core:window:allow-set-cursor-position",
"core:window:allow-set-ignore-cursor-events",
"core:window:allow-start-dragging",
"core:webview:allow-print",
"shell:allow-execute",
"shell:allow-open",
"dialog:allow-open",
"notification:default",
"global-shortcut:allow-is-registered",
"global-shortcut:allow-register",
"global-shortcut:allow-register-all",
"global-shortcut:allow-unregister",
"global-shortcut:allow-unregister-all",
"process:allow-restart",
"process:allow-exit",
"clipboard-manager:allow-read-text",
"clipboard-manager:allow-write-text",
"shell:default",
"dialog:default",
"fs:default",
"notification:default"
]
}
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -10,8 +10,8 @@ use network_interface::NetworkInterface;
use serde_yaml::Mapping; use serde_yaml::Mapping;
use std::collections::{HashMap, VecDeque}; use std::collections::{HashMap, VecDeque};
use sysproxy::{Autoproxy, Sysproxy}; use sysproxy::{Autoproxy, Sysproxy};
use tauri::{api, Manager};
type CmdResult<T = ()> = Result<T, String>; type CmdResult<T = ()> = Result<T, String>;
use tauri::Manager;
#[tauri::command] #[tauri::command]
pub fn copy_clash_env(app_handle: tauri::AppHandle) -> CmdResult { pub fn copy_clash_env(app_handle: tauri::AppHandle) -> CmdResult {
@ -361,7 +361,7 @@ pub fn get_network_interfaces_info() -> CmdResult<Vec<NetworkInterface>> {
#[tauri::command] #[tauri::command]
pub fn open_devtools(app_handle: tauri::AppHandle) { pub fn open_devtools(app_handle: tauri::AppHandle) {
if let Some(window) = app_handle.get_window("main") { if let Some(window) = app_handle.get_webview_window("main") {
if !window.is_devtools_open() { if !window.is_devtools_open() {
window.open_devtools(); window.open_devtools();
} else { } else {
@ -374,7 +374,6 @@ pub fn open_devtools(app_handle: tauri::AppHandle) {
pub fn exit_app(app_handle: tauri::AppHandle) { pub fn exit_app(app_handle: tauri::AppHandle) {
let _ = resolve::save_window_size_position(&app_handle, true); let _ = resolve::save_window_size_position(&app_handle, true);
resolve::resolve_reset(); resolve::resolve_reset();
api::process::kill_children();
app_handle.exit(0); app_handle.exit(0);
std::process::exit(0); std::process::exit(0);
} }

View File

@ -2,19 +2,22 @@ use crate::config::*;
use crate::core::{clash_api, handle, logger::Logger, service}; use crate::core::{clash_api, handle, logger::Logger, service};
use crate::log_err; use crate::log_err;
use crate::utils::dirs; use crate::utils::dirs;
use anyhow::{bail, Result}; use anyhow::{bail, Context, Result};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use parking_lot::Mutex; use parking_lot::Mutex;
use serde_yaml::Mapping; use serde_yaml::Mapping;
use std::{sync::Arc, time::Duration}; use std::{fs, io::Write, sync::Arc, time::Duration};
use sysinfo::{ProcessRefreshKind, RefreshKind, System}; use sysinfo::{ProcessRefreshKind, RefreshKind, System};
use tauri::api::process::{Command, CommandChild, CommandEvent}; use tauri::AppHandle;
use tauri_plugin_shell::process::{CommandChild, CommandEvent};
use tauri_plugin_shell::ShellExt;
use tokio::time::sleep; use tokio::time::sleep;
#[derive(Debug)] #[derive(Debug)]
pub struct CoreManager { pub struct CoreManager {
app_handle: Arc<Mutex<Option<AppHandle>>>,
sidecar: Arc<Mutex<Option<CommandChild>>>, sidecar: Arc<Mutex<Option<CommandChild>>>,
#[allow(unused)] #[allow(unused)]
use_service_mode: Arc<Mutex<bool>>, use_service_mode: Arc<Mutex<bool>>,
} }
@ -24,12 +27,14 @@ impl CoreManager {
static CORE_MANAGER: OnceCell<CoreManager> = OnceCell::new(); static CORE_MANAGER: OnceCell<CoreManager> = OnceCell::new();
CORE_MANAGER.get_or_init(|| CoreManager { CORE_MANAGER.get_or_init(|| CoreManager {
app_handle: Arc::new(Mutex::new(None)),
sidecar: Arc::new(Mutex::new(None)), sidecar: Arc::new(Mutex::new(None)),
use_service_mode: Arc::new(Mutex::new(false)), use_service_mode: Arc::new(Mutex::new(false)),
}) })
} }
pub fn init(&self) -> Result<()> { pub fn init(&self, app_handle: &AppHandle) -> Result<()> {
*self.app_handle.lock() = Some(app_handle.clone());
tauri::async_runtime::spawn(async { tauri::async_runtime::spawn(async {
// 启动clash // 启动clash
log_err!(Self::global().run_core().await); log_err!(Self::global().run_core().await);
@ -39,7 +44,7 @@ impl CoreManager {
} }
/// 检查订阅是否正确 /// 检查订阅是否正确
pub fn check_config(&self) -> Result<()> { pub async fn check_config(&self) -> Result<()> {
let config_path = Config::generate_file(ConfigType::Check)?; let config_path = Config::generate_file(ConfigType::Check)?;
let config_path = dirs::path_to_str(&config_path)?; let config_path = dirs::path_to_str(&config_path)?;
@ -62,19 +67,29 @@ impl CoreManager {
let test_dir = dirs::app_home_dir()?.join("test"); let test_dir = dirs::app_home_dir()?.join("test");
let test_dir = dirs::path_to_str(&test_dir)?; let test_dir = dirs::path_to_str(&test_dir)?;
let app_handle_option = {
let lock = self.app_handle.lock();
lock.as_ref().cloned()
};
if let Some(app_handle) = app_handle_option {
let output = app_handle
.shell()
.sidecar(clash_core)?
.args(["-t", "-d", test_dir, "-f", config_path])
.output()
.await?;
let output = Command::new_sidecar(clash_core)? if !output.status.success() {
.args(["-t", "-d", test_dir, "-f", config_path]) let stdout = String::from_utf8(output.stdout).unwrap_or_default();
.output()?; let error = clash_api::parse_check_output(stdout.clone());
let error = match !error.is_empty() {
if !output.status.success() { true => error,
let error = clash_api::parse_check_output(output.stdout.clone()); false => stdout.clone(),
let error = match !error.is_empty() { };
true => error, Logger::global().set_log(stdout.clone());
false => output.stdout.clone(), bail!("{error}");
}; }
Logger::global().set_log(output.stdout);
bail!("{error}");
} }
Ok(()) Ok(())
@ -156,37 +171,54 @@ impl CoreManager {
let args = vec!["-d", app_dir, "-f", config_path]; let args = vec!["-d", app_dir, "-f", config_path];
let cmd = Command::new_sidecar(clash_core)?; let app_handle = self.app_handle.lock();
let (mut rx, cmd_child) = cmd.args(args).spawn()?;
let mut sidecar = self.sidecar.lock(); if let Some(app_handle) = app_handle.as_ref() {
*sidecar = Some(cmd_child); let cmd = app_handle.shell().sidecar(clash_core)?;
drop(sidecar); let (mut rx, cmd_child) = cmd.args(args).spawn()?;
tauri::async_runtime::spawn(async move { // 将pid写入文件中
while let Some(event) = rx.recv().await { crate::log_err!((|| {
match event { let pid = cmd_child.pid();
CommandEvent::Stdout(line) => { let path = dirs::clash_pid_path()?;
log::info!(target: "app", "[mihomo]: {line}"); fs::File::create(path)
Logger::global().set_log(line); .context("failed to create the pid file")?
.write(format!("{pid}").as_bytes())
.context("failed to write pid to the file")?;
<Result<()>>::Ok(())
})());
let mut sidecar = self.sidecar.lock();
*sidecar = Some(cmd_child);
drop(sidecar);
tauri::async_runtime::spawn(async move {
while let Some(event) = rx.recv().await {
match event {
CommandEvent::Stdout(line) => {
let line = String::from_utf8(line).unwrap_or_default();
log::info!(target: "app", "[mihomo]: {line}");
Logger::global().set_log(line);
}
CommandEvent::Stderr(err) => {
let err = String::from_utf8(err).unwrap_or_default();
log::error!(target: "app", "[mihomo]: {err}");
Logger::global().set_log(err);
}
CommandEvent::Error(err) => {
log::error!(target: "app", "[mihomo]: {err}");
Logger::global().set_log(err);
}
CommandEvent::Terminated(_) => {
log::info!(target: "app", "mihomo core terminated");
let _ = CoreManager::global().recover_core();
break;
}
_ => {}
} }
CommandEvent::Stderr(err) => {
log::error!(target: "app", "[mihomo]: {err}");
Logger::global().set_log(err);
}
CommandEvent::Error(err) => {
log::error!(target: "app", "[mihomo]: {err}");
Logger::global().set_log(err);
}
CommandEvent::Terminated(_) => {
log::info!(target: "app", "mihomo core terminated");
let _ = CoreManager::global().recover_core();
break;
}
_ => {}
} }
} });
}); }
Ok(()) Ok(())
} }
@ -268,7 +300,7 @@ impl CoreManager {
// 更新订阅 // 更新订阅
Config::generate().await?; Config::generate().await?;
self.check_config()?; self.check_config().await?;
// 清掉旧日志 // 清掉旧日志
Logger::global().clear_log(); Logger::global().clear_log();
@ -296,7 +328,7 @@ impl CoreManager {
Config::generate().await?; Config::generate().await?;
// 检查订阅是否正常 // 检查订阅是否正常
self.check_config()?; self.check_config().await?;
// 更新运行时订阅 // 更新运行时订阅
let path = Config::generate_file(ConfigType::Run)?; let path = Config::generate_file(ConfigType::Run)?;

View File

@ -4,7 +4,7 @@ use anyhow::{bail, Result};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use parking_lot::Mutex; use parking_lot::Mutex;
use std::sync::Arc; use std::sync::Arc;
use tauri::{AppHandle, Manager, Window}; use tauri::{AppHandle, WebviewWindow, Manager, Emitter};
#[derive(Debug, Default, Clone)] #[derive(Debug, Default, Clone)]
pub struct Handle { pub struct Handle {
@ -20,15 +20,15 @@ impl Handle {
}) })
} }
pub fn init(&self, app_handle: AppHandle) { pub fn init(&self, app_handle: &AppHandle) {
*self.app_handle.lock() = Some(app_handle); *self.app_handle.lock() = Some(app_handle.clone());
} }
pub fn get_window(&self) -> Option<Window> { pub fn get_window(&self) -> Option<WebviewWindow> {
self.app_handle self.app_handle
.lock() .lock()
.as_ref() .as_ref()
.and_then(|a| a.get_window("main")) .and_then(|a| a.get_webview_window("main"))
} }
pub fn refresh_clash() { pub fn refresh_clash() {

View File

@ -3,11 +3,11 @@ use anyhow::{bail, Result};
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use parking_lot::Mutex; use parking_lot::Mutex;
use std::{collections::HashMap, sync::Arc}; use std::{collections::HashMap, sync::Arc};
use tauri::{AppHandle, GlobalShortcutManager}; use tauri::AppHandle;
use tauri_plugin_global_shortcut::GlobalShortcutExt;
pub struct Hotkey { pub struct Hotkey {
current: Arc<Mutex<Vec<String>>>, // 保存当前的热键设置 current: Arc<Mutex<Vec<String>>>, // 保存当前的热键设置
app_handle: Arc<Mutex<Option<AppHandle>>>, app_handle: Arc<Mutex<Option<AppHandle>>>,
} }
@ -21,9 +21,8 @@ impl Hotkey {
}) })
} }
pub fn init(&self, app_handle: AppHandle) -> Result<()> { pub fn init(&self, app_handle: &AppHandle) -> Result<()> {
*self.app_handle.lock() = Some(app_handle); *self.app_handle.lock() = Some(app_handle.clone());
let verge = Config::verge(); let verge = Config::verge();
if let Some(hotkeys) = verge.latest().hotkeys.as_ref() { if let Some(hotkeys) = verge.latest().hotkeys.as_ref() {
@ -49,18 +48,14 @@ impl Hotkey {
Ok(()) Ok(())
} }
fn get_manager(&self) -> Result<impl GlobalShortcutManager> { fn register(&self, hotkey: &str, func: &str) -> Result<()> {
let app_handle = self.app_handle.lock(); let app_handle = self.app_handle.lock();
if app_handle.is_none() { if app_handle.is_none() {
bail!("failed to get the hotkey manager"); bail!("failed to get the hotkey manager");
} }
Ok(app_handle.as_ref().unwrap().global_shortcut_manager()) let manager = app_handle.as_ref().unwrap().global_shortcut();
}
fn register(&self, hotkey: &str, func: &str) -> Result<()> { if manager.is_registered(hotkey) {
let mut manager = self.get_manager()?;
if manager.is_registered(hotkey)? {
manager.unregister(hotkey)?; manager.unregister(hotkey)?;
} }
@ -71,17 +66,22 @@ impl Hotkey {
"clash_mode_direct" => || feat::change_clash_mode("direct".into()), "clash_mode_direct" => || feat::change_clash_mode("direct".into()),
"toggle_system_proxy" => feat::toggle_system_proxy, "toggle_system_proxy" => feat::toggle_system_proxy,
"toggle_tun_mode" => feat::toggle_tun_mode, "toggle_tun_mode" => feat::toggle_tun_mode,
_ => bail!("invalid function \"{func}\""), _ => bail!("invalid function \"{func}\""),
}; };
manager.register(hotkey, f)?; let _ = manager.on_shortcut(hotkey, move |_, _, _| f());
log::info!(target: "app", "register hotkey {hotkey} {func}"); log::info!(target: "app", "register hotkey {hotkey} {func}");
Ok(()) Ok(())
} }
fn unregister(&self, hotkey: &str) -> Result<()> { fn unregister(&self, hotkey: &str) -> Result<()> {
self.get_manager()?.unregister(hotkey)?; let app_handle = self.app_handle.lock();
if app_handle.is_none() {
bail!("failed to get the hotkey manager");
}
let manager = app_handle.as_ref().unwrap().global_shortcut();
manager.unregister(hotkey)?;
log::info!(target: "app", "unregister hotkey {hotkey}"); log::info!(target: "app", "unregister hotkey {hotkey}");
Ok(()) Ok(())
} }
@ -153,8 +153,10 @@ impl Hotkey {
impl Drop for Hotkey { impl Drop for Hotkey {
fn drop(&mut self) { fn drop(&mut self) {
if let Ok(mut manager) = self.get_manager() { if let Some(app_handle) = self.app_handle.lock().as_ref() {
let _ = manager.unregister_all(); if let Err(e) = app_handle.global_shortcut().unregister_all() {
log::error!("Error unregistering all hotkeys: {:?}", e);
}
} }
} }
} }

View File

@ -5,19 +5,14 @@ use crate::{
utils::{dirs, resolve}, utils::{dirs, resolve},
}; };
use anyhow::Result; use anyhow::Result;
use tauri::{ use tauri::menu::{MenuBuilder, MenuEvent, MenuItemBuilder, PredefinedMenuItem, SubmenuBuilder};
api, AppHandle, CustomMenuItem, Manager, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem, use tauri::tray::{MouseButton, MouseButtonState, TrayIcon, TrayIconBuilder, TrayIconEvent};
SystemTraySubmenu, use tauri::{AppHandle, Manager};
};
pub struct Tray {} pub struct Tray {}
impl Tray { impl Tray {
pub fn tray_menu(app_handle: &AppHandle) -> SystemTrayMenu { pub fn update_systray(app_handle: &AppHandle) -> Result<()> {
let zh = { Config::verge().latest().language == Some("zh".into()) }; let zh = { Config::verge().latest().language == Some("zh".into()) };
let version = app_handle.package_info().version.to_string();
macro_rules! t { macro_rules! t {
($en: expr, $zh: expr) => { ($en: expr, $zh: expr) => {
if zh { if zh {
@ -28,80 +23,92 @@ impl Tray {
}; };
} }
SystemTrayMenu::new() let version = app_handle.package_info().version.to_string();
.add_item(CustomMenuItem::new(
"open_window", let open_window = MenuItemBuilder::with_id("open_window", t!("Dashboard", "打开面板"))
t!("Dashboard", "打开面板"), .build(app_handle)?;
)) let rule_mode =
.add_native_item(SystemTrayMenuItem::Separator) MenuItemBuilder::with_id("rule_mode", t!("Rule Mode", "规则模式")).build(app_handle)?;
.add_item(CustomMenuItem::new( let global_mode = MenuItemBuilder::with_id("global_mode", t!("Global Mode", "全局模式"))
"rule_mode", .build(app_handle)?;
t!("Rule Mode", "规则模式"), let direct_mode = MenuItemBuilder::with_id("direct_mode", t!("Direct Mode", "直连模式"))
)) .build(app_handle)?;
.add_item(CustomMenuItem::new( let system_proxy = MenuItemBuilder::with_id("system_proxy", t!("System Proxy", "系统代理"))
"global_mode", .build(app_handle)?;
t!("Global Mode", "全局模式"), let tun_mode =
)) MenuItemBuilder::with_id("tun_mode", t!("TUN Mode", "Tun 模式")).build(app_handle)?;
.add_item(CustomMenuItem::new( let copy_env = MenuItemBuilder::with_id("copy_env", t!("Copy Env", "复制环境变量"))
"direct_mode", .build(app_handle)?;
t!("Direct Mode", "直连模式"), let open_app_dir = MenuItemBuilder::with_id("open_app_dir", t!("App Dir", "应用目录"))
)) .build(app_handle)?;
.add_native_item(SystemTrayMenuItem::Separator) let open_core_dir = MenuItemBuilder::with_id("open_core_dir", t!("Core Dir", "内核目录"))
.add_item(CustomMenuItem::new( .build(app_handle)?;
"system_proxy", let open_logs_dir = MenuItemBuilder::with_id("open_logs_dir", t!("Logs Dir", "日志目录"))
t!("System Proxy", "系统代理"), .build(app_handle)?;
)) let open_dir = SubmenuBuilder::with_id(app_handle, "open_dir", t!("Open Dir", "打开目录"))
.add_item(CustomMenuItem::new("tun_mode", t!("TUN Mode", "Tun 模式"))) .items(&[&open_app_dir, &open_core_dir, &open_logs_dir])
.add_item(CustomMenuItem::new( .build()?;
"copy_env", let restart_clash =
t!("Copy Env", "复制环境变量"), MenuItemBuilder::with_id("restart_clash", t!("Restart Clash", "重启 Clash"))
)) .build(app_handle)?;
.add_submenu(SystemTraySubmenu::new( let restart_app = MenuItemBuilder::with_id("restart_app", t!("Restart App", "重启应用"))
t!("Open Dir", "打开目录"), .build(app_handle)?;
SystemTrayMenu::new() let app_version = MenuItemBuilder::with_id("app_version", format!("Version {version}"))
.add_item(CustomMenuItem::new( .build(app_handle)?;
"open_app_dir", let more = SubmenuBuilder::with_id(app_handle, "more", t!("More", "更多"))
t!("App Dir", "应用目录"), .items(&[&restart_clash, &restart_app, &app_version])
)) .build()?;
.add_item(CustomMenuItem::new( let quit = MenuItemBuilder::with_id("quit", t!("Quit", "退出"))
"open_core_dir", .accelerator("CmdOrControl+Q")
t!("Core Dir", "内核目录"), .build(app_handle)?;
)) let separator = PredefinedMenuItem::separator(app_handle)?;
.add_item(CustomMenuItem::new( let menu = MenuBuilder::new(app_handle)
"open_logs_dir", .items(&[
t!("Logs Dir", "日志目录"), &open_window,
)), &separator,
)) &rule_mode,
.add_submenu(SystemTraySubmenu::new( &global_mode,
t!("More", "更多"), &direct_mode,
SystemTrayMenu::new() &separator,
.add_item(CustomMenuItem::new( &system_proxy,
"restart_clash", &tun_mode,
t!("Restart Clash", "重启 Clash"), &copy_env,
)) &open_dir,
.add_item(CustomMenuItem::new( &more,
"restart_app", &separator,
t!("Restart App", "重启应用"), &quit,
)) ])
.add_item( .build()?;
CustomMenuItem::new("app_version", format!("Version {version}")).disabled(),
), let _ = TrayIconBuilder::with_id("verge_tray")
)) .menu(&menu)
.add_native_item(SystemTrayMenuItem::Separator) .on_menu_event(Self::on_menu_event)
.add_item(CustomMenuItem::new("quit", t!("Quit", "退出"))) .on_tray_icon_event(|tray, event| {
} let tray_event = { Config::verge().latest().tray_event.clone() };
let tray_event: String = tray_event.unwrap_or("main_window".into());
if let TrayIconEvent::Click {
button: MouseButton::Left,
button_state: MouseButtonState::Up,
..
} = event
{
let app = tray.app_handle();
match tray_event.as_str() {
"system_proxy" => feat::toggle_system_proxy(),
"tun_mode" => feat::toggle_tun_mode(),
"main_window" => resolve::create_window(app),
_ => {}
}
}
})
.build(app_handle);
pub fn update_systray(app_handle: &AppHandle) -> Result<()> {
app_handle
.tray_handle()
.set_menu(Tray::tray_menu(app_handle))?;
Tray::update_part(app_handle)?; Tray::update_part(app_handle)?;
Ok(()) Ok(())
} }
pub fn update_part(app_handle: &AppHandle) -> Result<()> { pub fn update_part(app_handle: &AppHandle) -> Result<()> {
let zh = { Config::verge().latest().language == Some("zh".into()) }; let zh = { Config::verge().latest().language == Some("zh".into()) };
let version = app_handle.package_info().version.to_string(); let version = app_handle.package_info().version.to_string();
macro_rules! t { macro_rules! t {
@ -124,225 +131,263 @@ impl Tray {
.to_owned() .to_owned()
}; };
let tray = app_handle.tray_handle(); if let Some(menu) = app_handle.menu() {
if let Some(item) = menu.get("rule_mode") {
let item = item.as_check_menuitem().unwrap();
let _ = item.set_checked(mode == "rule");
}
if let Some(item) = menu.get("global_mode") {
let item = item.as_check_menuitem().unwrap();
let _ = item.set_checked(mode == "global");
}
if let Some(item) = menu.get("direct_mode") {
let item = item.as_check_menuitem().unwrap();
let _ = item.set_checked(mode == "direct");
}
let _ = tray.get_item("rule_mode").set_selected(mode == "rule"); #[cfg(target_os = "linux")]
let _ = tray.get_item("global_mode").set_selected(mode == "global"); match mode.as_str() {
let _ = tray.get_item("direct_mode").set_selected(mode == "direct"); "rule" => {
if let Some(item) = menu.get("rule_mode") {
let _ = item
.as_menuitem()
.unwrap()
.set_text(t!("Rule Mode ✔", "规则模式 ✔"));
}
#[cfg(target_os = "linux")] if let Some(item) = menu.get("global_mode") {
match mode.as_str() { let _ = item
"rule" => { .as_menuitem()
let _ = tray .unwrap()
.get_item("rule_mode") .set_text(t!("Global Mode", "全局模式"));
.set_title(t!("Rule Mode ✔", "规则模式 ✔")); }
let _ = tray
.get_item("global_mode")
.set_title(t!("Global Mode", "全局模式"));
let _ = tray
.get_item("direct_mode")
.set_title(t!("Direct Mode", "直连模式"));
}
"global" => {
let _ = tray
.get_item("rule_mode")
.set_title(t!("Rule Mode", "规则模式"));
let _ = tray
.get_item("global_mode")
.set_title(t!("Global Mode ✔", "全局模式 ✔"));
let _ = tray
.get_item("direct_mode")
.set_title(t!("Direct Mode", "直连模式"));
}
"direct" => {
let _ = tray
.get_item("rule_mode")
.set_title(t!("Rule Mode", "规则模式"));
let _ = tray
.get_item("global_mode")
.set_title(t!("Global Mode", "全局模式"));
let _ = tray
.get_item("direct_mode")
.set_title(t!("Direct Mode ✔", "直连模式 ✔"));
}
_ => {}
}
let verge = Config::verge(); if let Some(item) = menu.get("direct_mode") {
let verge = verge.latest(); let _ = item
let system_proxy = verge.enable_system_proxy.as_ref().unwrap_or(&false); .as_menuitem()
let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false); .unwrap()
#[cfg(target_os = "macos")] .set_text(t!("Direct Mode", "直连模式"));
let tray_icon = verge.tray_icon.clone().unwrap_or("monochrome".to_string()); }
let common_tray_icon = verge.common_tray_icon.as_ref().unwrap_or(&false);
let sysproxy_tray_icon = verge.sysproxy_tray_icon.as_ref().unwrap_or(&false);
let tun_tray_icon = verge.tun_tray_icon.as_ref().unwrap_or(&false);
#[cfg(target_os = "macos")]
match tray_icon.as_str() {
"monochrome" => {
let _ = tray.set_icon_as_template(true);
}
"colorful" => {
let _ = tray.set_icon_as_template(false);
}
_ => {}
}
let mut indication_icon = if *system_proxy {
#[cfg(target_os = "macos")]
let mut icon = match tray_icon.as_str() {
"monochrome" => include_bytes!("../../icons/tray-icon-sys-mono.ico").to_vec(),
"colorful" => include_bytes!("../../icons/tray-icon-sys.ico").to_vec(),
_ => include_bytes!("../../icons/tray-icon-sys-mono.ico").to_vec(),
};
#[cfg(not(target_os = "macos"))]
let mut icon = include_bytes!("../../icons/tray-icon-sys.ico").to_vec();
if *sysproxy_tray_icon {
let icon_dir_path = dirs::app_home_dir()?.join("icons");
let png_path = icon_dir_path.join("sysproxy.png");
let ico_path = icon_dir_path.join("sysproxy.ico");
if ico_path.exists() {
icon = std::fs::read(ico_path).unwrap();
} else if png_path.exists() {
icon = std::fs::read(png_path).unwrap();
} }
} "global" => {
icon if let Some(item) = menu.get("rule_mode") {
} else { let _ = item
#[cfg(target_os = "macos")] .as_menuitem()
let mut icon = match tray_icon.as_str() { .unwrap()
"monochrome" => include_bytes!("../../icons/tray-icon-mono.ico").to_vec(), .set_text(t!("Rule Mode", "规则模式"));
"colorful" => include_bytes!("../../icons/tray-icon.ico").to_vec(), }
_ => include_bytes!("../../icons/tray-icon-mono.ico").to_vec(),
}; if let Some(item) = menu.get("global_mode") {
#[cfg(not(target_os = "macos"))] let _ = item
let mut icon = include_bytes!("../../icons/tray-icon.ico").to_vec(); .as_menuitem()
if *common_tray_icon { .unwrap()
let icon_dir_path = dirs::app_home_dir()?.join("icons"); .set_text(t!("Global Mode ✔", "全局模式 ✔"));
let png_path = icon_dir_path.join("common.png"); }
let ico_path = icon_dir_path.join("common.ico");
if ico_path.exists() { if let Some(item) = menu.get("direct_mode") {
icon = std::fs::read(ico_path).unwrap(); let _ = item
} else if png_path.exists() { .as_menuitem()
icon = std::fs::read(png_path).unwrap(); .unwrap()
.set_text(t!("Direct Mode", "直连模式"));
}
} }
} "direct" => {
icon if let Some(item) = menu.get("rule_mode") {
}; let _ = item
.as_menuitem()
if *tun_mode { .unwrap()
#[cfg(target_os = "macos")] .set_text(t!("Rule Mode", "规则模式"));
let mut icon = match tray_icon.as_str() { }
"monochrome" => include_bytes!("../../icons/tray-icon-tun-mono.ico").to_vec(), if let Some(item) = menu.get("global_mode") {
"colorful" => include_bytes!("../../icons/tray-icon-tun.ico").to_vec(), let _ = item
_ => include_bytes!("../../icons/tray-icon-tun-mono.ico").to_vec(), .as_menuitem()
}; .unwrap()
#[cfg(not(target_os = "macos"))] .set_text(t!("Global Mode", "全局模式"));
let mut icon = include_bytes!("../../icons/tray-icon-tun.ico").to_vec(); }
if *tun_tray_icon { if let Some(item) = menu.get("direct_mode") {
let icon_dir_path = dirs::app_home_dir()?.join("icons"); let _ = item
let png_path = icon_dir_path.join("tun.png"); .as_menuitem()
let ico_path = icon_dir_path.join("tun.ico"); .unwrap()
if ico_path.exists() { .set_text(t!("Direct Mode ✔", "直连模式 ✔"));
icon = std::fs::read(ico_path).unwrap(); }
} else if png_path.exists() {
icon = std::fs::read(png_path).unwrap();
} }
_ => {}
} }
indication_icon = icon
}
let _ = tray.set_icon(tauri::Icon::Raw(indication_icon)); let verge = Config::verge();
let verge = verge.latest();
let system_proxy = verge.enable_system_proxy.as_ref().unwrap_or(&false);
let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false);
#[cfg(target_os = "macos")]
let tray_icon = verge.tray_icon.clone().unwrap_or("monochrome".to_string());
let common_tray_icon = verge.common_tray_icon.as_ref().unwrap_or(&false);
let sysproxy_tray_icon = verge.sysproxy_tray_icon.as_ref().unwrap_or(&false);
let tun_tray_icon = verge.tun_tray_icon.as_ref().unwrap_or(&false);
let tray: TrayIcon = app_handle.tray_by_id("verge_tray").unwrap();
let _ = tray.get_item("system_proxy").set_selected(*system_proxy); #[cfg(target_os = "macos")]
let _ = tray.get_item("tun_mode").set_selected(*tun_mode); match tray_icon.as_str() {
#[cfg(target_os = "linux")] "monochrome" => {
{ let _ = tray.set_icon_as_template(true);
if *system_proxy { }
let _ = tray "colorful" => {
.get_item("system_proxy") let _ = tray.set_icon_as_template(false);
.set_title(t!("System Proxy ✔", "系统代理 ✔")); }
_ => {}
}
let mut indication_icon = if *system_proxy {
#[cfg(target_os = "macos")]
let mut icon = match tray_icon.as_str() {
"monochrome" => include_bytes!("../../icons/tray-icon-sys-mono.ico").to_vec(),
"colorful" => include_bytes!("../../icons/tray-icon-sys.ico").to_vec(),
_ => include_bytes!("../../icons/tray-icon-sys-mono.ico").to_vec(),
};
#[cfg(not(target_os = "macos"))]
let mut icon = include_bytes!("../../icons/tray-icon-sys.ico").to_vec();
if *sysproxy_tray_icon {
let icon_dir_path = dirs::app_home_dir()?.join("icons");
let png_path = icon_dir_path.join("sysproxy.png");
let ico_path = icon_dir_path.join("sysproxy.ico");
if ico_path.exists() {
icon = std::fs::read(ico_path).unwrap();
} else if png_path.exists() {
icon = std::fs::read(png_path).unwrap();
}
}
icon
} else { } else {
let _ = tray #[cfg(target_os = "macos")]
.get_item("system_proxy") let mut icon = match tray_icon.as_str() {
.set_title(t!("System Proxy", "系统代理")); "monochrome" => include_bytes!("../../icons/tray-icon-mono.ico").to_vec(),
} "colorful" => include_bytes!("../../icons/tray-icon.ico").to_vec(),
_ => include_bytes!("../../icons/tray-icon-mono.ico").to_vec(),
};
#[cfg(not(target_os = "macos"))]
let mut icon = include_bytes!("../../icons/tray-icon.ico").to_vec();
if *common_tray_icon {
let icon_dir_path = dirs::app_home_dir()?.join("icons");
let png_path = icon_dir_path.join("common.png");
let ico_path = icon_dir_path.join("common.ico");
if ico_path.exists() {
icon = std::fs::read(ico_path).unwrap();
} else if png_path.exists() {
icon = std::fs::read(png_path).unwrap();
}
}
icon
};
if *tun_mode { if *tun_mode {
let _ = tray #[cfg(target_os = "macos")]
.get_item("tun_mode") let mut icon = match tray_icon.as_str() {
.set_title(t!("TUN Mode ✔", "Tun 模式 ✔")); "monochrome" => include_bytes!("../../icons/tray-icon-tun-mono.ico").to_vec(),
} else { "colorful" => include_bytes!("../../icons/tray-icon-tun.ico").to_vec(),
let _ = tray _ => include_bytes!("../../icons/tray-icon-tun-mono.ico").to_vec(),
.get_item("tun_mode") };
.set_title(t!("TUN Mode", "Tun 模式")); #[cfg(not(target_os = "macos"))]
let mut icon = include_bytes!("../../icons/tray-icon-tun.ico").to_vec();
if *tun_tray_icon {
let icon_dir_path = dirs::app_home_dir()?.join("icons");
let png_path = icon_dir_path.join("tun.png");
let ico_path = icon_dir_path.join("tun.ico");
if ico_path.exists() {
icon = std::fs::read(ico_path).unwrap();
} else if png_path.exists() {
icon = std::fs::read(png_path).unwrap();
}
}
indication_icon = icon
} }
}
let switch_map = { let _ = tray.set_icon(Some(tauri::image::Image::from_bytes(&indication_icon)?));
let mut map = std::collections::HashMap::new(); if let Some(item) = menu.get("system_proxy") {
map.insert(true, "on"); let item = item.as_check_menuitem().unwrap();
map.insert(false, "off"); let _ = item.set_checked(mode == "system_proxy");
map }
}; if let Some(item) = menu.get("tun_mode") {
let item = item.as_check_menuitem().unwrap();
let _ = item.set_checked(mode == "tun_mode");
}
let mut current_profile_name = "None".to_string(); #[cfg(target_os = "linux")]
let profiles = Config::profiles(); {
let profiles = profiles.latest(); if let Some(item) = menu.get("system_proxy") {
if let Some(current_profile_uid) = profiles.get_current() { if *system_proxy {
let current_profile = profiles.get_item(&current_profile_uid); let _ = item
current_profile_name = match &current_profile.unwrap().name { .as_menuitem()
Some(profile_name) => profile_name.to_string(), .unwrap()
None => current_profile_name, .set_text(t!("System Proxy ✔", "系统代理 ✔"));
} else {
let _ = item
.as_menuitem()
.unwrap()
.set_text(t!("System Proxy", "系统代理"));
}
}
if let Some(item) = menu.get("tun_mode") {
if *tun_mode {
let _ = item
.as_menuitem()
.unwrap()
.set_text(t!("TUN Mode ✔", "Tun 模式 ✔"));
} else {
let _ = item
.as_menuitem()
.unwrap()
.set_text(t!("TUN Mode", "Tun 模式"));
}
}
}
let switch_map = {
let mut map = std::collections::HashMap::new();
map.insert(true, "on");
map.insert(false, "off");
map
}; };
};
let _ = tray.set_tooltip(&format!( let mut current_profile_name = "None".to_string();
"Clash Verge {version}\n{}: {}\n{}: {}\n{}: {}", let profiles = Config::profiles();
t!("SysProxy", "系统代理"), let profiles = profiles.latest();
switch_map[system_proxy], if let Some(current_profile_uid) = profiles.get_current() {
t!("TUN", "Tun模式"), let current_profile = profiles.get_item(&current_profile_uid);
switch_map[tun_mode], current_profile_name = match &current_profile.unwrap().name {
t!("Profile", "当前订阅"), Some(profile_name) => profile_name.to_string(),
current_profile_name None => current_profile_name,
)); };
};
let _ = tray.set_tooltip(Some(&format!(
"Clash Verge {version}\n{}: {}\n{}: {}\n{}: {}",
t!("SysProxy", "系统代理"),
switch_map[system_proxy],
t!("TUN", "Tun模式"),
switch_map[tun_mode],
t!("Profile", "当前订阅"),
current_profile_name
)));
}
Ok(()) Ok(())
} }
pub fn on_click(app_handle: &AppHandle) { pub fn on_menu_event(app_handle: &AppHandle, event: MenuEvent) {
let tray_event = { Config::verge().latest().tray_event.clone() }; match event.id.as_ref() {
let tray_event = tray_event.unwrap_or("main_window".into()); mode @ ("rule_mode" | "global_mode" | "direct_mode") => {
match tray_event.as_str() { let mode = &mode[0..mode.len() - 5];
feat::change_clash_mode(mode.into());
}
"open_window" => resolve::create_window(app_handle),
"system_proxy" => feat::toggle_system_proxy(), "system_proxy" => feat::toggle_system_proxy(),
"tun_mode" => feat::toggle_tun_mode(), "tun_mode" => feat::toggle_tun_mode(),
"main_window" => resolve::create_window(app_handle), "copy_env" => feat::copy_clash_env(app_handle),
_ => {} "open_app_dir" => crate::log_err!(cmds::open_app_dir()),
} "open_core_dir" => crate::log_err!(cmds::open_core_dir()),
} "open_logs_dir" => crate::log_err!(cmds::open_logs_dir()),
"restart_clash" => feat::restart_clash_core(),
pub fn on_system_tray_event(app_handle: &AppHandle, event: SystemTrayEvent) { "restart_app" => tauri::process::restart(&app_handle.env()),
match event { "quit" => cmds::exit_app(app_handle.clone()),
#[cfg(not(target_os = "macos"))]
SystemTrayEvent::LeftClick { .. } => Tray::on_click(app_handle),
#[cfg(target_os = "macos")]
SystemTrayEvent::RightClick { .. } => Tray::on_click(app_handle),
SystemTrayEvent::MenuItemClick { id, .. } => match id.as_str() {
mode @ ("rule_mode" | "global_mode" | "direct_mode") => {
let mode = &mode[0..mode.len() - 5];
feat::change_clash_mode(mode.into());
}
"open_window" => resolve::create_window(app_handle),
"system_proxy" => feat::toggle_system_proxy(),
"tun_mode" => feat::toggle_tun_mode(),
"copy_env" => feat::copy_clash_env(app_handle),
"open_app_dir" => crate::log_err!(cmds::open_app_dir()),
"open_core_dir" => crate::log_err!(cmds::open_core_dir()),
"open_logs_dir" => crate::log_err!(cmds::open_logs_dir()),
"restart_clash" => feat::restart_clash_core(),
"restart_app" => api::process::restart(&app_handle.env()),
"quit" => cmds::exit_app(app_handle.clone()),
_ => {}
},
_ => {} _ => {}
} }
} }

View File

@ -10,14 +10,15 @@ use crate::log_err;
use crate::utils::resolve; use crate::utils::resolve;
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use serde_yaml::{Mapping, Value}; use serde_yaml::{Mapping, Value};
use tauri::{AppHandle, ClipboardManager, Manager}; use tauri::{AppHandle, Manager};
use tauri_plugin_clipboard_manager::ClipboardExt;
// 打开面板 // 打开面板
pub fn open_or_close_dashboard() { pub fn open_or_close_dashboard() {
let handle = handle::Handle::global(); let handle = handle::Handle::global();
let app_handle = handle.app_handle.lock(); let app_handle = handle.app_handle.lock();
if let Some(app_handle) = app_handle.as_ref() { if let Some(app_handle) = app_handle.as_ref() {
if let Some(window) = app_handle.get_window("main") { if let Some(window) = app_handle.get_webview_window("main") {
if let Ok(true) = window.is_focused() { if let Ok(true) = window.is_focused() {
let _ = window.close(); let _ = window.close();
return; return;
@ -319,8 +320,7 @@ pub fn copy_clash_env(app_handle: &AppHandle) {
let cmd: String = format!("set http_proxy={http_proxy}\r\nset https_proxy={http_proxy}"); let cmd: String = format!("set http_proxy={http_proxy}\r\nset https_proxy={http_proxy}");
let ps: String = format!("$env:HTTP_PROXY=\"{http_proxy}\"; $env:HTTPS_PROXY=\"{http_proxy}\""); let ps: String = format!("$env:HTTP_PROXY=\"{http_proxy}\"; $env:HTTPS_PROXY=\"{http_proxy}\"");
let mut cliboard = app_handle.clipboard_manager(); let cliboard = app_handle.clipboard();
let env_type = { Config::verge().latest().env_type.clone() }; let env_type = { Config::verge().latest().env_type.clone() };
let env_type = match env_type { let env_type = match env_type {
Some(env_type) => env_type, Some(env_type) => env_type,

View File

@ -11,7 +11,7 @@ mod feat;
mod utils; mod utils;
use crate::utils::{init, resolve, server}; use crate::utils::{init, resolve, server};
use tauri::{api, SystemTray}; use tauri::tray::TrayIconBuilder;
fn main() -> std::io::Result<()> { fn main() -> std::io::Result<()> {
// 单例检测 // 单例检测
@ -34,14 +34,37 @@ fn main() -> std::io::Result<()> {
#[allow(unused_mut)] #[allow(unused_mut)]
let mut builder = tauri::Builder::default() let mut builder = tauri::Builder::default()
.system_tray(SystemTray::new()) .plugin(tauri_plugin_updater::Builder::new().build())
.plugin(tauri_plugin_clipboard_manager::init())
.plugin(tauri_plugin_process::init())
.plugin(tauri_plugin_global_shortcut::Builder::new().build())
.plugin(tauri_plugin_notification::init())
.plugin(tauri_plugin_fs::init())
.plugin(tauri_plugin_dialog::init())
.plugin(tauri_plugin_shell::init())
.setup(|app| { .setup(|app| {
TrayIconBuilder::new().build(app)?;
#[cfg(target_os = "macos")]
{
use tauri::menu::MenuBuilder;
let _ = MenuBuilder::new(app)
.copy()
.paste()
.undo()
.redo()
.cut()
.select_all()
.quit()
.close_window()
.build();
}
tauri::async_runtime::block_on(async move { tauri::async_runtime::block_on(async move {
resolve::resolve_setup(app).await; resolve::resolve_setup(app).await;
}); });
Ok(()) Ok(())
}) })
.on_system_tray_event(core::tray::Tray::on_system_tray_event)
.invoke_handler(tauri::generate_handler![ .invoke_handler(tauri::generate_handler![
// common // common
cmds::get_sys_proxy, cmds::get_sys_proxy,
@ -97,26 +120,6 @@ fn main() -> std::io::Result<()> {
cmds::clash_api_get_proxy_delay cmds::clash_api_get_proxy_delay
]); ]);
#[cfg(target_os = "macos")]
{
use tauri::{Menu, MenuItem, Submenu};
builder = builder.menu(
Menu::new().add_submenu(Submenu::new(
"Edit",
Menu::new()
.add_native_item(MenuItem::Undo)
.add_native_item(MenuItem::Redo)
.add_native_item(MenuItem::Copy)
.add_native_item(MenuItem::Paste)
.add_native_item(MenuItem::Cut)
.add_native_item(MenuItem::SelectAll)
.add_native_item(MenuItem::CloseWindow)
.add_native_item(MenuItem::Quit),
)),
);
}
let app = builder let app = builder
.build(tauri::generate_context!()) .build(tauri::generate_context!())
.expect("error while running tauri application"); .expect("error while running tauri application");
@ -125,10 +128,6 @@ fn main() -> std::io::Result<()> {
tauri::RunEvent::ExitRequested { api, .. } => { tauri::RunEvent::ExitRequested { api, .. } => {
api.prevent_exit(); api.prevent_exit();
} }
tauri::RunEvent::Updater(tauri::UpdaterEvent::Downloaded) => {
resolve::resolve_reset();
api::process::kill_children();
}
tauri::RunEvent::WindowEvent { label, event, .. } => { tauri::RunEvent::WindowEvent { label, event, .. } => {
if label == "main" { if label == "main" {
match event { match event {

View File

@ -2,10 +2,7 @@ use crate::core::handle;
use anyhow::Result; use anyhow::Result;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use std::path::PathBuf; use std::path::PathBuf;
use tauri::{ use tauri::Manager;
api::path::{data_dir, resource_dir},
Env,
};
#[cfg(not(feature = "verge-dev"))] #[cfg(not(feature = "verge-dev"))]
pub static APP_ID: &str = "io.github.clash-verge-rev.clash-verge-rev"; pub static APP_ID: &str = "io.github.clash-verge-rev.clash-verge-rev";
@ -48,9 +45,21 @@ pub fn app_home_dir() -> Result<PathBuf> {
return Ok(PathBuf::from(app_dir).join(".config").join(APP_ID)); return Ok(PathBuf::from(app_dir).join(".config").join(APP_ID));
} }
Ok(data_dir() let handle = handle::Handle::global();
.ok_or(anyhow::anyhow!("failed to get app home dir"))? let app_handle = handle.app_handle.lock();
.join(APP_ID))
if let Some(app_handle) = app_handle.as_ref() {
match app_handle.path().data_dir() {
Ok(dir) => {
return Ok(dir.join(APP_ID));
}
Err(e) => {
log::error!("Failed to get the app home directory: {}", e);
return Err(anyhow::anyhow!("Failed to get the app homedirectory"));
}
}
}
Err(anyhow::anyhow!("failed to get the app home dir"))
} }
/// get the resources dir /// get the resources dir
@ -58,10 +67,15 @@ pub fn app_resources_dir() -> Result<PathBuf> {
let handle = handle::Handle::global(); let handle = handle::Handle::global();
let app_handle = handle.app_handle.lock(); let app_handle = handle.app_handle.lock();
if let Some(app_handle) = app_handle.as_ref() { if let Some(app_handle) = app_handle.as_ref() {
let res_dir = resource_dir(app_handle.package_info(), &Env::default()) match app_handle.path().resource_dir() {
.ok_or(anyhow::anyhow!("failed to get the resource dir"))? Ok(dir) => {
.join("resources"); return Ok(dir.join("resources"));
return Ok(res_dir); }
Err(e) => {
log::error!("Failed to get the resource directory: {}", e);
return Err(anyhow::anyhow!("Failed to get the resource directory"));
}
};
}; };
Err(anyhow::anyhow!("failed to get the resource dir")) Err(anyhow::anyhow!("failed to get the resource dir"))
} }
@ -88,6 +102,10 @@ pub fn profiles_path() -> Result<PathBuf> {
Ok(app_home_dir()?.join(PROFILE_YAML)) Ok(app_home_dir()?.join(PROFILE_YAML))
} }
pub fn clash_pid_path() -> Result<PathBuf> {
Ok(app_home_dir()?.join("clash.pid"))
}
#[cfg(not(target_os = "windows"))] #[cfg(not(target_os = "windows"))]
pub fn service_path() -> Result<PathBuf> { pub fn service_path() -> Result<PathBuf> {
Ok(app_resources_dir()?.join("clash-verge-service")) Ok(app_resources_dir()?.join("clash-verge-service"))

View File

@ -4,10 +4,8 @@ use nanoid::nanoid;
use serde::{de::DeserializeOwned, Serialize}; use serde::{de::DeserializeOwned, Serialize};
use serde_yaml::{Mapping, Value}; use serde_yaml::{Mapping, Value};
use std::{fs, path::PathBuf, str::FromStr}; use std::{fs, path::PathBuf, str::FromStr};
use tauri::{ use tauri_plugin_shell::ShellExt;
api::shell::{open, Program},
Manager,
};
/// read data from yaml as struct T /// read data from yaml as struct T
pub fn read_yaml<T: DeserializeOwned>(path: &PathBuf) -> Result<T> { pub fn read_yaml<T: DeserializeOwned>(path: &PathBuf) -> Result<T> {
@ -104,20 +102,7 @@ pub fn get_last_part_and_decode(url: &str) -> Option<String> {
/// open file /// open file
/// use vscode by default /// use vscode by default
pub fn open_file(app: tauri::AppHandle, path: PathBuf) -> Result<()> { pub fn open_file(app: tauri::AppHandle, path: PathBuf) -> Result<()> {
#[cfg(target_os = "macos")] app.shell().open(path.to_string_lossy(), None).unwrap();
let code = "Visual Studio Code";
#[cfg(not(target_os = "macos"))]
let code = "code";
let _ = match Program::from_str(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)
}
};
Ok(()) Ok(())
} }

View File

@ -10,7 +10,8 @@ use log4rs::encode::pattern::PatternEncoder;
use std::fs::{self, DirEntry}; use std::fs::{self, DirEntry};
use std::path::PathBuf; use std::path::PathBuf;
use std::str::FromStr; use std::str::FromStr;
use tauri::api::process::Command; use tauri::AppHandle;
use tauri_plugin_shell::ShellExt;
/// initialize this instance's log file /// initialize this instance's log file
fn init_log() -> Result<()> { fn init_log() -> Result<()> {
@ -296,43 +297,39 @@ pub fn init_scheme() -> Result<()> {
Ok(()) Ok(())
} }
pub fn startup_script() -> Result<()> { pub async fn startup_script(app_handle: &AppHandle) -> Result<()> {
let path = { let script_path = {
let verge = Config::verge(); let verge = Config::verge();
let verge = verge.latest(); let verge = verge.latest();
verge.startup_script.clone().unwrap_or("".to_string()) verge.startup_script.clone().unwrap_or("".to_string())
}; };
if !path.is_empty() { if script_path.is_empty() {
let mut shell = ""; return Ok(());
if path.ends_with(".sh") {
shell = "bash";
}
if path.ends_with(".ps1") {
shell = "powershell";
}
if path.ends_with(".bat") {
shell = "powershell";
}
if shell.is_empty() {
return Err(anyhow::anyhow!("unsupported script: {path}"));
}
let current_dir = PathBuf::from(path.clone());
if !current_dir.exists() {
return Err(anyhow::anyhow!("script not found: {path}"));
}
let current_dir = current_dir.parent();
match current_dir {
Some(dir) => {
let _ = Command::new(shell)
.current_dir(dir.to_path_buf())
.args([path])
.output()?;
}
None => {
let _ = Command::new(shell).args([path]).output()?;
}
}
} }
let shell_type = if script_path.ends_with(".sh") {
"bash"
} else if script_path.ends_with(".ps1") || script_path.ends_with(".bat") {
"powershell"
} else {
return Err(anyhow::anyhow!("unsupported script extension: {}", script_path));
};
let script_dir = PathBuf::from(&script_path);
if !script_dir.exists() {
return Err(anyhow::anyhow!("script not found: {}", script_path));
}
let parent_dir = script_dir.parent();
let working_dir = parent_dir.unwrap_or(script_dir.as_ref());
app_handle
.shell()
.command(shell_type)
.current_dir(working_dir.to_path_buf())
.args(&[script_path])
.output().await?;
Ok(()) Ok(())
} }

View File

@ -6,10 +6,12 @@ use anyhow::Result;
use once_cell::sync::OnceCell; use once_cell::sync::OnceCell;
use serde_yaml::Mapping; use serde_yaml::Mapping;
use std::net::TcpListener; use std::net::TcpListener;
use tauri::api::notification;
use tauri::{App, AppHandle, Manager}; use tauri::{App, AppHandle, Manager};
#[cfg(not(target_os = "linux"))] #[cfg(not(target_os = "linux"))]
use window_shadows::set_shadow; // use window_shadows::set_shadow;
use tauri_plugin_notification::NotificationExt;
pub static VERSION: OnceCell<String> = OnceCell::new(); pub static VERSION: OnceCell<String> = OnceCell::new();
@ -33,14 +35,14 @@ pub fn find_unused_port() -> Result<u16> {
/// handle something when start app /// handle something when start app
pub async fn resolve_setup(app: &mut App) { pub async fn resolve_setup(app: &mut App) {
#[cfg(target_os = "macos")] #[cfg(target_os = "macos")]
app.set_activation_policy(tauri::ActivationPolicy::Accessory); app.set_activation_policy(tauri::ActivationPolicy::Regular);
let version = app.package_info().version.to_string(); let version = app.package_info().version.to_string();
handle::Handle::global().init(app.app_handle()); handle::Handle::global().init(app.app_handle());
VERSION.get_or_init(|| version.clone()); VERSION.get_or_init(|| version.clone());
log_err!(init::init_resources()); log_err!(init::init_resources());
log_err!(init::init_scheme()); log_err!(init::init_scheme());
log_err!(init::startup_script()); log_err!(init::startup_script(app.app_handle()).await);
// 处理随机端口 // 处理随机端口
let enable_random_port = Config::verge().latest().enable_random_port.unwrap_or(false); let enable_random_port = Config::verge().latest().enable_random_port.unwrap_or(false);
@ -74,7 +76,7 @@ pub async fn resolve_setup(app: &mut App) {
log_err!(Config::init_config().await); log_err!(Config::init_config().await);
log::trace!("launch core"); log::trace!("launch core");
log_err!(CoreManager::global().init()); log_err!(CoreManager::global().init(app.app_handle()));
// setup a simple http server for singleton // setup a simple http server for singleton
log::trace!("launch embed server"); log::trace!("launch embed server");
@ -115,17 +117,17 @@ pub fn resolve_reset() {
/// create main window /// create main window
pub fn create_window(app_handle: &AppHandle) { pub fn create_window(app_handle: &AppHandle) {
if let Some(window) = app_handle.get_window("main") { if let Some(window) = app_handle.get_webview_window("main") {
trace_err!(window.unminimize(), "set win unminimize"); trace_err!(window.unminimize(), "set win unminimize");
trace_err!(window.show(), "set win visible"); trace_err!(window.show(), "set win visible");
trace_err!(window.set_focus(), "set win focus"); trace_err!(window.set_focus(), "set win focus");
return; return;
} }
let mut builder = tauri::window::WindowBuilder::new( let mut builder = tauri::WebviewWindowBuilder::new(
app_handle, app_handle,
"main".to_string(), "main".to_string(),
tauri::WindowUrl::App("index.html".into()), tauri::WebviewUrl::App("index.html".into()),
) )
.title("Clash Verge") .title("Clash Verge")
.visible(false) .visible(false)
@ -199,8 +201,8 @@ pub fn create_window(app_handle: &AppHandle) {
trace_err!(win.center(), "set win center"); trace_err!(win.center(), "set win center");
} }
#[cfg(not(target_os = "linux"))] // #[cfg(not(target_os = "linux"))]
trace_err!(set_shadow(&win, true), "set win shadow"); // trace_err!(set_shadow(&win, true), "set win shadow");
if is_maximized { if is_maximized {
trace_err!(win.maximize(), "set win maximize"); trace_err!(win.maximize(), "set win maximize");
} }
@ -221,7 +223,7 @@ pub fn save_window_size_position(app_handle: &AppHandle, save_to_file: bool) ->
} }
let win = app_handle let win = app_handle
.get_window("main") .get_webview_window("main")
.ok_or(anyhow::anyhow!("failed to get window"))?; .ok_or(anyhow::anyhow!("failed to get window"))?;
let scale = win.scale_factor()?; let scale = win.scale_factor()?;
@ -241,21 +243,30 @@ pub async fn resolve_scheme(param: String) -> Result<()> {
let url = param let url = param
.trim_start_matches("clash://install-config/?url=") .trim_start_matches("clash://install-config/?url=")
.trim_start_matches("clash://install-config?url="); .trim_start_matches("clash://install-config?url=");
match import_profile(url.to_string(), None).await {
Ok(_) => { let handle = handle::Handle::global();
notification::Notification::new(crate::utils::dirs::APP_ID) let app_handle = handle.app_handle.lock();
.title("Clash Verge") if let Some(app_handle) = app_handle.as_ref() {
.body("Import profile success") match import_profile(url.to_string(), None).await {
.show() Ok(_) => {
.unwrap(); app_handle
} .notification()
Err(e) => { .builder()
notification::Notification::new(crate::utils::dirs::APP_ID) .title("Clash Verge")
.title("Clash Verge") .body("Import profile success")
.body(format!("Import profile failed: {e}")) .show()
.show() .unwrap();
.unwrap(); }
log::error!("Import profile failed: {e}"); Err(e) => {
app_handle
.notification()
.builder()
.title("Clash Verge")
.body(format!("Import profile failed: {e}"))
.show()
.unwrap();
log::error!("Import profile failed: {e}");
}
} }
} }
Ok(()) Ok(())

View File

@ -1,20 +1,16 @@
extern crate warp; extern crate warp;
use super::resolve; use super::resolve;
use crate::{ use crate::config::{Config, IVerge, DEFAULT_PAC};
config::{Config, IVerge, DEFAULT_PAC},
log_err,
};
use anyhow::{bail, Result}; use anyhow::{bail, Result};
use port_scanner::local_port_available; use port_scanner::local_port_available;
use std::convert::Infallible; use std::convert::Infallible;
use tauri::AppHandle; use tauri::AppHandle;
use warp::http::StatusCode;
use warp::Filter; use warp::Filter;
#[derive(serde::Deserialize, Debug)] #[derive(serde::Deserialize, Debug)]
struct QueryParam { struct QueryParam {}
param: String,
}
/// check whether there is already exists /// check whether there is already exists
pub async fn check_singleton() -> Result<()> { pub async fn check_singleton() -> Result<()> {
@ -56,14 +52,15 @@ pub async fn check_singleton() -> Result<()> {
/// The embed server only be used to implement singleton process /// The embed server only be used to implement singleton process
/// maybe it can be used as pac server later /// maybe it can be used as pac server later
pub fn embed_server(app_handle: AppHandle) { pub fn embed_server(app_handle: &AppHandle) {
let port = IVerge::get_singleton_port(); let port = IVerge::get_singleton_port();
let handle = app_handle.clone();
tauri::async_runtime::spawn(async move { tauri::async_runtime::spawn(async move {
let ping = warp::path!("commands" / "ping").map(move || "ok"); let ping = warp::path!("commands" / "ping").map(move || "ok");
let visible = warp::path!("commands" / "visible").map(move || { let visible = warp::path!("commands" / "visible").map(move || {
resolve::create_window(&app_handle); resolve::create_window(&handle);
"ok" "ok"
}); });
@ -87,9 +84,9 @@ pub fn embed_server(app_handle: AppHandle) {
.and(warp::query::<QueryParam>()) .and(warp::query::<QueryParam>())
.and_then(scheme_handler); .and_then(scheme_handler);
async fn scheme_handler(query: QueryParam) -> Result<impl warp::Reply, Infallible> { async fn scheme_handler(_: QueryParam) -> Result<impl warp::Reply, Infallible> {
log_err!(resolve::resolve_scheme(query.param).await); //let _ = resolve::resolve_scheme(query.param).await;
Ok("ok") Ok(warp::reply::with_status("Ok", StatusCode::OK))
} }
let commands = ping.or(visible).or(pac).or(scheme); let commands = ping.or(visible).or(pac).or(scheme);
warp::serve(commands).run(([127, 0, 0, 1], port)).await; warp::serve(commands).run(([127, 0, 0, 1], port)).await;

View File

@ -1,82 +1,49 @@
{ {
"$schema": "../node_modules/@tauri-apps/cli/schema.json", "$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"package": { "bundle": {
"productName": "Clash Verge", "active": true,
"version": "1.7.7" "longDescription": "A Clash Meta GUI based on tauri.",
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"resources": ["resources"],
"publisher": "Clash Verge Rev",
"externalBin": ["sidecar/verge-mihomo", "sidecar/verge-mihomo-alpha"],
"copyright": "GNU General Public License v3.0",
"category": "DeveloperTool",
"shortDescription": "A Clash Meta GUI based on tauri.",
"createUpdaterArtifacts": "v1Compatible"
}, },
"build": { "build": {
"distDir": "../dist", "beforeBuildCommand": "pnpm run web:build",
"devPath": "http://localhost:3000/", "frontendDist": "../dist",
"beforeDevCommand": "pnpm run web:dev", "beforeDevCommand": "pnpm run web:dev",
"beforeBuildCommand": "pnpm run web:build" "devUrl": "http://localhost:3000/"
}, },
"tauri": { "productName": "Clash Verge",
"bundle": { "version": "1.7.7",
"active": true, "identifier": "io.github.clash-verge-rev.clash-verge-rev",
"identifier": "io.github.clash-verge-rev.clash-verge-rev", "plugins": {
"icon": [
"icons/32x32.png",
"icons/128x128.png",
"icons/128x128@2x.png",
"icons/icon.icns",
"icons/icon.ico"
],
"resources": ["resources"],
"publisher": "Clash Verge Rev",
"externalBin": ["sidecar/verge-mihomo", "sidecar/verge-mihomo-alpha"],
"copyright": "GNU General Public License v3.0",
"category": "DeveloperTool",
"shortDescription": "A Clash Meta GUI based on tauri.",
"longDescription": "A Clash Meta GUI based on tauri."
},
"updater": { "updater": {
"active": true, "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEQyOEMyRjBCQkVGOUJEREYKUldUZnZmbStDeStNMHU5Mmo1N24xQXZwSVRYbXA2NUpzZE5oVzlqeS9Bc0t6RVV4MmtwVjBZaHgK",
"endpoints": [ "endpoints": [
"https://download.clashverge.dev/https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update-proxy.json", "https://download.clashverge.dev/https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update-proxy.json",
"https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update.json" "https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update.json"
], ]
"dialog": false,
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEQyOEMyRjBCQkVGOUJEREYKUldUZnZmbStDeStNMHU5Mmo1N24xQXZwSVRYbXA2NUpzZE5oVzlqeS9Bc0t6RVV4MmtwVjBZaHgK"
},
"allowlist": {
"shell": {
"all": true
},
"window": {
"all": true
},
"process": {
"all": true
},
"globalShortcut": {
"all": true
},
"clipboard": {
"all": true
},
"notification": {
"all": true
},
"dialog": {
"all": false,
"open": true
},
"protocol": {
"asset": true,
"assetScope": ["$APPDATA/**", "$RESOURCE/../**", "**"]
},
"path": {
"all": true
},
"fs": {
"exists": true,
"readFile": true,
"scope": ["$APPDATA/**", "$RESOURCE/../**", "**"]
}
},
"windows": [],
"security": {
"csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: asset: 'unsafe-eval' 'unsafe-inline' 'self';"
} }
},
"app": {
"security": {
"assetProtocol": {
"scope": ["$APPDATA/**", "$RESOURCE/../**", "**"],
"enable": true
},
"csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: asset: 'unsafe-eval' 'unsafe-inline' 'self';; connect-src ipc: http://ipc.localhost"
},
"windows": []
} }
} }

View File

@ -1,12 +1,8 @@
{ {
"$schema": "../node_modules/@tauri-apps/cli/schema.json", "$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"tauri": { "bundle": {
"systemTray": { "targets": ["deb", "rpm"],
"iconPath": "icons/tray-icon.ico" "linux": {
},
"bundle": {
"identifier": "io.github.clash-verge-rev.clash-verge-rev",
"targets": ["deb", "rpm"],
"deb": { "deb": {
"depends": ["openssl"], "depends": ["openssl"],
"desktopTemplate": "./template/clash-verge.desktop", "desktopTemplate": "./template/clash-verge.desktop",
@ -22,5 +18,10 @@
"obsoletes": ["clash-verge"] "obsoletes": ["clash-verge"]
} }
} }
},
"app": {
"trayIcon": {
"iconPath": "icons/tray-icon.ico"
}
} }
} }

View File

@ -1,20 +1,13 @@
{ {
"$schema": "../node_modules/@tauri-apps/cli/schema.json", "$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"tauri": { "bundle": {
"systemTray": { "targets": ["app", "dmg"],
"iconPath": "icons/tray-icon-mono.ico", "macOS": {
"iconAsTemplate": true "frameworks": [],
}, "minimumSystemVersion": "10.15",
"bundle": { "exceptionDomain": "",
"identifier": "io.github.clash-verge-rev.clash-verge-rev", "signingIdentity": null,
"targets": ["app", "dmg", "updater"], "entitlements": null,
"macOS": {
"frameworks": [],
"minimumSystemVersion": "10.15",
"exceptionDomain": "",
"signingIdentity": null,
"entitlements": null
},
"dmg": { "dmg": {
"background": "images/background.png", "background": "images/background.png",
"appPosition": { "appPosition": {
@ -35,5 +28,11 @@
} }
} }
} }
},
"app": {
"trayIcon": {
"iconPath": "icons/tray-icon-mono.ico",
"iconAsTemplate": true
}
} }
} }

View File

@ -1,29 +1,28 @@
{ {
"$schema": "../node_modules/@tauri-apps/cli/schema.json", "$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"tauri": { "bundle": {
"systemTray": { "targets": ["nsis"],
"iconPath": "icons/tray-icon.ico" "windows": {
}, "certificateThumbprint": null,
"bundle": { "digestAlgorithm": "sha256",
"identifier": "io.github.clash-verge-rev.clash-verge-rev", "timestampUrl": "",
"targets": ["nsis", "updater"], "webviewInstallMode": {
"windows": { "type": "embedBootstrapper",
"certificateThumbprint": null, "silent": true
"digestAlgorithm": "sha256", },
"timestampUrl": "", "nsis": {
"webviewInstallMode": { "displayLanguageSelector": true,
"type": "embedBootstrapper", "installerIcon": "icons/icon.ico",
"silent": true "languages": ["SimpChinese", "English"],
}, "installMode": "perMachine",
"nsis": { "template": "./template/installer.nsi"
"displayLanguageSelector": true,
"installerIcon": "icons/icon.ico",
"languages": ["SimpChinese", "English"],
"license": "../LICENSE",
"installMode": "perMachine",
"template": "./template/installer.nsi"
}
} }
} }
},
"app": {
"trayIcon": {
"iconPath": "icons/tray-icon.ico",
"iconAsTemplate": true
}
} }
} }

View File

@ -7,7 +7,8 @@ import {
ErrorRounded, ErrorRounded,
} from "@mui/icons-material"; } from "@mui/icons-material";
import { useVerge } from "@/hooks/use-verge"; import { useVerge } from "@/hooks/use-verge";
import { appWindow } from "@tauri-apps/api/window"; import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
const appWindow = getCurrentWebviewWindow();
interface InnerProps { interface InnerProps {
type: string; type: string;
duration?: number; duration?: number;

View File

@ -1,5 +1,5 @@
import { Button, ButtonGroup } from "@mui/material"; import { Button, ButtonGroup } from "@mui/material";
import { appWindow } from "@tauri-apps/api/window"; import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
import { import {
CloseRounded, CloseRounded,
CropSquareRounded, CropSquareRounded,
@ -9,6 +9,7 @@ import {
PushPinRounded, PushPinRounded,
} from "@mui/icons-material"; } from "@mui/icons-material";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
const appWindow = getCurrentWebviewWindow();
export const LayoutControl = () => { export const LayoutControl = () => {
const minWidth = 40; const minWidth = 40;

View File

@ -1,7 +1,7 @@
import useSWR from "swr"; import useSWR from "swr";
import { useRef } from "react"; import { useRef } from "react";
import { Button } from "@mui/material"; import { Button } from "@mui/material";
import { checkUpdate } from "@tauri-apps/api/updater"; import { check } from "@tauri-apps/plugin-updater";
import { UpdateViewer } from "../setting/mods/update-viewer"; import { UpdateViewer } from "../setting/mods/update-viewer";
import { DialogRef } from "../base"; import { DialogRef } from "../base";
import { useVerge } from "@/hooks/use-verge"; import { useVerge } from "@/hooks/use-verge";
@ -19,7 +19,7 @@ export const UpdateButton = (props: Props) => {
const { data: updateInfo } = useSWR( const { data: updateInfo } = useSWR(
auto_check_update || auto_check_update === null ? "checkUpdate" : null, auto_check_update || auto_check_update === null ? "checkUpdate" : null,
checkUpdate, check,
{ {
errorRetryCount: 2, errorRetryCount: 2,
revalidateIfStale: false, revalidateIfStale: false,
@ -27,7 +27,7 @@ export const UpdateButton = (props: Props) => {
} }
); );
if (!updateInfo?.shouldUpdate) return null; if (!updateInfo?.available) return null;
return ( return (
<> <>

View File

@ -1,9 +1,10 @@
import { useEffect, useMemo } from "react"; import { useEffect, useMemo } from "react";
import { alpha, createTheme, Shadows, Theme } from "@mui/material"; import { alpha, createTheme, Shadows, Theme } from "@mui/material";
import { appWindow } from "@tauri-apps/api/window"; import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
import { useSetThemeMode, useThemeMode } from "@/services/states"; import { useSetThemeMode, useThemeMode } from "@/services/states";
import { defaultTheme, defaultDarkTheme } from "@/pages/_theme"; import { defaultTheme, defaultDarkTheme } from "@/pages/_theme";
import { useVerge } from "@/hooks/use-verge"; import { useVerge } from "@/hooks/use-verge";
const appWindow = getCurrentWebviewWindow();
/** /**
* custom theme * custom theme

View File

@ -18,7 +18,7 @@ import {
import { useThemeMode } from "@/services/states"; import { useThemeMode } from "@/services/states";
import { Notice } from "@/components/base"; import { Notice } from "@/components/base";
import { nanoid } from "nanoid"; import { nanoid } from "nanoid";
import { appWindow } from "@tauri-apps/api/window"; import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
import getSystem from "@/utils/get-system"; import getSystem from "@/utils/get-system";
import debounce from "@/utils/debounce"; import debounce from "@/utils/debounce";
@ -29,6 +29,7 @@ import { type JSONSchema7 } from "json-schema";
import metaSchema from "meta-json-schema/schemas/meta-json-schema.json"; import metaSchema from "meta-json-schema/schemas/meta-json-schema.json";
import mergeSchema from "meta-json-schema/schemas/clash-verge-merge-json-schema.json"; import mergeSchema from "meta-json-schema/schemas/clash-verge-merge-json-schema.json";
import pac from "types-pac/pac.d.ts?raw"; import pac from "types-pac/pac.d.ts?raw";
const appWindow = getCurrentWebviewWindow();
type Language = "yaml" | "javascript" | "css"; type Language = "yaml" | "javascript" | "css";
type Schema<T extends Language> = LanguageSchemaMap[T]; type Schema<T extends Language> = LanguageSchemaMap[T];

View File

@ -10,7 +10,7 @@ import { DeleteForeverRounded, UndoRounded } from "@mui/icons-material";
import { useSortable } from "@dnd-kit/sortable"; import { useSortable } from "@dnd-kit/sortable";
import { CSS } from "@dnd-kit/utilities"; import { CSS } from "@dnd-kit/utilities";
import { downloadIconCache } from "@/services/cmds"; import { downloadIconCache } from "@/services/cmds";
import { convertFileSrc } from "@tauri-apps/api/tauri"; import { convertFileSrc } from "@tauri-apps/api/core";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
interface Props { interface Props {
type: "prepend" | "original" | "delete" | "append"; type: "prepend" | "original" | "delete" | "append";

View File

@ -30,7 +30,7 @@ import { EditorViewer } from "@/components/profile/editor-viewer";
import { ProfileBox } from "./profile-box"; import { ProfileBox } from "./profile-box";
import parseTraffic from "@/utils/parse-traffic"; import parseTraffic from "@/utils/parse-traffic";
import { ConfirmViewer } from "@/components/profile/confirm-viewer"; import { ConfirmViewer } from "@/components/profile/confirm-viewer";
import { open } from "@tauri-apps/api/shell"; import { open } from "@tauri-apps/plugin-shell";
import { ProxiesEditorViewer } from "./proxies-editor-viewer"; import { ProxiesEditorViewer } from "./proxies-editor-viewer";
const round = keyframes` const round = keyframes`
from { transform: rotate(0deg); } from { transform: rotate(0deg); }

View File

@ -19,7 +19,7 @@ import type { IRenderItem } from "./use-render-list";
import { useVerge } from "@/hooks/use-verge"; import { useVerge } from "@/hooks/use-verge";
import { useThemeMode } from "@/services/states"; import { useThemeMode } from "@/services/states";
import { useEffect, useMemo, useState } from "react"; import { useEffect, useMemo, useState } from "react";
import { convertFileSrc } from "@tauri-apps/api/tauri"; import { convertFileSrc } from "@tauri-apps/api/core";
import { downloadIconCache } from "@/services/cmds"; import { downloadIconCache } from "@/services/cmds";
interface RenderProps { interface RenderProps {

View File

@ -12,11 +12,11 @@ import {
import { useVerge } from "@/hooks/use-verge"; import { useVerge } from "@/hooks/use-verge";
import { BaseDialog, DialogRef, Notice, Switch } from "@/components/base"; import { BaseDialog, DialogRef, Notice, Switch } from "@/components/base";
import { GuardState } from "./guard-state"; import { GuardState } from "./guard-state";
import { open as openDialog } from "@tauri-apps/api/dialog"; import { open as openDialog } from "@tauri-apps/plugin-dialog";
import { convertFileSrc } from "@tauri-apps/api/tauri"; import { convertFileSrc } from "@tauri-apps/api/core";
import { copyIconFile, getAppDir } from "@/services/cmds"; import { copyIconFile, getAppDir } from "@/services/cmds";
import { join } from "@tauri-apps/api/path"; import { join } from "@tauri-apps/api/path";
import { exists } from "@tauri-apps/api/fs"; import { exists } from "@tauri-apps/plugin-fs";
import getSystem from "@/utils/get-system"; import getSystem from "@/utils/get-system";
const OS = getSystem(); const OS = getSystem();
@ -186,7 +186,7 @@ export const LayoutViewer = forwardRef<DialogRef>((props, ref) => {
onChangeData({ common_tray_icon: false }); onChangeData({ common_tray_icon: false });
patchVerge({ common_tray_icon: false }); patchVerge({ common_tray_icon: false });
} else { } else {
const path = await openDialog({ const selected = await openDialog({
directory: false, directory: false,
multiple: false, multiple: false,
filters: [ filters: [
@ -196,8 +196,8 @@ export const LayoutViewer = forwardRef<DialogRef>((props, ref) => {
}, },
], ],
}); });
if (path?.length) { if (selected?.path.length) {
await copyIconFile(`${path}`, "common"); await copyIconFile(`${selected.path}`, "common");
await initIconPath(); await initIconPath();
onChangeData({ common_tray_icon: true }); onChangeData({ common_tray_icon: true });
patchVerge({ common_tray_icon: true }); patchVerge({ common_tray_icon: true });
@ -232,7 +232,7 @@ export const LayoutViewer = forwardRef<DialogRef>((props, ref) => {
onChangeData({ sysproxy_tray_icon: false }); onChangeData({ sysproxy_tray_icon: false });
patchVerge({ sysproxy_tray_icon: false }); patchVerge({ sysproxy_tray_icon: false });
} else { } else {
const path = await openDialog({ const selected = await openDialog({
directory: false, directory: false,
multiple: false, multiple: false,
filters: [ filters: [
@ -242,8 +242,8 @@ export const LayoutViewer = forwardRef<DialogRef>((props, ref) => {
}, },
], ],
}); });
if (path?.length) { if (selected?.path.length) {
await copyIconFile(`${path}`, "sysproxy"); await copyIconFile(`${selected.path}`, "sysproxy");
await initIconPath(); await initIconPath();
onChangeData({ sysproxy_tray_icon: true }); onChangeData({ sysproxy_tray_icon: true });
patchVerge({ sysproxy_tray_icon: true }); patchVerge({ sysproxy_tray_icon: true });
@ -276,7 +276,7 @@ export const LayoutViewer = forwardRef<DialogRef>((props, ref) => {
onChangeData({ tun_tray_icon: false }); onChangeData({ tun_tray_icon: false });
patchVerge({ tun_tray_icon: false }); patchVerge({ tun_tray_icon: false });
} else { } else {
const path = await openDialog({ const selected = await openDialog({
directory: false, directory: false,
multiple: false, multiple: false,
filters: [ filters: [
@ -286,8 +286,8 @@ export const LayoutViewer = forwardRef<DialogRef>((props, ref) => {
}, },
], ],
}); });
if (path?.length) { if (selected?.path.length) {
await copyIconFile(`${path}`, "tun"); await copyIconFile(`${selected.path}`, "tun");
await initIconPath(); await initIconPath();
onChangeData({ tun_tray_icon: true }); onChangeData({ tun_tray_icon: true });
patchVerge({ tun_tray_icon: true }); patchVerge({ tun_tray_icon: true });

View File

@ -4,7 +4,7 @@ import { BaseDialog, DialogRef, Notice } from "@/components/base";
import { getNetworkInterfacesInfo } from "@/services/cmds"; import { getNetworkInterfacesInfo } from "@/services/cmds";
import { alpha, Box, Button, Chip, IconButton } from "@mui/material"; import { alpha, Box, Button, Chip, IconButton } from "@mui/material";
import { ContentCopyRounded } from "@mui/icons-material"; import { ContentCopyRounded } from "@mui/icons-material";
import { writeText } from "@tauri-apps/api/clipboard"; import { writeText } from "@tauri-apps/plugin-clipboard-manager";
export const NetworkInterfaceViewer = forwardRef<DialogRef>((props, ref) => { export const NetworkInterfaceViewer = forwardRef<DialogRef>((props, ref) => {
const { t } = useTranslation(); const { t } = useTranslation();

View File

@ -3,13 +3,13 @@ import { forwardRef, useImperativeHandle, useState, useMemo } from "react";
import { useLockFn } from "ahooks"; import { useLockFn } from "ahooks";
import { Box, LinearProgress, Button } from "@mui/material"; import { Box, LinearProgress, Button } from "@mui/material";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { relaunch } from "@tauri-apps/api/process"; import { relaunch } from "@tauri-apps/plugin-process";
import { checkUpdate, installUpdate } from "@tauri-apps/api/updater"; import { check as checkUpdate } from "@tauri-apps/plugin-updater";
import { BaseDialog, DialogRef, Notice } from "@/components/base"; import { BaseDialog, DialogRef, Notice } from "@/components/base";
import { useUpdateState, useSetUpdateState } from "@/services/states"; import { useUpdateState, useSetUpdateState } from "@/services/states";
import { listen, Event, UnlistenFn } from "@tauri-apps/api/event"; import { listen, Event, UnlistenFn } from "@tauri-apps/api/event";
import { portableFlag } from "@/pages/_layout"; import { portableFlag } from "@/pages/_layout";
import { open as openUrl } from "@tauri-apps/api/shell"; import { open as openUrl } from "@tauri-apps/plugin-shell";
import ReactMarkdown from "react-markdown"; import ReactMarkdown from "react-markdown";
let eventListener: UnlistenFn | null = null; let eventListener: UnlistenFn | null = null;
@ -38,17 +38,17 @@ export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
})); }));
const markdownContent = useMemo(() => { const markdownContent = useMemo(() => {
if (!updateInfo?.manifest?.body) { if (!updateInfo?.body) {
return "New Version is available"; return "New Version is available";
} }
return updateInfo?.manifest?.body; return updateInfo?.body;
}, [updateInfo]); }, [updateInfo]);
const breakChangeFlag = useMemo(() => { const breakChangeFlag = useMemo(() => {
if (!updateInfo?.manifest?.body) { if (!updateInfo?.body) {
return false; return false;
} }
return updateInfo?.manifest?.body.toLowerCase().includes("break change"); return updateInfo?.body.toLowerCase().includes("break change");
}, [updateInfo]); }, [updateInfo]);
const onUpdate = useLockFn(async () => { const onUpdate = useLockFn(async () => {
@ -56,7 +56,7 @@ export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
Notice.error(t("Portable Updater Error")); Notice.error(t("Portable Updater Error"));
return; return;
} }
if (!updateInfo?.manifest?.body) return; if (!updateInfo?.body) return;
if (breakChangeFlag) { if (breakChangeFlag) {
Notice.error(t("Break Change Update Error")); Notice.error(t("Break Change Update Error"));
return; return;
@ -77,7 +77,7 @@ export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
} }
); );
try { try {
await installUpdate(); await updateInfo.install();
await relaunch(); await relaunch();
} catch (err: any) { } catch (err: any) {
Notice.error(err?.message || err.toString()); Notice.error(err?.message || err.toString());
@ -91,14 +91,14 @@ export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
open={open} open={open}
title={ title={
<Box display="flex" justifyContent="space-between"> <Box display="flex" justifyContent="space-between">
{`New Version v${updateInfo?.manifest?.version}`} {`New Version v${updateInfo?.version}`}
<Box> <Box>
<Button <Button
variant="contained" variant="contained"
size="small" size="small"
onClick={() => { onClick={() => {
openUrl( openUrl(
`https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/v${updateInfo?.manifest?.version}` `https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/v${updateInfo?.version}`
); );
}} }}
> >

View File

@ -1,14 +1,7 @@
import { useCallback, useRef } from "react"; import { useCallback, useRef } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { open } from "@tauri-apps/api/dialog"; import { open } from "@tauri-apps/plugin-dialog";
import { import { Button, MenuItem, Select, Input, Typography } from "@mui/material";
Button,
MenuItem,
Select,
Input,
Typography,
Box,
} from "@mui/material";
import { import {
exitApp, exitApp,
openAppDir, openAppDir,
@ -17,7 +10,7 @@ import {
openDevTools, openDevTools,
copyClashEnv, copyClashEnv,
} from "@/services/cmds"; } from "@/services/cmds";
import { checkUpdate } from "@tauri-apps/api/updater"; import { check as checkUpdate } from "@tauri-apps/plugin-updater";
import { useVerge } from "@/hooks/use-verge"; import { useVerge } from "@/hooks/use-verge";
import { version } from "@root/package.json"; import { version } from "@root/package.json";
import { DialogRef, Notice } from "@/components/base"; import { DialogRef, Notice } from "@/components/base";
@ -67,7 +60,7 @@ const SettingVerge = ({ onError }: Props) => {
const onCheckUpdate = async () => { const onCheckUpdate = async () => {
try { try {
const info = await checkUpdate(); const info = await checkUpdate();
if (!info?.shouldUpdate) { if (!info?.available) {
Notice.success(t("Currently on the Latest Version")); Notice.success(t("Currently on the Latest Version"));
} else { } else {
updateRef.current?.open(); updateRef.current?.open();
@ -191,7 +184,7 @@ const SettingVerge = ({ onError }: Props) => {
<> <>
<Button <Button
onClick={async () => { onClick={async () => {
const path = await open({ const selected = await open({
directory: false, directory: false,
multiple: false, multiple: false,
filters: [ filters: [
@ -201,9 +194,9 @@ const SettingVerge = ({ onError }: Props) => {
}, },
], ],
}); });
if (path?.length) { if (selected?.path.length) {
onChangeData({ startup_script: `${path}` }); onChangeData({ startup_script: `${selected.path}` });
patchVerge({ startup_script: `${path}` }); patchVerge({ startup_script: `${selected.path}` });
} }
}} }}
> >

View File

@ -19,7 +19,7 @@ import { TestBox } from "./test-box";
import delayManager from "@/services/delay"; import delayManager from "@/services/delay";
import { cmdTestDelay, downloadIconCache } from "@/services/cmds"; import { cmdTestDelay, downloadIconCache } from "@/services/cmds";
import { listen, UnlistenFn } from "@tauri-apps/api/event"; import { listen, UnlistenFn } from "@tauri-apps/api/event";
import { convertFileSrc } from "@tauri-apps/api/tauri"; import { convertFileSrc } from "@tauri-apps/api/core";
interface Props { interface Props {
id: string; id: string;

View File

@ -7,7 +7,7 @@ import { useTranslation } from "react-i18next";
import { useLocation, useRoutes } from "react-router-dom"; import { useLocation, useRoutes } from "react-router-dom";
import { List, Paper, ThemeProvider, SvgIcon } from "@mui/material"; import { List, Paper, ThemeProvider, SvgIcon } from "@mui/material";
import { listen } from "@tauri-apps/api/event"; import { listen } from "@tauri-apps/api/event";
import { appWindow } from "@tauri-apps/api/window"; import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow";
import { routers } from "./_routers"; import { routers } from "./_routers";
import { getAxios } from "@/services/api"; import { getAxios } from "@/services/api";
import { useVerge } from "@/hooks/use-verge"; import { useVerge } from "@/hooks/use-verge";
@ -28,6 +28,7 @@ import { getPortableFlag } from "@/services/cmds";
import { useNavigate } from "react-router-dom"; import { useNavigate } from "react-router-dom";
import React from "react"; import React from "react";
import { TransitionGroup, CSSTransition } from "react-transition-group"; import { TransitionGroup, CSSTransition } from "react-transition-group";
const appWindow = getCurrentWebviewWindow();
export let portableFlag = false; export let portableFlag = false;
dayjs.extend(relativeTime); dayjs.extend(relativeTime);

View File

@ -48,8 +48,8 @@ import { ConfigViewer } from "@/components/setting/mods/config-viewer";
import { throttle } from "lodash-es"; import { throttle } from "lodash-es";
import { BaseStyledTextField } from "@/components/base/base-styled-text-field"; import { BaseStyledTextField } from "@/components/base/base-styled-text-field";
import { listen } from "@tauri-apps/api/event"; import { listen } from "@tauri-apps/api/event";
import { readTextFile } from "@tauri-apps/api/fs"; import { readTextFile } from "@tauri-apps/plugin-fs";
import { readText } from "@tauri-apps/api/clipboard"; import { readText } from "@tauri-apps/plugin-clipboard-manager";
const ProfilePage = () => { const ProfilePage = () => {
const { t } = useTranslation(); const { t } = useTranslation();

View File

@ -1,5 +1,5 @@
import dayjs from "dayjs"; import dayjs from "dayjs";
import { invoke } from "@tauri-apps/api/tauri"; import { invoke } from "@tauri-apps/api/core";
import { Notice } from "@/components/base"; import { Notice } from "@/components/base";
export async function copyClashEnv() { export async function copyClashEnv() {