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/material": "^5.16.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",
"ahooks": "^3.8.0",
"axios": "^1.7.2",
@ -60,7 +68,7 @@
},
"devDependencies": {
"@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/js-cookie": "^3.0.6",
"@types/js-yaml": "^4.0.9",

View File

@ -38,8 +38,32 @@ importers:
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)
"@tauri-apps/api":
specifier: ^1.6.0
version: 1.6.0
specifier: 2.0.0-rc.4
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":
specifier: ^7.0.15
version: 7.0.15
@ -129,8 +153,8 @@ importers:
specifier: ^5.1.1
version: 5.1.1
"@tauri-apps/cli":
specifier: ^1.6.0
version: 1.6.0
specifier: 2.0.0-rc.8
version: 2.0.0-rc.8
"@types/fs-extra":
specifier: ^9.0.13
version: 9.0.13
@ -2076,111 +2100,158 @@ packages:
peerDependencies:
"@svgr/core": "*"
"@tauri-apps/api@1.6.0":
"@tauri-apps/api@2.0.0-rc.4":
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:
{
integrity: sha512-SNRwUD9nqGxY47mbY1CGTt/jqyQOU7Ps7Mx/mpgahL0FVUDiCEY/5L9QfEPPhEgccgcelEVn7i6aQHIkHyUtCA==,
integrity: sha512-XDjFLfCz7gOsKg9qFyK7S9EbrdcLhWE2bSTvHDpnFdMc+DcjMR4O3HuCO7SrPmmFUf86OknYFiuMPEUoHf0Fiw==,
}
engines: { node: ">= 10" }
cpu: [arm64]
os: [darwin]
"@tauri-apps/cli-darwin-x64@1.6.0":
"@tauri-apps/cli-darwin-x64@2.0.0-rc.8":
resolution:
{
integrity: sha512-g2/uDR/eeH2arvuawA4WwaEOqv/7jDO/ZLNI3JlBjP5Pk8GGb3Kdy0ro1xQzF94mtk2mOnOXa4dMgAet4sUJ1A==,
integrity: sha512-vYuITao7qq45jzTXRFdAcB+rVBULmofksWsKzoi+cgI8R0kcfB3bts+FRBdV3j+fkbpBQKpriW6Y6gdOzbf6Ow==,
}
engines: { node: ">= 10" }
cpu: [x64]
os: [darwin]
"@tauri-apps/cli-linux-arm-gnueabihf@1.6.0":
"@tauri-apps/cli-linux-arm-gnueabihf@2.0.0-rc.8":
resolution:
{
integrity: sha512-EVwf4oRkQyG8BpSrk0gqO7oA0sDM2MdNDtJpMfleYFEgCxLIOGZKNqaOW3M7U+0Y4qikmG3TtRK+ngc8Ymtrjg==,
integrity: sha512-iW1gJyRBvwxCeBEu+ain18v8FtFBhrsqepzuUyOy7hZJWUMPdKHJhbFVz6xp3RGZf5ycsLUr3ZhqMB3vKtv7iQ==,
}
engines: { node: ">= 10" }
cpu: [arm]
os: [linux]
"@tauri-apps/cli-linux-arm64-gnu@1.6.0":
"@tauri-apps/cli-linux-arm64-gnu@2.0.0-rc.8":
resolution:
{
integrity: sha512-YdpY17cAySrhK9dX4BUVEmhAxE2o+6skIEFg8iN/xrDwRxhaNPI9I80YXPatUTX54Kx55T5++25VJG9+3iw83A==,
integrity: sha512-k1HQHpu59B8JteXALCSdCQBUVb+ytsg1u/QEWPzCm7mFhzr7jqdfBjQVAKt0T5rdQQTcNtK341oB7izuXmJE2A==,
}
engines: { node: ">= 10" }
cpu: [arm64]
os: [linux]
"@tauri-apps/cli-linux-arm64-musl@1.6.0":
"@tauri-apps/cli-linux-arm64-musl@2.0.0-rc.8":
resolution:
{
integrity: sha512-4U628tuf2U8pMr4tIBJhEkrFwt+46dwhXrDlpdyWSZtnop5RJAVKHODm0KbWns4xGKfTW1F3r6sSv+2ZxLcISA==,
integrity: sha512-tMotP7J/dMqCL8zPCwtMwjeO3SK1yxU9bUMZ3mwdAtf+3xr2d2hpmGo4KXk5r2jQee9+dIC3IBXexFhrVJGRHA==,
}
engines: { node: ">= 10" }
cpu: [arm64]
os: [linux]
"@tauri-apps/cli-linux-x64-gnu@1.6.0":
"@tauri-apps/cli-linux-x64-gnu@2.0.0-rc.8":
resolution:
{
integrity: sha512-AKRzp76fVUaJyXj5KRJT9bJyhwZyUnRQU0RqIRqOtZCT5yr6qGP8rjtQ7YhCIzWrseBlOllc3Qvbgw3Yl0VQcA==,
integrity: sha512-AnLghADQgt4oyp4rQyjpKfGpdAqofd+SRO9kht5d4c0RSViqwHBjIxrCt9FDhBpBwwkC6T6NNuQqaofZSnOH6Q==,
}
engines: { node: ">= 10" }
cpu: [x64]
os: [linux]
"@tauri-apps/cli-linux-x64-musl@1.6.0":
"@tauri-apps/cli-linux-x64-musl@2.0.0-rc.8":
resolution:
{
integrity: sha512-0edIdq6aMBTaRMIXddHfyAFL361JqulLLd2Wi2aoOie7DkQ2MYh6gv3hA7NB9gqFwNIGE+xtJ4BkXIP2tSGPlg==,
integrity: sha512-jv/uiXI2K41/XMA2vnKMcpdv1tKvez70HHvBoIwHhelmXV5V6Tpu5fTMEAXbA6WbJNfcFRi0kd55YRBOS0DZcw==,
}
engines: { node: ">= 10" }
cpu: [x64]
os: [linux]
"@tauri-apps/cli-win32-arm64-msvc@1.6.0":
"@tauri-apps/cli-win32-arm64-msvc@2.0.0-rc.8":
resolution:
{
integrity: sha512-QwWpWk4ubcwJ1rljsRAmINgB2AwkyzZhpYbalA+MmzyYMREcdXWGkyixWbRZgqc6fEWEBmq5UG73qz5eBJiIKg==,
integrity: sha512-wctgwSfuyJ0lXBzQSzGq5TAy2m9VZichZPPJ7GitYzdHbqIRLmvxM1abRen7HikP1lpjdmjl96/kWQOebEV0qw==,
}
engines: { node: ">= 10" }
cpu: [arm64]
os: [win32]
"@tauri-apps/cli-win32-ia32-msvc@1.6.0":
"@tauri-apps/cli-win32-ia32-msvc@2.0.0-rc.8":
resolution:
{
integrity: sha512-Vtw0yxO9+aEFuhuxQ57ALG43tjECopRimRuKGbtZYDCriB/ty5TrT3QWMdy0dxBkpDTu3Rqsz30sbDzw6tlP3Q==,
integrity: sha512-BBjoAezNpHscGhBICHrt+X+IyJIh9jYnaEIGvbCIMcGB6sSDxvtttw4ef9+4jNh3JixotOR2mwIudTc1H9HffQ==,
}
engines: { node: ">= 10" }
cpu: [ia32]
os: [win32]
"@tauri-apps/cli-win32-x64-msvc@1.6.0":
"@tauri-apps/cli-win32-x64-msvc@2.0.0-rc.8":
resolution:
{
integrity: sha512-h54FHOvGi7+LIfRchzgZYSCHB1HDlP599vWXQQJ/XnwJY+6Rwr2E5bOe/EhqoG8rbGkfK0xX3KPAvXPbUlmggg==,
integrity: sha512-1pgDw2woeyDMvXctmqc53LBxsaAYqiRNUVgwvzlmzJDbUHv4+Pj4B1OGKQh1YKlsmks8LK3mK9uSbk+k1OLZhw==,
}
engines: { node: ">= 10" }
cpu: [x64]
os: [win32]
"@tauri-apps/cli@1.6.0":
"@tauri-apps/cli@2.0.0-rc.8":
resolution:
{
integrity: sha512-DBBpBl6GhTzm8ImMbKkfaZ4fDTykWrC7Q5OXP4XqD91recmDEn2LExuvuiiS3HYe7uP8Eb5B9NPHhqJb+Zo7qQ==,
integrity: sha512-hB6Pa0IaJs/As3Hc5IOiiK4Ilh9jspEA+2uPURCRQg4BkzQWwQzNdGQ4gjAzFNFAWPzy/4uuIXdnd1a+jC4wLA==,
}
engines: { node: ">= 10" }
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":
resolution:
{
@ -5959,50 +6030,82 @@ snapshots:
transitivePeerDependencies:
- 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
"@tauri-apps/cli-darwin-x64@1.6.0":
"@tauri-apps/cli-darwin-x64@2.0.0-rc.8":
optional: true
"@tauri-apps/cli-linux-arm-gnueabihf@1.6.0":
"@tauri-apps/cli-linux-arm-gnueabihf@2.0.0-rc.8":
optional: true
"@tauri-apps/cli-linux-arm64-gnu@1.6.0":
"@tauri-apps/cli-linux-arm64-gnu@2.0.0-rc.8":
optional: true
"@tauri-apps/cli-linux-arm64-musl@1.6.0":
"@tauri-apps/cli-linux-arm64-musl@2.0.0-rc.8":
optional: true
"@tauri-apps/cli-linux-x64-gnu@1.6.0":
"@tauri-apps/cli-linux-x64-gnu@2.0.0-rc.8":
optional: true
"@tauri-apps/cli-linux-x64-musl@1.6.0":
"@tauri-apps/cli-linux-x64-musl@2.0.0-rc.8":
optional: true
"@tauri-apps/cli-win32-arm64-msvc@1.6.0":
"@tauri-apps/cli-win32-arm64-msvc@2.0.0-rc.8":
optional: true
"@tauri-apps/cli-win32-ia32-msvc@1.6.0":
"@tauri-apps/cli-win32-ia32-msvc@2.0.0-rc.8":
optional: true
"@tauri-apps/cli-win32-x64-msvc@1.6.0":
"@tauri-apps/cli-win32-x64-msvc@2.0.0-rc.8":
optional: true
"@tauri-apps/cli@1.6.0":
"@tauri-apps/cli@2.0.0-rc.8":
optionalDependencies:
"@tauri-apps/cli-darwin-arm64": 1.6.0
"@tauri-apps/cli-darwin-x64": 1.6.0
"@tauri-apps/cli-linux-arm-gnueabihf": 1.6.0
"@tauri-apps/cli-linux-arm64-gnu": 1.6.0
"@tauri-apps/cli-linux-arm64-musl": 1.6.0
"@tauri-apps/cli-linux-x64-gnu": 1.6.0
"@tauri-apps/cli-linux-x64-musl": 1.6.0
"@tauri-apps/cli-win32-arm64-msvc": 1.6.0
"@tauri-apps/cli-win32-ia32-msvc": 1.6.0
"@tauri-apps/cli-win32-x64-msvc": 1.6.0
"@tauri-apps/cli-darwin-arm64": 2.0.0-rc.8
"@tauri-apps/cli-darwin-x64": 2.0.0-rc.8
"@tauri-apps/cli-linux-arm-gnueabihf": 2.0.0-rc.8
"@tauri-apps/cli-linux-arm64-gnu": 2.0.0-rc.8
"@tauri-apps/cli-linux-arm64-musl": 2.0.0-rc.8
"@tauri-apps/cli-linux-x64-gnu": 2.0.0-rc.8
"@tauri-apps/cli-linux-x64-musl": 2.0.0-rc.8
"@tauri-apps/cli-win32-arm64-msvc": 2.0.0-rc.8
"@tauri-apps/cli-win32-ia32-msvc": 2.0.0-rc.8
"@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":
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-dependencies]
tauri-build = { version="1", features = [] }
tauri-build = { version= "2.0.0-rc", features = [] }
[dependencies]
warp = "0.3"
@ -32,13 +32,19 @@ delay_timer = "0.11"
parking_lot = "0.12"
auto-launch = "0.5.0"
percent-encoding = "2.3.1"
window-shadows = { version = "0.2" }
window-shadows = { version = "0.2.2" }
tokio = { version = "1", features = ["full"] }
serde = { version = "1.0", features = ["derive"] }
reqwest = { version = "0.12", features = ["json", "rustls-tls"] }
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"] }
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]
runas = "=1.2.0"
deelevate = "0.2.0"
@ -46,6 +52,10 @@ winreg = "0.52.0"
[target.'cfg(target_os = "linux")'.dependencies]
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
[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 std::collections::{HashMap, VecDeque};
use sysproxy::{Autoproxy, Sysproxy};
use tauri::{api, Manager};
type CmdResult<T = ()> = Result<T, String>;
use tauri::Manager;
#[tauri::command]
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]
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() {
window.open_devtools();
} else {
@ -374,7 +374,6 @@ pub fn open_devtools(app_handle: tauri::AppHandle) {
pub fn exit_app(app_handle: tauri::AppHandle) {
let _ = resolve::save_window_size_position(&app_handle, true);
resolve::resolve_reset();
api::process::kill_children();
app_handle.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::log_err;
use crate::utils::dirs;
use anyhow::{bail, Result};
use anyhow::{bail, Context, Result};
use once_cell::sync::OnceCell;
use parking_lot::Mutex;
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 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;
#[derive(Debug)]
pub struct CoreManager {
app_handle: Arc<Mutex<Option<AppHandle>>>,
sidecar: Arc<Mutex<Option<CommandChild>>>,
#[allow(unused)]
use_service_mode: Arc<Mutex<bool>>,
}
@ -24,12 +27,14 @@ impl CoreManager {
static CORE_MANAGER: OnceCell<CoreManager> = OnceCell::new();
CORE_MANAGER.get_or_init(|| CoreManager {
app_handle: Arc::new(Mutex::new(None)),
sidecar: Arc::new(Mutex::new(None)),
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 {
// 启动clash
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 = 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::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)?
.args(["-t", "-d", test_dir, "-f", config_path])
.output()?;
if !output.status.success() {
let error = clash_api::parse_check_output(output.stdout.clone());
let error = match !error.is_empty() {
true => error,
false => output.stdout.clone(),
};
Logger::global().set_log(output.stdout);
bail!("{error}");
if !output.status.success() {
let stdout = String::from_utf8(output.stdout).unwrap_or_default();
let error = clash_api::parse_check_output(stdout.clone());
let error = match !error.is_empty() {
true => error,
false => stdout.clone(),
};
Logger::global().set_log(stdout.clone());
bail!("{error}");
}
}
Ok(())
@ -156,37 +171,54 @@ impl CoreManager {
let args = vec!["-d", app_dir, "-f", config_path];
let cmd = Command::new_sidecar(clash_core)?;
let (mut rx, cmd_child) = cmd.args(args).spawn()?;
let app_handle = self.app_handle.lock();
let mut sidecar = self.sidecar.lock();
*sidecar = Some(cmd_child);
drop(sidecar);
if let Some(app_handle) = app_handle.as_ref() {
let cmd = app_handle.shell().sidecar(clash_core)?;
let (mut rx, cmd_child) = cmd.args(args).spawn()?;
tauri::async_runtime::spawn(async move {
while let Some(event) = rx.recv().await {
match event {
CommandEvent::Stdout(line) => {
log::info!(target: "app", "[mihomo]: {line}");
Logger::global().set_log(line);
// 将pid写入文件中
crate::log_err!((|| {
let pid = cmd_child.pid();
let path = dirs::clash_pid_path()?;
fs::File::create(path)
.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(())
}
@ -268,7 +300,7 @@ impl CoreManager {
// 更新订阅
Config::generate().await?;
self.check_config()?;
self.check_config().await?;
// 清掉旧日志
Logger::global().clear_log();
@ -296,7 +328,7 @@ impl CoreManager {
Config::generate().await?;
// 检查订阅是否正常
self.check_config()?;
self.check_config().await?;
// 更新运行时订阅
let path = Config::generate_file(ConfigType::Run)?;

View File

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

View File

@ -3,11 +3,11 @@ use anyhow::{bail, Result};
use once_cell::sync::OnceCell;
use parking_lot::Mutex;
use std::{collections::HashMap, sync::Arc};
use tauri::{AppHandle, GlobalShortcutManager};
use tauri::AppHandle;
use tauri_plugin_global_shortcut::GlobalShortcutExt;
pub struct Hotkey {
current: Arc<Mutex<Vec<String>>>, // 保存当前的热键设置
app_handle: Arc<Mutex<Option<AppHandle>>>,
}
@ -21,9 +21,8 @@ impl Hotkey {
})
}
pub fn init(&self, app_handle: AppHandle) -> Result<()> {
*self.app_handle.lock() = Some(app_handle);
pub fn init(&self, app_handle: &AppHandle) -> Result<()> {
*self.app_handle.lock() = Some(app_handle.clone());
let verge = Config::verge();
if let Some(hotkeys) = verge.latest().hotkeys.as_ref() {
@ -49,18 +48,14 @@ impl Hotkey {
Ok(())
}
fn get_manager(&self) -> Result<impl GlobalShortcutManager> {
fn register(&self, hotkey: &str, func: &str) -> Result<()> {
let app_handle = self.app_handle.lock();
if app_handle.is_none() {
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<()> {
let mut manager = self.get_manager()?;
if manager.is_registered(hotkey)? {
if manager.is_registered(hotkey) {
manager.unregister(hotkey)?;
}
@ -71,17 +66,22 @@ impl Hotkey {
"clash_mode_direct" => || feat::change_clash_mode("direct".into()),
"toggle_system_proxy" => feat::toggle_system_proxy,
"toggle_tun_mode" => feat::toggle_tun_mode,
_ => bail!("invalid function \"{func}\""),
};
manager.register(hotkey, f)?;
let _ = manager.on_shortcut(hotkey, move |_, _, _| f());
log::info!(target: "app", "register hotkey {hotkey} {func}");
Ok(())
}
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}");
Ok(())
}
@ -153,8 +153,10 @@ impl Hotkey {
impl Drop for Hotkey {
fn drop(&mut self) {
if let Ok(mut manager) = self.get_manager() {
let _ = manager.unregister_all();
if let Some(app_handle) = self.app_handle.lock().as_ref() {
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},
};
use anyhow::Result;
use tauri::{
api, AppHandle, CustomMenuItem, Manager, SystemTrayEvent, SystemTrayMenu, SystemTrayMenuItem,
SystemTraySubmenu,
};
use tauri::menu::{MenuBuilder, MenuEvent, MenuItemBuilder, PredefinedMenuItem, SubmenuBuilder};
use tauri::tray::{MouseButton, MouseButtonState, TrayIcon, TrayIconBuilder, TrayIconEvent};
use tauri::{AppHandle, Manager};
pub struct 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 version = app_handle.package_info().version.to_string();
macro_rules! t {
($en: expr, $zh: expr) => {
if zh {
@ -28,80 +23,92 @@ impl Tray {
};
}
SystemTrayMenu::new()
.add_item(CustomMenuItem::new(
"open_window",
t!("Dashboard", "打开面板"),
))
.add_native_item(SystemTrayMenuItem::Separator)
.add_item(CustomMenuItem::new(
"rule_mode",
t!("Rule Mode", "规则模式"),
))
.add_item(CustomMenuItem::new(
"global_mode",
t!("Global Mode", "全局模式"),
))
.add_item(CustomMenuItem::new(
"direct_mode",
t!("Direct Mode", "直连模式"),
))
.add_native_item(SystemTrayMenuItem::Separator)
.add_item(CustomMenuItem::new(
"system_proxy",
t!("System Proxy", "系统代理"),
))
.add_item(CustomMenuItem::new("tun_mode", t!("TUN Mode", "Tun 模式")))
.add_item(CustomMenuItem::new(
"copy_env",
t!("Copy Env", "复制环境变量"),
))
.add_submenu(SystemTraySubmenu::new(
t!("Open Dir", "打开目录"),
SystemTrayMenu::new()
.add_item(CustomMenuItem::new(
"open_app_dir",
t!("App Dir", "应用目录"),
))
.add_item(CustomMenuItem::new(
"open_core_dir",
t!("Core Dir", "内核目录"),
))
.add_item(CustomMenuItem::new(
"open_logs_dir",
t!("Logs Dir", "日志目录"),
)),
))
.add_submenu(SystemTraySubmenu::new(
t!("More", "更多"),
SystemTrayMenu::new()
.add_item(CustomMenuItem::new(
"restart_clash",
t!("Restart Clash", "重启 Clash"),
))
.add_item(CustomMenuItem::new(
"restart_app",
t!("Restart App", "重启应用"),
))
.add_item(
CustomMenuItem::new("app_version", format!("Version {version}")).disabled(),
),
))
.add_native_item(SystemTrayMenuItem::Separator)
.add_item(CustomMenuItem::new("quit", t!("Quit", "退出")))
}
let version = app_handle.package_info().version.to_string();
let open_window = MenuItemBuilder::with_id("open_window", t!("Dashboard", "打开面板"))
.build(app_handle)?;
let rule_mode =
MenuItemBuilder::with_id("rule_mode", t!("Rule Mode", "规则模式")).build(app_handle)?;
let global_mode = MenuItemBuilder::with_id("global_mode", t!("Global Mode", "全局模式"))
.build(app_handle)?;
let direct_mode = MenuItemBuilder::with_id("direct_mode", t!("Direct Mode", "直连模式"))
.build(app_handle)?;
let system_proxy = MenuItemBuilder::with_id("system_proxy", t!("System Proxy", "系统代理"))
.build(app_handle)?;
let tun_mode =
MenuItemBuilder::with_id("tun_mode", t!("TUN Mode", "Tun 模式")).build(app_handle)?;
let copy_env = MenuItemBuilder::with_id("copy_env", t!("Copy Env", "复制环境变量"))
.build(app_handle)?;
let open_app_dir = MenuItemBuilder::with_id("open_app_dir", t!("App Dir", "应用目录"))
.build(app_handle)?;
let open_core_dir = MenuItemBuilder::with_id("open_core_dir", t!("Core Dir", "内核目录"))
.build(app_handle)?;
let open_logs_dir = MenuItemBuilder::with_id("open_logs_dir", t!("Logs Dir", "日志目录"))
.build(app_handle)?;
let open_dir = SubmenuBuilder::with_id(app_handle, "open_dir", t!("Open Dir", "打开目录"))
.items(&[&open_app_dir, &open_core_dir, &open_logs_dir])
.build()?;
let restart_clash =
MenuItemBuilder::with_id("restart_clash", t!("Restart Clash", "重启 Clash"))
.build(app_handle)?;
let restart_app = MenuItemBuilder::with_id("restart_app", t!("Restart App", "重启应用"))
.build(app_handle)?;
let app_version = MenuItemBuilder::with_id("app_version", format!("Version {version}"))
.build(app_handle)?;
let more = SubmenuBuilder::with_id(app_handle, "more", t!("More", "更多"))
.items(&[&restart_clash, &restart_app, &app_version])
.build()?;
let quit = MenuItemBuilder::with_id("quit", t!("Quit", "退出"))
.accelerator("CmdOrControl+Q")
.build(app_handle)?;
let separator = PredefinedMenuItem::separator(app_handle)?;
let menu = MenuBuilder::new(app_handle)
.items(&[
&open_window,
&separator,
&rule_mode,
&global_mode,
&direct_mode,
&separator,
&system_proxy,
&tun_mode,
&copy_env,
&open_dir,
&more,
&separator,
&quit,
])
.build()?;
let _ = TrayIconBuilder::with_id("verge_tray")
.menu(&menu)
.on_menu_event(Self::on_menu_event)
.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)?;
Ok(())
}
pub fn update_part(app_handle: &AppHandle) -> Result<()> {
let zh = { Config::verge().latest().language == Some("zh".into()) };
let version = app_handle.package_info().version.to_string();
macro_rules! t {
@ -124,225 +131,263 @@ impl Tray {
.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");
let _ = tray.get_item("global_mode").set_selected(mode == "global");
let _ = tray.get_item("direct_mode").set_selected(mode == "direct");
#[cfg(target_os = "linux")]
match mode.as_str() {
"rule" => {
if let Some(item) = menu.get("rule_mode") {
let _ = item
.as_menuitem()
.unwrap()
.set_text(t!("Rule Mode ✔", "规则模式 ✔"));
}
#[cfg(target_os = "linux")]
match mode.as_str() {
"rule" => {
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", "直连模式"));
}
"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 ✔", "直连模式 ✔"));
}
_ => {}
}
if let Some(item) = menu.get("global_mode") {
let _ = item
.as_menuitem()
.unwrap()
.set_text(t!("Global Mode", "全局模式"));
}
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);
#[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();
if let Some(item) = menu.get("direct_mode") {
let _ = item
.as_menuitem()
.unwrap()
.set_text(t!("Direct Mode", "直连模式"));
}
}
}
icon
} else {
#[cfg(target_os = "macos")]
let mut icon = match tray_icon.as_str() {
"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();
"global" => {
if let Some(item) = menu.get("rule_mode") {
let _ = item
.as_menuitem()
.unwrap()
.set_text(t!("Rule Mode", "规则模式"));
}
if let Some(item) = menu.get("global_mode") {
let _ = item
.as_menuitem()
.unwrap()
.set_text(t!("Global Mode ✔", "全局模式 ✔"));
}
if let Some(item) = menu.get("direct_mode") {
let _ = item
.as_menuitem()
.unwrap()
.set_text(t!("Direct Mode", "直连模式"));
}
}
}
icon
};
if *tun_mode {
#[cfg(target_os = "macos")]
let mut icon = match tray_icon.as_str() {
"monochrome" => include_bytes!("../../icons/tray-icon-tun-mono.ico").to_vec(),
"colorful" => include_bytes!("../../icons/tray-icon-tun.ico").to_vec(),
_ => include_bytes!("../../icons/tray-icon-tun-mono.ico").to_vec(),
};
#[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();
"direct" => {
if let Some(item) = menu.get("rule_mode") {
let _ = item
.as_menuitem()
.unwrap()
.set_text(t!("Rule Mode", "规则模式"));
}
if let Some(item) = menu.get("global_mode") {
let _ = item
.as_menuitem()
.unwrap()
.set_text(t!("Global Mode", "全局模式"));
}
if let Some(item) = menu.get("direct_mode") {
let _ = item
.as_menuitem()
.unwrap()
.set_text(t!("Direct Mode ✔", "直连模式 ✔"));
}
}
_ => {}
}
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);
let _ = tray.get_item("tun_mode").set_selected(*tun_mode);
#[cfg(target_os = "linux")]
{
if *system_proxy {
let _ = tray
.get_item("system_proxy")
.set_title(t!("System Proxy ✔", "系统代理 ✔"));
#[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();
}
}
icon
} else {
let _ = tray
.get_item("system_proxy")
.set_title(t!("System Proxy", "系统代理"));
}
#[cfg(target_os = "macos")]
let mut icon = match tray_icon.as_str() {
"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 {
let _ = tray
.get_item("tun_mode")
.set_title(t!("TUN Mode ✔", "Tun 模式 ✔"));
} else {
let _ = tray
.get_item("tun_mode")
.set_title(t!("TUN Mode", "Tun 模式"));
#[cfg(target_os = "macos")]
let mut icon = match tray_icon.as_str() {
"monochrome" => include_bytes!("../../icons/tray-icon-tun-mono.ico").to_vec(),
"colorful" => include_bytes!("../../icons/tray-icon-tun.ico").to_vec(),
_ => include_bytes!("../../icons/tray-icon-tun-mono.ico").to_vec(),
};
#[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 mut map = std::collections::HashMap::new();
map.insert(true, "on");
map.insert(false, "off");
map
};
let _ = tray.set_icon(Some(tauri::image::Image::from_bytes(&indication_icon)?));
if let Some(item) = menu.get("system_proxy") {
let item = item.as_check_menuitem().unwrap();
let _ = item.set_checked(mode == "system_proxy");
}
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();
let profiles = Config::profiles();
let profiles = profiles.latest();
if let Some(current_profile_uid) = profiles.get_current() {
let current_profile = profiles.get_item(&current_profile_uid);
current_profile_name = match &current_profile.unwrap().name {
Some(profile_name) => profile_name.to_string(),
None => current_profile_name,
#[cfg(target_os = "linux")]
{
if let Some(item) = menu.get("system_proxy") {
if *system_proxy {
let _ = item
.as_menuitem()
.unwrap()
.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!(
"Clash Verge {version}\n{}: {}\n{}: {}\n{}: {}",
t!("SysProxy", "系统代理"),
switch_map[system_proxy],
t!("TUN", "Tun模式"),
switch_map[tun_mode],
t!("Profile", "当前订阅"),
current_profile_name
));
let mut current_profile_name = "None".to_string();
let profiles = Config::profiles();
let profiles = profiles.latest();
if let Some(current_profile_uid) = profiles.get_current() {
let current_profile = profiles.get_item(&current_profile_uid);
current_profile_name = match &current_profile.unwrap().name {
Some(profile_name) => profile_name.to_string(),
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(())
}
pub fn on_click(app_handle: &AppHandle) {
let tray_event = { Config::verge().latest().tray_event.clone() };
let tray_event = tray_event.unwrap_or("main_window".into());
match tray_event.as_str() {
pub fn on_menu_event(app_handle: &AppHandle, event: MenuEvent) {
match event.id.as_ref() {
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(),
"main_window" => resolve::create_window(app_handle),
_ => {}
}
}
pub fn on_system_tray_event(app_handle: &AppHandle, event: SystemTrayEvent) {
match event {
#[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()),
_ => {}
},
"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" => tauri::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 anyhow::{bail, Result};
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() {
let handle = handle::Handle::global();
let app_handle = handle.app_handle.lock();
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() {
let _ = window.close();
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 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 = match env_type {
Some(env_type) => env_type,

View File

@ -11,7 +11,7 @@ mod feat;
mod utils;
use crate::utils::{init, resolve, server};
use tauri::{api, SystemTray};
use tauri::tray::TrayIconBuilder;
fn main() -> std::io::Result<()> {
// 单例检测
@ -34,14 +34,37 @@ fn main() -> std::io::Result<()> {
#[allow(unused_mut)]
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| {
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 {
resolve::resolve_setup(app).await;
});
Ok(())
})
.on_system_tray_event(core::tray::Tray::on_system_tray_event)
.invoke_handler(tauri::generate_handler![
// common
cmds::get_sys_proxy,
@ -97,26 +120,6 @@ fn main() -> std::io::Result<()> {
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
.build(tauri::generate_context!())
.expect("error while running tauri application");
@ -125,10 +128,6 @@ fn main() -> std::io::Result<()> {
tauri::RunEvent::ExitRequested { api, .. } => {
api.prevent_exit();
}
tauri::RunEvent::Updater(tauri::UpdaterEvent::Downloaded) => {
resolve::resolve_reset();
api::process::kill_children();
}
tauri::RunEvent::WindowEvent { label, event, .. } => {
if label == "main" {
match event {

View File

@ -2,10 +2,7 @@ use crate::core::handle;
use anyhow::Result;
use once_cell::sync::OnceCell;
use std::path::PathBuf;
use tauri::{
api::path::{data_dir, resource_dir},
Env,
};
use tauri::Manager;
#[cfg(not(feature = "verge-dev"))]
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));
}
Ok(data_dir()
.ok_or(anyhow::anyhow!("failed to get app home dir"))?
.join(APP_ID))
let handle = handle::Handle::global();
let app_handle = handle.app_handle.lock();
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
@ -58,10 +67,15 @@ pub fn app_resources_dir() -> Result<PathBuf> {
let handle = handle::Handle::global();
let app_handle = handle.app_handle.lock();
if let Some(app_handle) = app_handle.as_ref() {
let res_dir = resource_dir(app_handle.package_info(), &Env::default())
.ok_or(anyhow::anyhow!("failed to get the resource dir"))?
.join("resources");
return Ok(res_dir);
match app_handle.path().resource_dir() {
Ok(dir) => {
return Ok(dir.join("resources"));
}
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"))
}
@ -88,6 +102,10 @@ pub fn profiles_path() -> Result<PathBuf> {
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"))]
pub fn service_path() -> Result<PathBuf> {
Ok(app_resources_dir()?.join("clash-verge-service"))

View File

@ -4,10 +4,8 @@ use nanoid::nanoid;
use serde::{de::DeserializeOwned, Serialize};
use serde_yaml::{Mapping, Value};
use std::{fs, path::PathBuf, str::FromStr};
use tauri::{
api::shell::{open, Program},
Manager,
};
use tauri_plugin_shell::ShellExt;
/// read data from yaml as struct 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
/// use vscode by default
pub fn open_file(app: tauri::AppHandle, path: PathBuf) -> Result<()> {
#[cfg(target_os = "macos")]
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)
}
};
app.shell().open(path.to_string_lossy(), None).unwrap();
Ok(())
}

View File

@ -10,7 +10,8 @@ use log4rs::encode::pattern::PatternEncoder;
use std::fs::{self, DirEntry};
use std::path::PathBuf;
use std::str::FromStr;
use tauri::api::process::Command;
use tauri::AppHandle;
use tauri_plugin_shell::ShellExt;
/// initialize this instance's log file
fn init_log() -> Result<()> {
@ -296,43 +297,39 @@ pub fn init_scheme() -> Result<()> {
Ok(())
}
pub fn startup_script() -> Result<()> {
let path = {
pub async fn startup_script(app_handle: &AppHandle) -> Result<()> {
let script_path = {
let verge = Config::verge();
let verge = verge.latest();
verge.startup_script.clone().unwrap_or("".to_string())
};
if !path.is_empty() {
let mut shell = "";
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()?;
}
}
if script_path.is_empty() {
return Ok(());
}
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(())
}

View File

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

View File

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

View File

@ -1,82 +1,49 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"package": {
"productName": "Clash Verge",
"version": "1.7.7"
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"bundle": {
"active": true,
"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": {
"distDir": "../dist",
"devPath": "http://localhost:3000/",
"beforeBuildCommand": "pnpm run web:build",
"frontendDist": "../dist",
"beforeDevCommand": "pnpm run web:dev",
"beforeBuildCommand": "pnpm run web:build"
"devUrl": "http://localhost:3000/"
},
"tauri": {
"bundle": {
"active": true,
"identifier": "io.github.clash-verge-rev.clash-verge-rev",
"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."
},
"productName": "Clash Verge",
"version": "1.7.7",
"identifier": "io.github.clash-verge-rev.clash-verge-rev",
"plugins": {
"updater": {
"active": true,
"pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEQyOEMyRjBCQkVGOUJEREYKUldUZnZmbStDeStNMHU5Mmo1N24xQXZwSVRYbXA2NUpzZE5oVzlqeS9Bc0t6RVV4MmtwVjBZaHgK",
"endpoints": [
"https://download.clashverge.dev/https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update-proxy.json",
"https://github.com/clash-verge-rev/clash-verge-rev/releases/download/updater/update.json"
],
"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",
"tauri": {
"systemTray": {
"iconPath": "icons/tray-icon.ico"
},
"bundle": {
"identifier": "io.github.clash-verge-rev.clash-verge-rev",
"targets": ["deb", "rpm"],
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"bundle": {
"targets": ["deb", "rpm"],
"linux": {
"deb": {
"depends": ["openssl"],
"desktopTemplate": "./template/clash-verge.desktop",
@ -22,5 +18,10 @@
"obsoletes": ["clash-verge"]
}
}
},
"app": {
"trayIcon": {
"iconPath": "icons/tray-icon.ico"
}
}
}

View File

@ -1,20 +1,13 @@
{
"$schema": "../node_modules/@tauri-apps/cli/schema.json",
"tauri": {
"systemTray": {
"iconPath": "icons/tray-icon-mono.ico",
"iconAsTemplate": true
},
"bundle": {
"identifier": "io.github.clash-verge-rev.clash-verge-rev",
"targets": ["app", "dmg", "updater"],
"macOS": {
"frameworks": [],
"minimumSystemVersion": "10.15",
"exceptionDomain": "",
"signingIdentity": null,
"entitlements": null
},
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"bundle": {
"targets": ["app", "dmg"],
"macOS": {
"frameworks": [],
"minimumSystemVersion": "10.15",
"exceptionDomain": "",
"signingIdentity": null,
"entitlements": null,
"dmg": {
"background": "images/background.png",
"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",
"tauri": {
"systemTray": {
"iconPath": "icons/tray-icon.ico"
},
"bundle": {
"identifier": "io.github.clash-verge-rev.clash-verge-rev",
"targets": ["nsis", "updater"],
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": "",
"webviewInstallMode": {
"type": "embedBootstrapper",
"silent": true
},
"nsis": {
"displayLanguageSelector": true,
"installerIcon": "icons/icon.ico",
"languages": ["SimpChinese", "English"],
"license": "../LICENSE",
"installMode": "perMachine",
"template": "./template/installer.nsi"
}
"$schema": "../node_modules/@tauri-apps/cli/config.schema.json",
"bundle": {
"targets": ["nsis"],
"windows": {
"certificateThumbprint": null,
"digestAlgorithm": "sha256",
"timestampUrl": "",
"webviewInstallMode": {
"type": "embedBootstrapper",
"silent": true
},
"nsis": {
"displayLanguageSelector": true,
"installerIcon": "icons/icon.ico",
"languages": ["SimpChinese", "English"],
"installMode": "perMachine",
"template": "./template/installer.nsi"
}
}
},
"app": {
"trayIcon": {
"iconPath": "icons/tray-icon.ico",
"iconAsTemplate": true
}
}
}

View File

@ -7,7 +7,8 @@ import {
ErrorRounded,
} from "@mui/icons-material";
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 {
type: string;
duration?: number;

View File

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

View File

@ -1,7 +1,7 @@
import useSWR from "swr";
import { useRef } from "react";
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 { DialogRef } from "../base";
import { useVerge } from "@/hooks/use-verge";
@ -19,7 +19,7 @@ export const UpdateButton = (props: Props) => {
const { data: updateInfo } = useSWR(
auto_check_update || auto_check_update === null ? "checkUpdate" : null,
checkUpdate,
check,
{
errorRetryCount: 2,
revalidateIfStale: false,
@ -27,7 +27,7 @@ export const UpdateButton = (props: Props) => {
}
);
if (!updateInfo?.shouldUpdate) return null;
if (!updateInfo?.available) return null;
return (
<>

View File

@ -1,9 +1,10 @@
import { useEffect, useMemo } from "react";
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 { defaultTheme, defaultDarkTheme } from "@/pages/_theme";
import { useVerge } from "@/hooks/use-verge";
const appWindow = getCurrentWebviewWindow();
/**
* custom theme

View File

@ -18,7 +18,7 @@ import {
import { useThemeMode } from "@/services/states";
import { Notice } from "@/components/base";
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 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 mergeSchema from "meta-json-schema/schemas/clash-verge-merge-json-schema.json";
import pac from "types-pac/pac.d.ts?raw";
const appWindow = getCurrentWebviewWindow();
type Language = "yaml" | "javascript" | "css";
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 { CSS } from "@dnd-kit/utilities";
import { downloadIconCache } from "@/services/cmds";
import { convertFileSrc } from "@tauri-apps/api/tauri";
import { convertFileSrc } from "@tauri-apps/api/core";
import { useEffect, useState } from "react";
interface Props {
type: "prepend" | "original" | "delete" | "append";

View File

@ -30,7 +30,7 @@ import { EditorViewer } from "@/components/profile/editor-viewer";
import { ProfileBox } from "./profile-box";
import parseTraffic from "@/utils/parse-traffic";
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";
const round = keyframes`
from { transform: rotate(0deg); }

View File

@ -19,7 +19,7 @@ import type { IRenderItem } from "./use-render-list";
import { useVerge } from "@/hooks/use-verge";
import { useThemeMode } from "@/services/states";
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";
interface RenderProps {

View File

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

View File

@ -3,13 +3,13 @@ import { forwardRef, useImperativeHandle, useState, useMemo } from "react";
import { useLockFn } from "ahooks";
import { Box, LinearProgress, Button } from "@mui/material";
import { useTranslation } from "react-i18next";
import { relaunch } from "@tauri-apps/api/process";
import { checkUpdate, installUpdate } from "@tauri-apps/api/updater";
import { relaunch } from "@tauri-apps/plugin-process";
import { check as checkUpdate } from "@tauri-apps/plugin-updater";
import { BaseDialog, DialogRef, Notice } from "@/components/base";
import { useUpdateState, useSetUpdateState } from "@/services/states";
import { listen, Event, UnlistenFn } from "@tauri-apps/api/event";
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";
let eventListener: UnlistenFn | null = null;
@ -38,17 +38,17 @@ export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
}));
const markdownContent = useMemo(() => {
if (!updateInfo?.manifest?.body) {
if (!updateInfo?.body) {
return "New Version is available";
}
return updateInfo?.manifest?.body;
return updateInfo?.body;
}, [updateInfo]);
const breakChangeFlag = useMemo(() => {
if (!updateInfo?.manifest?.body) {
if (!updateInfo?.body) {
return false;
}
return updateInfo?.manifest?.body.toLowerCase().includes("break change");
return updateInfo?.body.toLowerCase().includes("break change");
}, [updateInfo]);
const onUpdate = useLockFn(async () => {
@ -56,7 +56,7 @@ export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
Notice.error(t("Portable Updater Error"));
return;
}
if (!updateInfo?.manifest?.body) return;
if (!updateInfo?.body) return;
if (breakChangeFlag) {
Notice.error(t("Break Change Update Error"));
return;
@ -77,7 +77,7 @@ export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
}
);
try {
await installUpdate();
await updateInfo.install();
await relaunch();
} catch (err: any) {
Notice.error(err?.message || err.toString());
@ -91,14 +91,14 @@ export const UpdateViewer = forwardRef<DialogRef>((props, ref) => {
open={open}
title={
<Box display="flex" justifyContent="space-between">
{`New Version v${updateInfo?.manifest?.version}`}
{`New Version v${updateInfo?.version}`}
<Box>
<Button
variant="contained"
size="small"
onClick={() => {
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 { useTranslation } from "react-i18next";
import { open } from "@tauri-apps/api/dialog";
import {
Button,
MenuItem,
Select,
Input,
Typography,
Box,
} from "@mui/material";
import { open } from "@tauri-apps/plugin-dialog";
import { Button, MenuItem, Select, Input, Typography } from "@mui/material";
import {
exitApp,
openAppDir,
@ -17,7 +10,7 @@ import {
openDevTools,
copyClashEnv,
} 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 { version } from "@root/package.json";
import { DialogRef, Notice } from "@/components/base";
@ -67,7 +60,7 @@ const SettingVerge = ({ onError }: Props) => {
const onCheckUpdate = async () => {
try {
const info = await checkUpdate();
if (!info?.shouldUpdate) {
if (!info?.available) {
Notice.success(t("Currently on the Latest Version"));
} else {
updateRef.current?.open();
@ -191,7 +184,7 @@ const SettingVerge = ({ onError }: Props) => {
<>
<Button
onClick={async () => {
const path = await open({
const selected = await open({
directory: false,
multiple: false,
filters: [
@ -201,9 +194,9 @@ const SettingVerge = ({ onError }: Props) => {
},
],
});
if (path?.length) {
onChangeData({ startup_script: `${path}` });
patchVerge({ startup_script: `${path}` });
if (selected?.path.length) {
onChangeData({ startup_script: `${selected.path}` });
patchVerge({ startup_script: `${selected.path}` });
}
}}
>

View File

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

View File

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

View File

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

View File

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