mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-10-18 23:30:51 +08:00
Compare commits
19
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
72d8b144a6 | ||
|
|
396d343a3a | ||
|
|
b729e59650 | ||
|
|
a3cc58013b | ||
|
|
cef66cd745 | ||
|
|
3b7bfa10fd | ||
|
|
b238175f18 | ||
|
|
8f00096a9f | ||
|
|
9f498a85d0 | ||
|
|
e014b913d2 | ||
|
|
f38ceae3fb | ||
|
|
089635c8bf | ||
|
|
dc5a8a5ff4 | ||
|
|
476ddb63c3 | ||
|
|
c6b51171a1 | ||
|
|
d4287970bc | ||
|
|
b20fdc9b96 | ||
|
|
5b10331022 | ||
|
|
3ec858eb9f |
@@ -4,11 +4,11 @@
|
||||
|
||||
"features": {
|
||||
"ghcr.io/devcontainers/features/node:2": {
|
||||
"version": "24.21.0",
|
||||
"pnpmVersion": "12.11.2"
|
||||
"version": "24.22.0",
|
||||
"pnpmVersion": "12.14.0"
|
||||
},
|
||||
"ghcr.io/devcontainers/features/rust:1": {
|
||||
"version": "1.99.0",
|
||||
"version": "1.99.1",
|
||||
"profile": "default"
|
||||
},
|
||||
"ghcr.io/devcontainers/features/git:1": {},
|
||||
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.21.0'
|
||||
node-version: '24.22.0'
|
||||
|
||||
- name: Install dependencies
|
||||
run: pnpm install --frozen-lockfile
|
||||
@@ -154,7 +154,7 @@ jobs:
|
||||
- name: Install Rust Stable
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: '1.99.0'
|
||||
toolchain: '1.99.1'
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
- name: Add Rust Target
|
||||
@@ -185,7 +185,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.21.0'
|
||||
node-version: '24.22.0'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Pnpm Cache
|
||||
@@ -206,7 +206,7 @@ jobs:
|
||||
|
||||
- name: Add Rust Target
|
||||
run: |
|
||||
rustup target add ${{ matrix.target }} --toolchain 1.99.0 || rustup target add ${{ matrix.target }}
|
||||
rustup target add ${{ matrix.target }} --toolchain 1.99.1 || rustup target add ${{ matrix.target }}
|
||||
rustup target list --installed
|
||||
echo "Rust target ${{ matrix.target }} installed."
|
||||
|
||||
@@ -257,7 +257,7 @@ jobs:
|
||||
- name: Install Rust Stable
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: '1.99.0'
|
||||
toolchain: '1.99.1'
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
- name: Add Rust Target
|
||||
@@ -282,7 +282,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.21.0'
|
||||
node-version: '24.22.0'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Pnpm Cache
|
||||
@@ -347,7 +347,7 @@ jobs:
|
||||
|
||||
- name: Add Rust Target
|
||||
run: |
|
||||
rustup target add ${{ matrix.target }} --toolchain 1.99.0 || rustup target add ${{ matrix.target }}
|
||||
rustup target add ${{ matrix.target }} --toolchain 1.99.1 || rustup target add ${{ matrix.target }}
|
||||
rustup target list --installed
|
||||
echo "Rust target ${{ matrix.target }} installed."
|
||||
|
||||
@@ -426,7 +426,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.21.0'
|
||||
node-version: '24.22.0'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Pnpm Cache
|
||||
@@ -454,7 +454,7 @@ jobs:
|
||||
|
||||
- name: Add Rust Target
|
||||
run: |
|
||||
rustup target add ${{ matrix.target }} --toolchain 1.99.0 || rustup target add ${{ matrix.target }}
|
||||
rustup target add ${{ matrix.target }} --toolchain 1.99.1 || rustup target add ${{ matrix.target }}
|
||||
rustup target list --installed
|
||||
echo "Rust target ${{ matrix.target }} installed."
|
||||
|
||||
@@ -524,7 +524,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.21.0'
|
||||
node-version: '24.22.0'
|
||||
|
||||
- uses: pnpm/action-setup@v6.1.0
|
||||
name: Install pnpm
|
||||
|
||||
@@ -43,7 +43,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.21.0'
|
||||
node-version: '24.22.0'
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
name: Install pnpm
|
||||
|
||||
@@ -74,7 +74,7 @@ jobs:
|
||||
|
||||
- name: Install Rust Stable
|
||||
if: github.event.inputs[matrix.input] == 'true'
|
||||
uses: dtolnay/rust-toolchain@1.99.0
|
||||
uses: dtolnay/rust-toolchain@1.99.1
|
||||
|
||||
- name: Rust Cache
|
||||
uses: Swatinem/rust-cache@v2
|
||||
@@ -103,7 +103,7 @@ jobs:
|
||||
if: github.event.inputs[matrix.input] == 'true'
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.21.0'
|
||||
node-version: '24.22.0'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Pnpm Cache
|
||||
@@ -129,7 +129,7 @@ jobs:
|
||||
if: github.event.inputs[matrix.input] == 'true'
|
||||
run: |
|
||||
# Ensure cross target is installed for the pinned toolchain; fallback without explicit toolchain if needed
|
||||
rustup target add ${{ matrix.target }} --toolchain 1.99.0 || rustup target add ${{ matrix.target }}
|
||||
rustup target add ${{ matrix.target }} --toolchain 1.99.1 || rustup target add ${{ matrix.target }}
|
||||
rustup target list --installed
|
||||
echo "Rust target ${{ matrix.target }} installed."
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ jobs:
|
||||
- uses: actions/setup-node@v7
|
||||
if: steps.check_frontend.outputs.frontend == 'true'
|
||||
with:
|
||||
node-version: '24.21.0'
|
||||
node-version: '24.22.0'
|
||||
cache: 'pnpm'
|
||||
|
||||
- name: Restore pnpm cache
|
||||
@@ -138,7 +138,7 @@ jobs:
|
||||
- uses: actions/setup-node@v7
|
||||
if: steps.check_inputs.outputs.contract == 'true'
|
||||
with:
|
||||
node-version: '24.21.0'
|
||||
node-version: '24.22.0'
|
||||
cache: 'pnpm'
|
||||
|
||||
- run: pnpm install --frozen-lockfile
|
||||
|
||||
@@ -204,7 +204,7 @@ jobs:
|
||||
- name: Install Rust Stable
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: '1.99.0'
|
||||
toolchain: '1.99.1'
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
- name: Add Rust Target
|
||||
@@ -230,7 +230,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.21.0'
|
||||
node-version: '24.22.0'
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
name: Install pnpm
|
||||
@@ -244,7 +244,7 @@ jobs:
|
||||
|
||||
- name: Add Rust Target
|
||||
run: |
|
||||
rustup target add ${{ matrix.target }} --toolchain 1.99.0 || rustup target add ${{ matrix.target }}
|
||||
rustup target add ${{ matrix.target }} --toolchain 1.99.1 || rustup target add ${{ matrix.target }}
|
||||
rustup target list --installed
|
||||
echo "Rust target ${{ matrix.target }} installed."
|
||||
|
||||
@@ -311,7 +311,7 @@ jobs:
|
||||
- name: Install Rust Stable
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: '1.99.0'
|
||||
toolchain: '1.99.1'
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
- name: Add Rust Target
|
||||
@@ -331,7 +331,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.21.0'
|
||||
node-version: '24.22.0'
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v6
|
||||
@@ -389,7 +389,7 @@ jobs:
|
||||
|
||||
- name: Add Rust Target
|
||||
run: |
|
||||
rustup target add ${{ matrix.target }} --toolchain 1.99.0 || rustup target add ${{ matrix.target }}
|
||||
rustup target add ${{ matrix.target }} --toolchain 1.99.1 || rustup target add ${{ matrix.target }}
|
||||
rustup target list --installed
|
||||
echo "Rust target ${{ matrix.target }} installed."
|
||||
|
||||
@@ -446,7 +446,7 @@ jobs:
|
||||
- name: Install Rust Stable
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: '1.99.0'
|
||||
toolchain: '1.99.1'
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
- name: Add Rust Target
|
||||
@@ -466,7 +466,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.21.0'
|
||||
node-version: '24.22.0'
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
name: Install pnpm
|
||||
@@ -487,7 +487,7 @@ jobs:
|
||||
|
||||
- name: Add Rust Target
|
||||
run: |
|
||||
rustup target add ${{ matrix.target }} --toolchain 1.99.0 || rustup target add ${{ matrix.target }}
|
||||
rustup target add ${{ matrix.target }} --toolchain 1.99.1 || rustup target add ${{ matrix.target }}
|
||||
rustup target list --installed
|
||||
echo "Rust target ${{ matrix.target }} installed."
|
||||
|
||||
@@ -547,7 +547,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.21.0'
|
||||
node-version: '24.22.0'
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
name: Install pnpm
|
||||
@@ -573,7 +573,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.21.0'
|
||||
node-version: '24.22.0'
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
name: Install pnpm
|
||||
@@ -638,7 +638,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.21.0'
|
||||
node-version: '24.22.0'
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
name: Install pnpm
|
||||
|
||||
@@ -22,13 +22,13 @@ jobs:
|
||||
- name: Install Rust
|
||||
uses: dtolnay/rust-toolchain@master
|
||||
with:
|
||||
toolchain: '1.99.0'
|
||||
toolchain: '1.99.1'
|
||||
targets: x86_64-pc-windows-msvc
|
||||
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.21.0'
|
||||
node-version: '24.22.0'
|
||||
|
||||
- name: Install pnpm
|
||||
uses: pnpm/action-setup@v6
|
||||
|
||||
@@ -34,7 +34,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.21.0'
|
||||
node-version: '24.22.0'
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
name: Install pnpm
|
||||
|
||||
@@ -15,7 +15,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.21.0'
|
||||
node-version: '24.22.0'
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
name: Install pnpm
|
||||
@@ -39,7 +39,7 @@ jobs:
|
||||
- name: Install Node
|
||||
uses: actions/setup-node@v7
|
||||
with:
|
||||
node-version: '24.21.0'
|
||||
node-version: '24.22.0'
|
||||
|
||||
- uses: pnpm/action-setup@v6
|
||||
name: Install pnpm
|
||||
|
||||
+3
-3
@@ -1,3 +1,3 @@
|
||||
nodejs 24.21.0
|
||||
pnpm 12.11.2
|
||||
rust 1.99.0
|
||||
nodejs 24.22.0
|
||||
pnpm 12.14.0
|
||||
rust 1.99.1
|
||||
|
||||
Generated
+65
-159
@@ -1119,7 +1119,7 @@ dependencies = [
|
||||
"getrandom 0.4.3",
|
||||
"libc",
|
||||
"log",
|
||||
"minisign-verify 0.3.0",
|
||||
"minisign-verify",
|
||||
"nanoid",
|
||||
"network-interface",
|
||||
"objc2",
|
||||
@@ -4167,12 +4167,6 @@ dependencies = [
|
||||
"unicase",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minisign-verify"
|
||||
version = "0.2.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "22f9645cb765ea72b8111f36c522475d2daa0d22c957a9826437e97534bc4e9e"
|
||||
|
||||
[[package]]
|
||||
name = "minisign-verify"
|
||||
version = "0.3.0"
|
||||
@@ -6622,7 +6616,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1e362d9935bc50f019969e2f9ecd66786612daae13e8f277be7bfb66e8bed3f7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"sigchld",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
@@ -6642,17 +6635,6 @@ version = "2.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
|
||||
|
||||
[[package]]
|
||||
name = "sigchld"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47106eded3c154e70176fc83df9737335c94ce22f821c32d17ed1db1f83badb1"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"os_pipe",
|
||||
"signal-hook 0.3.18",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.1.17"
|
||||
@@ -6663,16 +6645,6 @@ dependencies = [
|
||||
"signal-hook-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook"
|
||||
version = "0.3.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"signal-hook-registry",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "signal-hook-registry"
|
||||
version = "1.4.8"
|
||||
@@ -6878,6 +6850,12 @@ version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
|
||||
|
||||
[[package]]
|
||||
name = "static_vcruntime"
|
||||
version = "3.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ff7589a1859b09232522bb9edf076ca7fcf14ded96685d3fa629db73023ffbb7"
|
||||
|
||||
[[package]]
|
||||
name = "string_cache"
|
||||
version = "0.9.0"
|
||||
@@ -6937,9 +6915,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
|
||||
|
||||
[[package]]
|
||||
name = "swift-rs"
|
||||
version = "1.0.8"
|
||||
version = "1.0.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e45c444e496845d3f2a351146bff59aae4975b2280238df1dfaa0c7d1846f38e"
|
||||
checksum = "cefd87076fd385308ee4aff597256c902f1fbb4d031de1558efc12f8ffefadd9"
|
||||
dependencies = [
|
||||
"base64 0.21.7",
|
||||
"serde",
|
||||
@@ -7186,9 +7164,9 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
|
||||
|
||||
[[package]]
|
||||
name = "tauri"
|
||||
version = "2.12.1"
|
||||
version = "2.12.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed99ee9694a2deb776d91cae48ac7411ddfc89ecae2f9b5041111d8c88f2ace9"
|
||||
checksum = "f316751027fc2644221d1a448564d519efb07067fda584879d9cda966391d212"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"bytes",
|
||||
@@ -7234,16 +7212,16 @@ dependencies = [
|
||||
"tray-icon",
|
||||
"url",
|
||||
"webkit2gtk",
|
||||
"webview2-com 0.39.1",
|
||||
"webview2-com",
|
||||
"window-vibrancy",
|
||||
"windows 0.62.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-build"
|
||||
version = "2.7.1"
|
||||
version = "2.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "59563ca5b331fd97f27ee9672ec8d36d33bb3c9e2c9f6710a1a5687256052e45"
|
||||
checksum = "641dd5fd297c99ab92717b0428cca5b9c6f960b2ac96ca131b3eebc9d70b8ddc"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"cargo_toml",
|
||||
@@ -7255,6 +7233,8 @@ dependencies = [
|
||||
"semver 1.0.28",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"static_vcruntime",
|
||||
"strsim",
|
||||
"tauri-utils",
|
||||
"tauri-winres",
|
||||
"walkdir",
|
||||
@@ -7262,11 +7242,11 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-codegen"
|
||||
version = "2.7.1"
|
||||
version = "2.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e50213e463086ecc283d7e9f51c30cc0fd304ad457425158018be26dbf0d0a80"
|
||||
checksum = "a6585c5b536c00f039d12ce70a9755172d5423bbcfd3969863baa70314c3e02f"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"base64 0.23.1",
|
||||
"brotli",
|
||||
"ico",
|
||||
"json-patch",
|
||||
@@ -7289,9 +7269,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-macros"
|
||||
version = "2.7.1"
|
||||
version = "2.7.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8129fb43bb6e0d6b73ad3315cd172beaab17cd28899c594b1b32c80edbbfd99c"
|
||||
checksum = "e2e83a0cfa1bae376e8fbbdc0b1692707aed50cd6072ce85cae4789af5d6c19f"
|
||||
dependencies = [
|
||||
"heck 0.5.0",
|
||||
"proc-macro2",
|
||||
@@ -7347,9 +7327,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-clipboard-manager"
|
||||
version = "2.4.1"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "333d3b6beeb1c2c7fc040233d4ea7b6c03a9f8f039592b7400833c43fb5b568a"
|
||||
checksum = "9be5ed8d94c33afe8377e885119069f6508780659271d0a25cfc54d441f2bc81"
|
||||
dependencies = [
|
||||
"arboard",
|
||||
"log",
|
||||
@@ -7362,9 +7342,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-deep-link"
|
||||
version = "2.6.1"
|
||||
version = "2.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4ac7a92a46ab5c88f44532ca50906d6e448a948d4ddf8c5376ab125e3260f736"
|
||||
checksum = "1b3c64e24ebcbc63aaa2d88a9c51e2507084b46620baf39ecb0baa0738a08bb4"
|
||||
dependencies = [
|
||||
"dunce",
|
||||
"plist",
|
||||
@@ -7410,9 +7390,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-dialog"
|
||||
version = "2.8.1"
|
||||
version = "2.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "daf9a5c92e39bdd84f22f6be9230e39d0c4e140345f71ffe6058dca4f71127cf"
|
||||
checksum = "b7461aaacf1901915ea7b9f5cd74f8224a351ed96af0a62c07fce934003cb34b"
|
||||
dependencies = [
|
||||
"log",
|
||||
"raw-window-handle",
|
||||
@@ -7428,9 +7408,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-fs"
|
||||
version = "2.6.0"
|
||||
version = "2.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "466eb32466fe5021b6728b06862f9ff14a9f7e1af890047654e03a96d7c0e1bb"
|
||||
checksum = "5d58eb1d4984e535f47a642ffec09a2da6f5c18a12b5902d12126cb7626d628b"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"dunce",
|
||||
@@ -7452,9 +7432,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-global-shortcut"
|
||||
version = "2.4.0"
|
||||
version = "2.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0bd2e1f725891a1613af8f25bf918ba3357654b0fd3f2d794c0ce76cbdd92ed2"
|
||||
checksum = "93ff17919fe09852d269bd37b1d3d2e993b9dbb514afe7acbf3346c1d3627e2d"
|
||||
dependencies = [
|
||||
"global-hotkey",
|
||||
"log",
|
||||
@@ -7467,9 +7447,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-http"
|
||||
version = "2.8.0"
|
||||
version = "2.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "93e9d6b3319e101bc1be27cfa0fb4054d7784a12509c67ead0033b82180e5ed1"
|
||||
checksum = "2c57e3451553f95a1db3bfed6a358a77ce5080b6399a242c6eadac113f97abd2"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"cookie_store",
|
||||
@@ -7486,13 +7466,13 @@ dependencies = [
|
||||
"thiserror 2.0.20",
|
||||
"tokio",
|
||||
"url",
|
||||
"urlpattern 0.3.0",
|
||||
"urlpattern",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-mihomo"
|
||||
version = "0.6.0"
|
||||
source = "git+https://github.com/clash-verge-rev/tauri-plugin-mihomo?branch=main#7a03d16f4d39b1bb885b7babe66b9e153e34b615"
|
||||
source = "git+https://github.com/clash-verge-rev/tauri-plugin-mihomo?rev=7a03d16f4d39b1bb885b7babe66b9e153e34b615#7a03d16f4d39b1bb885b7babe66b9e153e34b615"
|
||||
dependencies = [
|
||||
"arc-swap",
|
||||
"clashmap",
|
||||
@@ -7516,9 +7496,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-notification"
|
||||
version = "2.5.1"
|
||||
version = "2.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "533770889abb60a0407d5a32096182f0e6463f5a3dfaa675be94580aa14070e1"
|
||||
checksum = "b939d7b19c8af1381a72ef8474b3aba2b56c1ddac2c23f0c8e96abbcf86b198b"
|
||||
dependencies = [
|
||||
"log",
|
||||
"notify-rust",
|
||||
@@ -7531,13 +7511,14 @@ dependencies = [
|
||||
"thiserror 2.0.20",
|
||||
"time",
|
||||
"url",
|
||||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-opener"
|
||||
version = "2.7.0"
|
||||
version = "2.7.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d39404b2572927e0d3ed30a7ca226037f7508d22e79c96ee6ef0d42b6a4b3345"
|
||||
checksum = "53810be1b4fed22bb6ac3f4cab5a2b80d9391d1af436bda145607532bc0bb219"
|
||||
dependencies = [
|
||||
"dunce",
|
||||
"glob",
|
||||
@@ -7567,11 +7548,12 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-shell"
|
||||
version = "2.4.0"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "094bdbc47e5efe36544ba21b3a53ab5c724150236cf55e6c9be46191deb6eab6"
|
||||
checksum = "8567d42b2c7264355daa1df415ec77f52aa42e885924c86a969be2e920e05521"
|
||||
dependencies = [
|
||||
"encoding_rs",
|
||||
"libc",
|
||||
"log",
|
||||
"open",
|
||||
"os_pipe",
|
||||
@@ -7584,13 +7566,14 @@ dependencies = [
|
||||
"tauri-plugin",
|
||||
"thiserror 2.0.20",
|
||||
"tokio",
|
||||
"windows-sys 0.60.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-updater"
|
||||
version = "2.13.1"
|
||||
version = "2.14.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3cb0b2ea3e85ca287990d3859a29cc5cb18024240fd78f62e027fb7963670f18"
|
||||
checksum = "7259989d4fe11b2bf51f7d671642249b1837c4ae2da1430dc713ae38f98ceeec"
|
||||
dependencies = [
|
||||
"base64 0.22.1",
|
||||
"dirs 7.0.0",
|
||||
@@ -7598,8 +7581,9 @@ dependencies = [
|
||||
"futures-util",
|
||||
"http",
|
||||
"infer",
|
||||
"libc",
|
||||
"log",
|
||||
"minisign-verify 0.2.5",
|
||||
"minisign-verify",
|
||||
"osakit",
|
||||
"percent-encoding",
|
||||
"reqwest 0.13.5",
|
||||
@@ -7621,9 +7605,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-plugin-wdio-webdriver"
|
||||
version = "1.4.0"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52c5e97428174a52b7f4357bbc4535fcce52c6a06c8ee7a379502f7cda1b6045"
|
||||
checksum = "4f76676d510d271d80abcf8b0552987b5b4f0eae5e3f906e9b5660d43c97c62e"
|
||||
dependencies = [
|
||||
"async-trait",
|
||||
"axum",
|
||||
@@ -7647,9 +7631,9 @@ dependencies = [
|
||||
"tracing",
|
||||
"uuid",
|
||||
"webkit2gtk",
|
||||
"webview2-com 0.38.2",
|
||||
"windows 0.61.3",
|
||||
"windows-core 0.61.2",
|
||||
"webview2-com",
|
||||
"windows 0.62.2",
|
||||
"windows-core 0.62.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@@ -7669,9 +7653,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime"
|
||||
version = "2.12.1"
|
||||
version = "2.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c2a57a2a9b8c8b7fc00d35b510fe808ded7f9f1b44fc1cdda6aee38632ea5413"
|
||||
checksum = "7c55766d99cea69055904d1735b93c2049302b2e09effd462120deb5581d78e9"
|
||||
dependencies = [
|
||||
"cookie",
|
||||
"dpi",
|
||||
@@ -7688,15 +7672,15 @@ dependencies = [
|
||||
"thiserror 2.0.20",
|
||||
"url",
|
||||
"webkit2gtk",
|
||||
"webview2-com 0.39.1",
|
||||
"webview2-com",
|
||||
"windows 0.62.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tauri-runtime-wry"
|
||||
version = "2.12.1"
|
||||
version = "2.12.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f9e7e0c9f80a5130773cfabb6df4dc924518fb1b2c923726966804634b57d22"
|
||||
checksum = "8d61892587aea2a85245bcaec98c6fe46268874c0a0deae5535fb71ca7b93eae"
|
||||
dependencies = [
|
||||
"gtk",
|
||||
"http",
|
||||
@@ -7714,7 +7698,7 @@ dependencies = [
|
||||
"tracing",
|
||||
"url",
|
||||
"webkit2gtk",
|
||||
"webview2-com 0.39.1",
|
||||
"webview2-com",
|
||||
"windows 0.62.2",
|
||||
"wry",
|
||||
]
|
||||
@@ -7752,9 +7736,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "tauri-utils"
|
||||
version = "2.10.1"
|
||||
version = "2.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2ff55a614843b9a3f010f211df637c94df2607175b8d0f6e0f4a7c19e18621ee"
|
||||
checksum = "362ab21cd4cc713f2a02b1a9b5688198b1541cfb6907c4281e9190e0f0ef4aa3"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"brotli",
|
||||
@@ -7783,7 +7767,7 @@ dependencies = [
|
||||
"thiserror 2.0.20",
|
||||
"toml 1.1.6+spec-1.1.0",
|
||||
"url",
|
||||
"urlpattern 0.6.0",
|
||||
"urlpattern",
|
||||
"uuid",
|
||||
"walkdir",
|
||||
]
|
||||
@@ -7902,7 +7886,7 @@ dependencies = [
|
||||
"regex",
|
||||
"semver 0.11.0",
|
||||
"sha2 0.9.9",
|
||||
"signal-hook 0.1.17",
|
||||
"signal-hook",
|
||||
"terminfo",
|
||||
"termios",
|
||||
"thiserror 1.0.69",
|
||||
@@ -8592,47 +8576,6 @@ dependencies = [
|
||||
"windows-sys 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unic-char-property"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"
|
||||
dependencies = [
|
||||
"unic-char-range",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unic-char-range"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"
|
||||
|
||||
[[package]]
|
||||
name = "unic-common"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
|
||||
|
||||
[[package]]
|
||||
name = "unic-ucd-ident"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987"
|
||||
dependencies = [
|
||||
"unic-char-property",
|
||||
"unic-char-range",
|
||||
"unic-ucd-version",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unic-ucd-version"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"
|
||||
dependencies = [
|
||||
"unic-common",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicase"
|
||||
version = "2.9.0"
|
||||
@@ -8692,18 +8635,6 @@ dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urlpattern"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d"
|
||||
dependencies = [
|
||||
"regex",
|
||||
"serde",
|
||||
"unic-ucd-ident",
|
||||
"url",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "urlpattern"
|
||||
version = "0.6.0"
|
||||
@@ -9095,20 +9026,6 @@ dependencies = [
|
||||
"rustls-pki-types",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webview2-com"
|
||||
version = "0.38.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a"
|
||||
dependencies = [
|
||||
"webview2-com-macros",
|
||||
"webview2-com-sys 0.38.2",
|
||||
"windows 0.61.3",
|
||||
"windows-core 0.61.2",
|
||||
"windows-implement",
|
||||
"windows-interface",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webview2-com"
|
||||
version = "0.39.1"
|
||||
@@ -9116,7 +9033,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f89fca7a704cee10dcb3654c1dbb8941d1783132f1917358af75bec37a7d7e6"
|
||||
dependencies = [
|
||||
"webview2-com-macros",
|
||||
"webview2-com-sys 0.39.1",
|
||||
"webview2-com-sys",
|
||||
"windows 0.62.2",
|
||||
"windows-core 0.62.2",
|
||||
]
|
||||
@@ -9132,17 +9049,6 @@ dependencies = [
|
||||
"syn 2.0.119",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webview2-com-sys"
|
||||
version = "0.38.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c"
|
||||
dependencies = [
|
||||
"thiserror 2.0.20",
|
||||
"windows 0.61.3",
|
||||
"windows-core 0.61.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "webview2-com-sys"
|
||||
version = "0.39.1"
|
||||
@@ -9882,7 +9788,7 @@ dependencies = [
|
||||
"url",
|
||||
"webkit2gtk",
|
||||
"webkit2gtk-sys",
|
||||
"webview2-com 0.39.1",
|
||||
"webview2-com",
|
||||
"windows 0.62.2",
|
||||
"windows-core 0.62.2",
|
||||
"windows-version",
|
||||
|
||||
+2
-2
@@ -61,8 +61,8 @@ tracing-estuary = { git = "https://github.com/Tunglies/tracing-estuary.git", bra
|
||||
tauri-plugin-clash-verge-sysinfo = { path = "crates/tauri-plugin-clash-verge-sysinfo" }
|
||||
tracing = "0.1.44"
|
||||
|
||||
tauri = { version = "2.12.1" }
|
||||
tauri-plugin-clipboard-manager = "2.4.1"
|
||||
tauri = { version = "2.12.4" }
|
||||
tauri-plugin-clipboard-manager = "2.5.0"
|
||||
parking_lot = { version = "0.12.5", features = ["hardware-lock-elision"] }
|
||||
anyhow = "1.0.104"
|
||||
chrono = "0.4.45"
|
||||
|
||||
+7
-2
@@ -3,8 +3,13 @@
|
||||
<details>
|
||||
<summary><strong> 🐞 修复问题 </strong></summary>
|
||||
|
||||
- 修复通过 URL 导入订阅时,即使导入的订阅并非当前使用的订阅,也会重新加载当前配置的问题
|
||||
- 修复「首页」网站测速失败后卡片一直显示测试中的问题
|
||||
- 修复通过 URL 导入订阅时,即使导入的订阅并非当前使用的订阅,也会重新加载当前配置
|
||||
- 修复 首页 网站测速失败后卡片一直显示测试中
|
||||
- 修复 代理 页展开分组后不显示最新测速结果,需切换节点才刷新
|
||||
|
||||
**🐧 Linux**
|
||||
|
||||
- Linux:修复 deb/rpm 安装包错误包含多余的 `frontend-contract` 可执行文件,导致下游重新打包失败的问题
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
@@ -1,3 +1,15 @@
|
||||
//! Linux-only termination signal bridge.
|
||||
//!
|
||||
//! Upstream Tauri delivers session-end and termination events to the app on
|
||||
//! Windows and macOS, but has no equivalent on Linux: a desktop session may
|
||||
//! end without any window or run event reaching the process. Linux builds
|
||||
//! keep this crate to listen for SIGTERM, SIGINT and SIGHUP and forward them
|
||||
//! to the app quit flow, so system proxy and TUN cleanup still runs before
|
||||
//! the process dies. Non-Linux builds compile this crate to nothing and must
|
||||
//! not depend on it.
|
||||
|
||||
#![cfg(target_os = "linux")]
|
||||
|
||||
use std::sync::{
|
||||
OnceLock,
|
||||
atomic::{AtomicBool, Ordering},
|
||||
@@ -5,20 +17,13 @@ use std::sync::{
|
||||
|
||||
use clash_verge_logging::{Type, logging};
|
||||
|
||||
#[cfg(unix)]
|
||||
mod unix;
|
||||
#[cfg(windows)]
|
||||
mod windows;
|
||||
|
||||
pub(crate) static RUNTIME: OnceLock<Option<tokio::runtime::Runtime>> = OnceLock::new();
|
||||
pub(crate) static SHUTDOWN_LATCH: ShutdownLatch = ShutdownLatch::new();
|
||||
|
||||
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
|
||||
pub enum ShutdownOutcome {
|
||||
Committed,
|
||||
Canceled,
|
||||
}
|
||||
|
||||
/// Serializes signal-triggered cleanups: a signal arriving while a quit
|
||||
/// attempt is still in flight is dropped instead of overlapping it.
|
||||
pub(crate) struct ShutdownLatch {
|
||||
is_cleaning_up: AtomicBool,
|
||||
}
|
||||
@@ -36,17 +41,18 @@ impl ShutdownLatch {
|
||||
.is_ok()
|
||||
}
|
||||
|
||||
fn finish(&self, outcome: ShutdownOutcome) {
|
||||
if matches!(outcome, ShutdownOutcome::Canceled) {
|
||||
self.is_cleaning_up.store(false, Ordering::Release);
|
||||
}
|
||||
fn finish(&self) {
|
||||
self.is_cleaning_up.store(false, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
/// Spawns the Linux signal loop on a dedicated runtime so it survives the
|
||||
/// teardown of any app-owned runtime. The handler runs once per accepted
|
||||
/// signal; the quit flow it invokes owns the exit-or-cancel decision.
|
||||
pub fn register<F, Fut>(f: F)
|
||||
where
|
||||
F: Fn() -> Fut + Send + Sync + 'static,
|
||||
Fut: Future<Output = ShutdownOutcome> + Send + 'static,
|
||||
Fut: Future<Output = ()> + Send + 'static,
|
||||
{
|
||||
RUNTIME.get_or_init(|| match tokio::runtime::Runtime::new() {
|
||||
Ok(rt) => Some(rt),
|
||||
@@ -61,36 +67,22 @@ where
|
||||
}
|
||||
});
|
||||
|
||||
#[cfg(unix)]
|
||||
unix::register(f);
|
||||
|
||||
#[cfg(windows)]
|
||||
windows::register(f);
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{ShutdownLatch, ShutdownOutcome};
|
||||
use super::ShutdownLatch;
|
||||
|
||||
#[test]
|
||||
fn canceled_shutdown_releases_latch_and_permits_next_attempt() {
|
||||
fn finished_attempt_releases_latch_for_the_next_signal() {
|
||||
let latch = ShutdownLatch::new();
|
||||
|
||||
assert!(latch.try_begin());
|
||||
assert!(!latch.try_begin());
|
||||
|
||||
latch.finish(ShutdownOutcome::Canceled);
|
||||
latch.finish();
|
||||
|
||||
assert!(latch.try_begin());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn committed_shutdown_retains_latch_and_suppresses_repeats() {
|
||||
let latch = ShutdownLatch::new();
|
||||
|
||||
assert!(latch.try_begin());
|
||||
latch.finish(ShutdownOutcome::Committed);
|
||||
|
||||
assert!(!latch.try_begin());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
use clash_verge_logging::{Type, logging};
|
||||
use tokio::signal::unix::{SignalKind, signal};
|
||||
|
||||
use crate::{RUNTIME, SHUTDOWN_LATCH, ShutdownOutcome};
|
||||
use crate::{RUNTIME, SHUTDOWN_LATCH};
|
||||
|
||||
pub fn register<F, Fut>(f: F)
|
||||
where
|
||||
F: Fn() -> Fut + Send + Sync + 'static,
|
||||
Fut: Future<Output = ShutdownOutcome> + Send + 'static,
|
||||
Fut: Future<Output = ()> + Send + 'static,
|
||||
{
|
||||
if let Some(Some(rt)) = RUNTIME.get() {
|
||||
rt.spawn(async move {
|
||||
@@ -60,7 +60,8 @@ where
|
||||
}
|
||||
logging!(info, Type::SystemSignal, "Caught signal {}", signal_name);
|
||||
|
||||
SHUTDOWN_LATCH.finish(f().await);
|
||||
f().await;
|
||||
SHUTDOWN_LATCH.finish();
|
||||
}
|
||||
});
|
||||
} else {
|
||||
|
||||
@@ -1,83 +0,0 @@
|
||||
use clash_verge_logging::{Type, logging};
|
||||
use tokio::signal::windows;
|
||||
|
||||
use crate::{RUNTIME, SHUTDOWN_LATCH, ShutdownOutcome};
|
||||
|
||||
pub fn register<F, Fut>(f: F)
|
||||
where
|
||||
F: Fn() -> Fut + Send + Sync + 'static,
|
||||
Fut: Future<Output = ShutdownOutcome> + Send + 'static,
|
||||
{
|
||||
if let Some(Some(rt)) = RUNTIME.get() {
|
||||
rt.spawn(async move {
|
||||
let mut ctrl_c = match windows::ctrl_c() {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
logging!(error, Type::SystemSignal, "Failed to register Ctrl+C: {}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut ctrl_close = match windows::ctrl_close() {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
logging!(error, Type::SystemSignal, "Failed to register Ctrl+Close: {}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut ctrl_shutdown = match windows::ctrl_shutdown() {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
logging!(error, Type::SystemSignal, "Failed to register Ctrl+Shutdown: {}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let mut ctrl_logoff = match windows::ctrl_logoff() {
|
||||
Ok(s) => s,
|
||||
Err(e) => {
|
||||
logging!(error, Type::SystemSignal, "Failed to register Ctrl+Logoff: {}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
loop {
|
||||
let signal_name;
|
||||
tokio::select! {
|
||||
_ = ctrl_c.recv() => {
|
||||
signal_name = "Ctrl+C";
|
||||
}
|
||||
_ = ctrl_close.recv() => {
|
||||
signal_name = "Ctrl+Close";
|
||||
}
|
||||
_ = ctrl_shutdown.recv() => {
|
||||
signal_name = "Ctrl+Shutdown";
|
||||
}
|
||||
_ = ctrl_logoff.recv() => {
|
||||
signal_name = "Ctrl+Logoff";
|
||||
}
|
||||
}
|
||||
|
||||
if !SHUTDOWN_LATCH.try_begin() {
|
||||
logging!(
|
||||
info,
|
||||
Type::SystemSignal,
|
||||
"Already shutting down, ignoring repeated signal: {}",
|
||||
signal_name
|
||||
);
|
||||
continue;
|
||||
}
|
||||
logging!(info, Type::SystemSignal, "Caught Windows signal: {}", signal_name);
|
||||
|
||||
SHUTDOWN_LATCH.finish(f().await);
|
||||
}
|
||||
});
|
||||
} else {
|
||||
logging!(
|
||||
error,
|
||||
Type::SystemSignal,
|
||||
"register shutdown signal failed, RUNTIME is not available"
|
||||
);
|
||||
}
|
||||
}
|
||||
+15
-15
@@ -54,13 +54,13 @@
|
||||
"@mui/icons-material": "^9.4.0",
|
||||
"@mui/material": "^9.4.0",
|
||||
"@tanstack/react-virtual": "^3.14.13",
|
||||
"@tauri-apps/api": "2.12.1",
|
||||
"@tauri-apps/plugin-clipboard-manager": "2.4.0",
|
||||
"@tauri-apps/plugin-dialog": "2.8.0",
|
||||
"@tauri-apps/plugin-fs": "2.6.0",
|
||||
"@tauri-apps/plugin-http": "2.8.0",
|
||||
"@tauri-apps/plugin-opener": "2.7.0",
|
||||
"@tauri-apps/plugin-updater": "2.13.1",
|
||||
"@tauri-apps/api": "2.12.4",
|
||||
"@tauri-apps/plugin-clipboard-manager": "2.5.0",
|
||||
"@tauri-apps/plugin-dialog": "2.8.3",
|
||||
"@tauri-apps/plugin-fs": "2.7.1",
|
||||
"@tauri-apps/plugin-http": "2.8.3",
|
||||
"@tauri-apps/plugin-opener": "2.7.2",
|
||||
"@tauri-apps/plugin-updater": "2.14.0",
|
||||
"ahooks": "^3.10.0",
|
||||
"cidr-block": "^2.3.0",
|
||||
"compare-versions": "^6.1.1",
|
||||
@@ -75,11 +75,11 @@
|
||||
"monaco-yaml": "^5.5.1",
|
||||
"nanoid": "^6.0.1",
|
||||
"qrcode.react": "^4.2.0",
|
||||
"react": "19.3.0",
|
||||
"react-dom": "19.3.0",
|
||||
"react": "19.3.1",
|
||||
"react-dom": "19.3.1",
|
||||
"react-error-boundary": "6.1.6",
|
||||
"react-hook-form": "^7.88.0",
|
||||
"react-i18next": "17.0.15",
|
||||
"react-i18next": "17.0.19",
|
||||
"react-markdown": "10.1.0",
|
||||
"react-router": "^8.4.0",
|
||||
"rehype-raw": "^7.0.0",
|
||||
@@ -95,7 +95,7 @@
|
||||
"@biomejs/biome": "^2.5.14",
|
||||
"@eslint-react/eslint-plugin": "^5.20.5",
|
||||
"@eslint/js": "^10.0.1",
|
||||
"@tauri-apps/cli": "2.12.1",
|
||||
"@tauri-apps/cli": "2.12.3",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
"@types/node": "^24.13.6",
|
||||
"@types/react": "19.3.0",
|
||||
@@ -103,7 +103,7 @@
|
||||
"@types/validator": "^13.15.10",
|
||||
"@vitejs/plugin-legacy": "^8.2.3",
|
||||
"@vitejs/plugin-react": "^6.1.1",
|
||||
"@wdio/tauri-service": "1.4.0",
|
||||
"@wdio/tauri-service": "1.5.0",
|
||||
"adm-zip": "^0.6.1",
|
||||
"axios": "^1.20.0",
|
||||
"cli-color": "^2.0.4",
|
||||
@@ -123,7 +123,7 @@
|
||||
"https-proxy-agent": "^9.1.0",
|
||||
"husky": "^9.1.7",
|
||||
"jiti": "^2.7.0",
|
||||
"knip": "6.39.0",
|
||||
"knip": "6.41.0",
|
||||
"lint-staged": "^17.5.1",
|
||||
"sass": "^1.104.1",
|
||||
"tar": "^7.5.22",
|
||||
@@ -133,7 +133,7 @@
|
||||
"vite": "^8.3.0",
|
||||
"vite-plugin-svgr": "^5.2.0",
|
||||
"vitest": "^5.0.1",
|
||||
"webdriverio": "10.0.0",
|
||||
"webdriverio": "10.0.2",
|
||||
"ws": "8.22.0"
|
||||
},
|
||||
"lint-staged": {
|
||||
@@ -146,5 +146,5 @@
|
||||
]
|
||||
},
|
||||
"type": "module",
|
||||
"packageManager": "pnpm@12.11.2"
|
||||
"packageManager": "pnpm@12.14.0"
|
||||
}
|
||||
|
||||
Generated
+451
-447
File diff suppressed because it is too large
Load Diff
@@ -45,6 +45,17 @@
|
||||
"matchManagers": ["npm"],
|
||||
"groupName": "npm dependencies"
|
||||
},
|
||||
{
|
||||
"description": "Update Tauri npm packages and Cargo crates together so frontend and backend stay in sync",
|
||||
"matchPackageNames": [
|
||||
"@tauri-apps/**",
|
||||
"@wdio/tauri-service",
|
||||
"tauri",
|
||||
"tauri-*"
|
||||
],
|
||||
"groupName": "tauri",
|
||||
"addLabels": ["tauri"]
|
||||
},
|
||||
{
|
||||
"description": "Group all GitHub Actions updates into a single PR",
|
||||
"matchManagers": ["github-actions"],
|
||||
|
||||
+1
-1
@@ -1,3 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "1.99.0"
|
||||
channel = "1.99.1"
|
||||
components = ["rustfmt", "clippy"]
|
||||
|
||||
@@ -22,7 +22,7 @@ try {
|
||||
'run',
|
||||
'-p',
|
||||
'clash-verge',
|
||||
'--bin',
|
||||
'--example',
|
||||
'frontend-contract',
|
||||
'--locked',
|
||||
'-q',
|
||||
|
||||
+16
-13
@@ -29,13 +29,12 @@ perf-harness = ["dep:tauri-plugin-wdio-webdriver"]
|
||||
identifier = "io.github.clash-verge-rev.clash-verge-rev"
|
||||
|
||||
[build-dependencies]
|
||||
tauri-build = { version = "2.7.1", features = [] }
|
||||
tauri-build = { version = "2.7.3", features = [] }
|
||||
|
||||
[dependencies]
|
||||
clash-verge-draft = { workspace = true }
|
||||
clash-verge-media-unlock = { workspace = true }
|
||||
clash-verge-logging = { workspace = true }
|
||||
clash-verge-signal = { workspace = true }
|
||||
clash-verge-i18n = { workspace = true }
|
||||
clash-verge-limiter = { workspace = true }
|
||||
tauri-plugin-clash-verge-sysinfo = { workspace = true }
|
||||
@@ -73,12 +72,12 @@ sysproxy = { git = "https://github.com/clash-verge-rev/sysproxy-rs.git", branch
|
||||
"guard",
|
||||
] }
|
||||
network-interface = { version = "2.0.5", features = ["serde"] }
|
||||
tauri-plugin-shell = "2.4.0"
|
||||
tauri-plugin-opener = "2.7.0"
|
||||
tauri-plugin-dialog = "2.8.1"
|
||||
tauri-plugin-fs = "2.6.0"
|
||||
tauri-plugin-shell = "2.5.0"
|
||||
tauri-plugin-opener = "2.7.2"
|
||||
tauri-plugin-dialog = "2.8.3"
|
||||
tauri-plugin-fs = "2.7.1"
|
||||
tauri-plugin-process = "2.4.0"
|
||||
tauri-plugin-deep-link = "2.6.1"
|
||||
tauri-plugin-deep-link = "2.6.2"
|
||||
tauri-plugin-window-state = "2.5.0"
|
||||
zip = "9.0.1"
|
||||
flate2 = "1.1.10"
|
||||
@@ -91,13 +90,13 @@ getrandom = "0.4.3"
|
||||
futures = "0.3.34"
|
||||
gethostname = "1.1.0"
|
||||
scopeguard = "1.2.0"
|
||||
tauri-plugin-notification = "2.5.1"
|
||||
tauri-plugin-notification = "2.7.0"
|
||||
tokio-stream = "0.1.19"
|
||||
backon = { version = "1.6.0", features = ["tokio-sleep"] }
|
||||
tauri-plugin-http = "2.8.0"
|
||||
tauri-plugin-http = "2.8.3"
|
||||
tracing = { workspace = true }
|
||||
tauri-plugin-devtools = { version = "2.2.2", optional = true }
|
||||
tauri-plugin-mihomo = { git = "https://github.com/clash-verge-rev/tauri-plugin-mihomo", branch = "main" }
|
||||
tauri-plugin-mihomo = { git = "https://github.com/clash-verge-rev/tauri-plugin-mihomo", rev = "7a03d16f4d39b1bb885b7babe66b9e153e34b615" }
|
||||
async-trait = "0.1.92"
|
||||
clash_verge_service_ipc = { git = "https://github.com/clash-verge-rev/clash-verge-service-ipc.git", tag = "v2.7.6", version = "2.7.6", features = [
|
||||
"client",
|
||||
@@ -116,8 +115,12 @@ specta-typescript = "=0.0.12"
|
||||
[target.'cfg(unix)'.dependencies]
|
||||
libc = "0.2.190"
|
||||
|
||||
[target.'cfg(target_os = "linux")'.dependencies]
|
||||
# Upstream has no session-end signal support on Linux; see crates/clash-verge-signal.
|
||||
clash-verge-signal = { workspace = true }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
tauri-plugin-wdio-webdriver = { version = "=1.4.0", optional = true }
|
||||
tauri-plugin-wdio-webdriver = { version = "=1.5.0", optional = true }
|
||||
objc2 = "0.6.5"
|
||||
objc2-foundation = { version = "0.3.2", features = [
|
||||
"NSString",
|
||||
@@ -150,8 +153,8 @@ windows-sys.workspace = true
|
||||
|
||||
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
|
||||
tauri-plugin-autostart = "2.7.0"
|
||||
tauri-plugin-global-shortcut = "2.4.0"
|
||||
tauri-plugin-updater = "2.13.1"
|
||||
tauri-plugin-global-shortcut = "2.4.1"
|
||||
tauri-plugin-updater = "2.14.0"
|
||||
|
||||
[dev-dependencies]
|
||||
criterion = { workspace = true }
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
// Dev-only target: Tauri ships everything under src/bin/ to /usr/bin in deb/rpm
|
||||
// bundles (PR #8131), so this generator must stay an example, never a bin.
|
||||
fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
let mut args = std::env::args_os().skip(1);
|
||||
let output = args
|
||||
@@ -282,7 +282,9 @@ async fn handle_validation_failure(
|
||||
) -> CmdResult<ValidationOutcome> {
|
||||
logging!(warn, Type::Cmd, "配置验证失败: {}", outcome);
|
||||
discard_and_restore(current_profile).await?;
|
||||
handle_validation_notice(&outcome, ValidationNoticeTarget::Runtime, "运行时配置");
|
||||
if !matches!(outcome, ValidationOutcome::Busy) {
|
||||
handle_validation_notice(&outcome, ValidationNoticeTarget::Runtime, "运行时配置");
|
||||
}
|
||||
Ok(outcome)
|
||||
}
|
||||
|
||||
@@ -311,9 +313,6 @@ async fn perform_config_update(
|
||||
current_value: Option<&String>,
|
||||
current_profile: Option<&String>,
|
||||
) -> CmdResult<ValidationOutcome> {
|
||||
defer! {
|
||||
CURRENT_SWITCHING_PROFILE.store(false, Ordering::Release);
|
||||
}
|
||||
let update_result = run_profile_config_update_transition(|| CoreManager::global().update_config_forced()).await;
|
||||
|
||||
match update_result {
|
||||
@@ -409,6 +408,9 @@ pub async fn patch_profiles_config(profiles: ProfileSelection) -> CmdResult<Vali
|
||||
logging!(info, Type::Cmd, "当前正在切换配置,放弃请求");
|
||||
return Ok(ValidationOutcome::Busy);
|
||||
}
|
||||
defer! {
|
||||
CURRENT_SWITCHING_PROFILE.store(false, Ordering::Release);
|
||||
}
|
||||
let _profile_write_guard = PROFILE_WRITE_LOCK.lock().await;
|
||||
|
||||
let profiles = IProfiles {
|
||||
|
||||
@@ -130,7 +130,7 @@ pub async fn open_or_close_dashboard() {
|
||||
logging!(info, Type::Window, "Window toggle result: {result:?}");
|
||||
}
|
||||
|
||||
pub async fn quit() -> clash_verge_signal::ShutdownOutcome {
|
||||
pub async fn quit() {
|
||||
logging!(debug, Type::System, "启动退出流程");
|
||||
// 设置退出标志
|
||||
handle::Handle::global().set_is_exiting();
|
||||
@@ -165,13 +165,11 @@ pub async fn quit() -> clash_verge_signal::ShutdownOutcome {
|
||||
NoticeStatus::AppQuitCoreStopFailed,
|
||||
cleanup_result.stop_error.unwrap_or_default(),
|
||||
);
|
||||
return clash_verge_signal::ShutdownOutcome::Canceled;
|
||||
}
|
||||
|
||||
utils::server::shutdown_embedded_server();
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
app_handle.exit(if cleanup_result.all_success { 0 } else { 1 });
|
||||
clash_verge_signal::ShutdownOutcome::Committed
|
||||
}
|
||||
|
||||
pub async fn clean_async() -> CleanupResult {
|
||||
|
||||
@@ -230,6 +230,7 @@ pub fn run() -> std::process::ExitCode {
|
||||
if let Err(panic) = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
|
||||
resolve::resolve_setup_async();
|
||||
resolve::resolve_setup_sync();
|
||||
#[cfg(target_os = "linux")]
|
||||
resolve::init_signal();
|
||||
})) {
|
||||
log_setup_panic("window-core", panic);
|
||||
|
||||
@@ -16,7 +16,7 @@ fn main() -> ExitCode {
|
||||
#[cfg(not(all(feature = "perf-harness", target_os = "macos")))]
|
||||
fn main() -> ExitCode {
|
||||
let default_parallelism = std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1);
|
||||
let worker_limit = std::cmp::min(default_parallelism, 8);
|
||||
let worker_limit = std::cmp::min(default_parallelism, 4);
|
||||
let blocking_limit = 2 * worker_limit;
|
||||
|
||||
#[allow(clippy::unwrap_used)]
|
||||
|
||||
@@ -20,7 +20,6 @@ use crate::{
|
||||
utils::{init, server, window_manager::WindowManager},
|
||||
};
|
||||
use clash_verge_logging::{Type, logging, logging_error};
|
||||
use clash_verge_signal;
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
pub mod dns;
|
||||
@@ -189,6 +188,8 @@ async fn init_silent_updater() {
|
||||
logging!(info, Type::Setup, "Silent updater initialized");
|
||||
}
|
||||
|
||||
// Upstream has no session-end signal support on Linux; see crates/clash-verge-signal.
|
||||
#[cfg(target_os = "linux")]
|
||||
pub(crate) fn init_signal() {
|
||||
logging!(debug, Type::Setup, "Initializing signal handlers...");
|
||||
clash_verge_signal::register(feat::quit);
|
||||
|
||||
@@ -39,8 +39,6 @@ static INSTANCE_LOCK: OnceLock<std::fs::File> = OnceLock::new();
|
||||
const PAC_INITIAL_AVAILABLE: bool = false;
|
||||
static PAC_AVAILABLE: AtomicBool = AtomicBool::new(PAC_INITIAL_AVAILABLE);
|
||||
static COMMANDS_READY: AtomicBool = AtomicBool::new(false);
|
||||
#[cfg(feature = "verge-dev")]
|
||||
static DEV_QUIT_REQUESTED: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
pub enum SingletonDisposition {
|
||||
@@ -164,55 +162,12 @@ fn instance_auth(token: String) -> impl warp::Filter<Extract = (), Error = warp:
|
||||
.untuple_one()
|
||||
}
|
||||
|
||||
#[cfg(feature = "verge-dev")]
|
||||
fn dev_quit_route<F>(
|
||||
token: String,
|
||||
commands_ready: &'static AtomicBool,
|
||||
quit_requested: &'static AtomicBool,
|
||||
trigger: F,
|
||||
) -> impl warp::Filter<Extract = (impl warp::Reply,), Error = warp::Rejection> + Clone
|
||||
where
|
||||
F: Fn() + Clone + Send + Sync + 'static,
|
||||
{
|
||||
instance_auth(token)
|
||||
.and(warp::path!("commands" / "dev" / "quit"))
|
||||
.and(warp::post())
|
||||
.and_then(move || {
|
||||
let trigger = trigger.clone();
|
||||
async move {
|
||||
if !commands_ready.load(Ordering::Acquire) {
|
||||
return Ok::<_, warp::Rejection>(warp::reply::with_status(
|
||||
String::from("starting"),
|
||||
warp::http::StatusCode::SERVICE_UNAVAILABLE,
|
||||
));
|
||||
}
|
||||
if quit_requested
|
||||
.compare_exchange(false, true, Ordering::AcqRel, Ordering::Acquire)
|
||||
.is_ok()
|
||||
{
|
||||
trigger();
|
||||
}
|
||||
Ok(warp::reply::with_status(
|
||||
String::from("accepted"),
|
||||
warp::http::StatusCode::ACCEPTED,
|
||||
))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
#[cfg(feature = "verge-dev")]
|
||||
fn release_dev_quit_latch(quit_requested: &AtomicBool, outcome: clash_verge_signal::ShutdownOutcome) {
|
||||
if matches!(outcome, clash_verge_signal::ShutdownOutcome::Canceled) {
|
||||
quit_requested.store(false, Ordering::Release);
|
||||
}
|
||||
}
|
||||
|
||||
fn start_embedded_server(listener: tokio::net::TcpListener, token: String) {
|
||||
let (shutdown_tx, shutdown_rx) = oneshot::channel();
|
||||
let _ = SHUTDOWN_SENDER.set(Mutex::new(Some(shutdown_tx)));
|
||||
|
||||
#[cfg(feature = "verge-dev")]
|
||||
let auth = instance_auth(token.clone());
|
||||
let auth = instance_auth(token);
|
||||
#[cfg(not(feature = "verge-dev"))]
|
||||
let auth = instance_auth(token);
|
||||
|
||||
@@ -278,12 +233,6 @@ fn start_embedded_server(listener: tokio::net::TcpListener, token: String) {
|
||||
});
|
||||
|
||||
let commands = auth.clone().and(visible).or(auth.and(scheme)).or(pac);
|
||||
#[cfg(feature = "verge-dev")]
|
||||
let commands = commands.or(dev_quit_route(token, &COMMANDS_READY, &DEV_QUIT_REQUESTED, || {
|
||||
AsyncHandler::spawn(|| async {
|
||||
release_dev_quit_latch(&DEV_QUIT_REQUESTED, crate::feat::quit().await);
|
||||
});
|
||||
}));
|
||||
AsyncHandler::spawn(move || async move {
|
||||
warp::serve(commands)
|
||||
.incoming(listener)
|
||||
|
||||
@@ -27,8 +27,12 @@ const TRAY_STATUS_ITEM_EXTRA_WIDTH: f64 = 30.0;
|
||||
const TRAY_STATUS_ITEM_MIN_LENGTH: f64 = 58.0;
|
||||
const NS_VARIABLE_STATUS_ITEM_LENGTH: f64 = -1.0;
|
||||
|
||||
/// (button_height, mono_advance, attributes).
|
||||
type CachedAttrs = (f64, f64, Retained<NSDictionary<NSString, AnyObject>>);
|
||||
|
||||
thread_local! {
|
||||
static LAST_DISPLAY_STR: RefCell<String> = const { RefCell::new(String::new()) };
|
||||
static CACHED_ATTRS: RefCell<Option<CachedAttrs>> = const { RefCell::new(None) };
|
||||
}
|
||||
|
||||
fn format_tray_speed(up: u64, down: u64) -> String {
|
||||
@@ -74,8 +78,26 @@ fn create_attributed_string(
|
||||
}
|
||||
}
|
||||
|
||||
fn status_item_length_for_speed(attr_str: &NSAttributedString) -> f64 {
|
||||
(attr_str.size().width.ceil() + TRAY_STATUS_ITEM_EXTRA_WIDTH).max(TRAY_STATUS_ITEM_MIN_LENGTH)
|
||||
/// Attributes and per-glyph advance, rebuilt only when the button height changes.
|
||||
fn cached_attributes(button_height: f64) -> (f64, Retained<NSDictionary<NSString, AnyObject>>) {
|
||||
CACHED_ATTRS.with(|cache| {
|
||||
if let Some((height, advance, attrs)) = cache.borrow().as_ref()
|
||||
&& *height == button_height
|
||||
{
|
||||
return (*advance, attrs.clone());
|
||||
}
|
||||
let attrs = build_attributes(button_height);
|
||||
let probe = NSString::from_str("0");
|
||||
let advance = create_attributed_string(&probe, Some(&attrs)).size().width;
|
||||
*cache.borrow_mut() = Some((button_height, advance, attrs.clone()));
|
||||
(advance, attrs)
|
||||
})
|
||||
}
|
||||
|
||||
/// Longest line times the monospaced advance; matches `size().width` for the ASCII-only speed text.
|
||||
fn arithmetic_width(text: &str, advance: f64) -> f64 {
|
||||
let glyphs = text.split('\n').map(str::len).max().unwrap_or(0) as f64;
|
||||
glyphs * advance
|
||||
}
|
||||
|
||||
fn sync_click_target_frame(button: &NSStatusBarButton) {
|
||||
@@ -89,7 +111,7 @@ fn sync_click_target_frame(button: &NSStatusBarButton) {
|
||||
}
|
||||
|
||||
/// `with_inner_tray_icon` should run this on the main thread; fail safely if it does not.
|
||||
fn apply_status_item_attributed_title(status_item: &NSStatusItem, text: &NSString, show_speed: bool) {
|
||||
fn apply_status_item_attributed_title(status_item: &NSStatusItem, text: &str, show_speed: bool) {
|
||||
let Some(mtm) = MainThreadMarker::new() else {
|
||||
logging!(warn, Type::Tray, "托盘速率富文本设置跳过:非主线程调用");
|
||||
return;
|
||||
@@ -97,17 +119,19 @@ fn apply_status_item_attributed_title(status_item: &NSStatusItem, text: &NSStrin
|
||||
let Some(button) = status_item.button(mtm) else {
|
||||
return;
|
||||
};
|
||||
let ns_string = NSString::from_str(text);
|
||||
let attr_str = if show_speed {
|
||||
let attrs = build_attributes(button.bounds().size.height);
|
||||
let (advance, attrs) = cached_attributes(button.bounds().size.height);
|
||||
let attrs: &NSDictionary<NSString, AnyObject> = &attrs;
|
||||
let attr_str = create_attributed_string(text, Some(attrs));
|
||||
let length = status_item_length_for_speed(&attr_str);
|
||||
let attr_str = create_attributed_string(&ns_string, Some(attrs));
|
||||
let width = arithmetic_width(text, advance);
|
||||
let length = (width.ceil() + TRAY_STATUS_ITEM_EXTRA_WIDTH).max(TRAY_STATUS_ITEM_MIN_LENGTH);
|
||||
if status_item.length() != length {
|
||||
status_item.setLength(length);
|
||||
}
|
||||
attr_str
|
||||
} else {
|
||||
create_attributed_string(text, None)
|
||||
create_attributed_string(&ns_string, None)
|
||||
};
|
||||
button.setAttributedTitle(&attr_str);
|
||||
sync_click_target_frame(&button);
|
||||
@@ -121,8 +145,7 @@ pub fn set_speed_attributed_title(status_item: &NSStatusItem, up: u64, down: u64
|
||||
return;
|
||||
}
|
||||
*last = speed_text;
|
||||
let ns_string = NSString::from_str(last.as_str());
|
||||
apply_status_item_attributed_title(status_item, &ns_string, true);
|
||||
apply_status_item_attributed_title(status_item, last.as_str(), true);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -130,7 +153,6 @@ pub fn clear_speed_attributed_title(status_item: &NSStatusItem) {
|
||||
LAST_DISPLAY_STR.with(|last| {
|
||||
last.borrow_mut().clear();
|
||||
});
|
||||
let empty = NSString::from_str("");
|
||||
status_item.setLength(NS_VARIABLE_STATUS_ITEM_LENGTH);
|
||||
apply_status_item_attributed_title(status_item, &empty, false);
|
||||
apply_status_item_attributed_title(status_item, "", false);
|
||||
}
|
||||
|
||||
@@ -44,6 +44,7 @@ import {
|
||||
ProxyGroupNavigator,
|
||||
} from './proxy-group-navigator'
|
||||
import { ProxyRender } from './proxy-render'
|
||||
import type { HeadState } from './use-head-state'
|
||||
import {
|
||||
PROXY_GROUP_HEADER_SENSORS,
|
||||
useProxyGroupHeaderLayout,
|
||||
@@ -90,11 +91,11 @@ function useProxyRenderState(
|
||||
) {
|
||||
const { verge } = useVerge()
|
||||
const { proxyView } = useProxiesData()
|
||||
const { renderList, onProxies, onHeadState } = useRenderList(
|
||||
mode,
|
||||
isChainMode,
|
||||
activeSelectedGroup,
|
||||
)
|
||||
const {
|
||||
renderList,
|
||||
onProxies,
|
||||
onHeadState: setListHeadState,
|
||||
} = useRenderList(mode, isChainMode, activeSelectedGroup)
|
||||
const scrollPositionKey = useMemo(
|
||||
() =>
|
||||
isChainMode
|
||||
@@ -103,6 +104,14 @@ function useProxyRenderState(
|
||||
[activeSelectedGroup, isChainMode, mode],
|
||||
)
|
||||
|
||||
const onHeadState = useCallback(
|
||||
(groupName: string, patch: Partial<HeadState>) => {
|
||||
if (patch.open) onProxies()
|
||||
setListHeadState(groupName, patch)
|
||||
},
|
||||
[onProxies, setListHeadState],
|
||||
)
|
||||
|
||||
const timeout = verge?.default_latency_timeout || 10000
|
||||
|
||||
const handleCheckAll = useStableCallback(
|
||||
|
||||
@@ -1,20 +1,11 @@
|
||||
import Visibility from '@mui/icons-material/Visibility'
|
||||
import VisibilityOff from '@mui/icons-material/VisibilityOff'
|
||||
import {
|
||||
TextField,
|
||||
Button,
|
||||
Grid,
|
||||
Stack,
|
||||
IconButton,
|
||||
InputAdornment,
|
||||
} from '@mui/material'
|
||||
import { Button, Grid, Stack, TextField } from '@mui/material'
|
||||
import { useLockFn } from 'ahooks'
|
||||
import { useState, useRef, memo, useEffect } from 'react'
|
||||
import { memo, useEffect, useRef } from 'react'
|
||||
import { useForm } from 'react-hook-form'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
import { useVerge } from '@/hooks/use-verge'
|
||||
import { saveWebdavConfig, createWebdavBackup } from '@/services/cmds'
|
||||
import { createWebdavBackup, saveWebdavConfig } from '@/services/cmds'
|
||||
import { mutate } from '@/services/mutate'
|
||||
import { errorDetail, showNotice } from '@/services/notice-service'
|
||||
import {
|
||||
@@ -43,7 +34,6 @@ export const BackupConfigViewer = memo(
|
||||
const { t } = useTranslation()
|
||||
const { verge, mutateVerge } = useVerge()
|
||||
const { webdav_url, webdav_username, webdav_password } = verge || {}
|
||||
const [showPassword, setShowPassword] = useState(false)
|
||||
const usernameRef = useRef<HTMLInputElement>(null)
|
||||
const passwordRef = useRef<HTMLInputElement>(null)
|
||||
const urlRef = useRef<HTMLInputElement>(null)
|
||||
@@ -68,10 +58,6 @@ export const BackupConfigViewer = memo(
|
||||
const webdavStatus = getWebdavStatus(webdavSignature)
|
||||
const shouldAutoInit = webdavStatus !== 'failed'
|
||||
|
||||
const handleClickShowPassword = () => {
|
||||
setShowPassword((prev) => !prev)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (!webdav_url || !webdav_username || !webdav_password) {
|
||||
return
|
||||
@@ -215,7 +201,7 @@ export const BackupConfigViewer = memo(
|
||||
<Grid size={{ xs: 6 }}>
|
||||
<TextField
|
||||
label={t('shared.labels.password')}
|
||||
type={showPassword ? 'text' : 'password'}
|
||||
type="password"
|
||||
variant="outlined"
|
||||
size="small"
|
||||
autoCorrect="off"
|
||||
@@ -223,20 +209,6 @@ export const BackupConfigViewer = memo(
|
||||
spellCheck="false"
|
||||
inputRef={passwordRef}
|
||||
{...register('password')}
|
||||
slotProps={{
|
||||
input: {
|
||||
endAdornment: (
|
||||
<InputAdornment position="end">
|
||||
<IconButton
|
||||
onClick={handleClickShowPassword}
|
||||
edge="end"
|
||||
>
|
||||
{showPassword ? <VisibilityOff /> : <Visibility />}
|
||||
</IconButton>
|
||||
</InputAdornment>
|
||||
),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
@@ -51,6 +51,7 @@ export const useProfiles = () => {
|
||||
try {
|
||||
const result = await mutate(() => patchProfilesConfig(value), {
|
||||
id: 'patch-profiles-config',
|
||||
errorNotice: false,
|
||||
onFulfilled: (outcome) => {
|
||||
if (outcome.status === 'valid') {
|
||||
void setCacheData<IProfilesConfig>(profilesQueryKey, (current) =>
|
||||
@@ -60,12 +61,8 @@ export const useProfiles = () => {
|
||||
},
|
||||
})
|
||||
|
||||
if (!result.ok) {
|
||||
// Backend Busy keeps local state untouched, as before.
|
||||
return result.value
|
||||
}
|
||||
const outcome = result.value
|
||||
if (outcome.status !== 'valid' && outcome.status !== 'busy') {
|
||||
if (outcome.status !== 'valid') {
|
||||
await mutateProfiles()
|
||||
}
|
||||
return outcome
|
||||
|
||||
+207
-216
@@ -32,230 +32,221 @@ export const forgetShownStartupError = () => {
|
||||
* type makes a backend status without a frontend entry a compile error (and
|
||||
* contract regeneration keeps the union in sync).
|
||||
*/
|
||||
const handlers: Record<
|
||||
NoticeStatus,
|
||||
(msg: string, navigate: NavigateFunction) => void
|
||||
> = {
|
||||
info: (msg) => {
|
||||
if (msg) showNotice.info(msg)
|
||||
},
|
||||
'import_sub_url::ok': (_msg, navigate) => {
|
||||
// 空 msg 传入,我们不希望导致 后端-前端-后端 死循环,这里只做提醒。
|
||||
// 未来细分事件通知时,可以考虑传入订阅 ID 或其他标识符
|
||||
// navigate("/profile", { state: { current: msg } });
|
||||
navigate('/profile')
|
||||
showNotice.success(
|
||||
'shared.feedback.notifications.importSubscriptionSuccess',
|
||||
)
|
||||
},
|
||||
'import_sub_url::error': (msg, navigate) => {
|
||||
navigate('/profile')
|
||||
showNotice.error(msg)
|
||||
},
|
||||
'set_config::error': (msg) => showNotice.error(msg),
|
||||
'core_start::error': () => {
|
||||
const read = ++startupErrorReads
|
||||
void getCoreStartupError()
|
||||
.then(async (failure) => {
|
||||
if (read < settledStartupErrorRead) return
|
||||
settledStartupErrorRead = read
|
||||
if (!failure) {
|
||||
shownStartupError = null
|
||||
return
|
||||
}
|
||||
const window = getCurrentWindow()
|
||||
const [visible, minimized] = await Promise.all([
|
||||
window.isVisible(),
|
||||
window.isMinimized(),
|
||||
])
|
||||
const shown = `${failure.kind}:${failure.detail}`
|
||||
if (
|
||||
read === settledStartupErrorRead &&
|
||||
visible &&
|
||||
!minimized &&
|
||||
shownStartupError !== shown
|
||||
) {
|
||||
shownStartupError = shown
|
||||
showNotice.error(
|
||||
failure.kind === 'serviceCoreStopped'
|
||||
? 'settings.feedback.errors.clash.serviceCoreStopped'
|
||||
: 'settings.feedback.errors.clash.startFailed',
|
||||
failure.detail,
|
||||
)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to read the pending core startup error', error)
|
||||
})
|
||||
},
|
||||
'service_core::repair_required': () => {
|
||||
void takeServiceRepairNotice()
|
||||
.then((pending) => {
|
||||
if (pending) {
|
||||
requestService({ reason: 'serviceLocationRefused' })
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to read the pending service repair notice', error)
|
||||
})
|
||||
},
|
||||
'service_core::app_data_not_owned': () => {
|
||||
void takeServiceOwnerNotice()
|
||||
.then((command) => {
|
||||
if (command) {
|
||||
if (shownOwnerNotice !== null) hideNotice(shownOwnerNotice)
|
||||
shownOwnerNotice = showNotice.warning(
|
||||
'settings.feedback.notifications.clashService.appDataNotOwned',
|
||||
{ command },
|
||||
0,
|
||||
)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to read the pending service owner notice', error)
|
||||
})
|
||||
},
|
||||
'service_core::sidecar_fallback': () => {
|
||||
void takeServiceFallbackNotice()
|
||||
.then((notice) => {
|
||||
if (notice?.kind === 'coreRejected') {
|
||||
showNotice.warning(
|
||||
'settings.feedback.notifications.clashService.permissionFallback',
|
||||
{ reason: notice.reason },
|
||||
0,
|
||||
)
|
||||
} else if (notice?.kind === 'notAutoStarted') {
|
||||
requestService({ reason: 'serviceNotAutoStarted' })
|
||||
} else if (notice) {
|
||||
showNotice.warning(
|
||||
'settings.feedback.notifications.clashService.sidecarFallback',
|
||||
)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(
|
||||
'Failed to read the pending service fallback notice',
|
||||
error,
|
||||
)
|
||||
})
|
||||
},
|
||||
'dns_override::auto_disabled': () => {
|
||||
void takeDnsOverrideNotice()
|
||||
.then((pending) => {
|
||||
if (pending) {
|
||||
showNotice.info('settings.modals.dns.protection.autoDisabled')
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to read the pending DNS override notice', error)
|
||||
})
|
||||
},
|
||||
'enhance::discarded_keys': () => {
|
||||
void takeDiscardedKeysNotice()
|
||||
.then((keys) => {
|
||||
if (keys) {
|
||||
showNotice.warning('profiles.page.feedback.notices.discardedKeys', {
|
||||
keys,
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to read the pending discarded keys notice', error)
|
||||
})
|
||||
},
|
||||
'tun_mode::auto_disabled': () =>
|
||||
showNotice.info(
|
||||
'settings.sections.system.notifications.tunMode.autoDisabled',
|
||||
),
|
||||
'tun_mode::auto_disable_failed': () =>
|
||||
showNotice.error(
|
||||
'settings.sections.system.notifications.tunMode.autoDisableFailed',
|
||||
),
|
||||
'app_restart::core_stop_failed': (msg) =>
|
||||
showNotice.error(
|
||||
'layout.feedback.errors.restartCoreStopFailed',
|
||||
msg || undefined,
|
||||
),
|
||||
'app_quit::core_stop_failed': (msg) =>
|
||||
showNotice.error(
|
||||
'layout.feedback.errors.quitCoreStopFailed',
|
||||
msg || undefined,
|
||||
),
|
||||
update_with_clash_proxy: (msg) =>
|
||||
showNotice.success(
|
||||
'settings.feedback.notifications.updater.withClashProxySuccess',
|
||||
msg,
|
||||
),
|
||||
update_failed_even_with_clash: (msg) =>
|
||||
showNotice.error(
|
||||
'settings.feedback.notifications.updater.withClashProxyFailed',
|
||||
msg,
|
||||
),
|
||||
'reactivate_profiles::error': (msg) => showNotice.error(msg),
|
||||
update_failed: (msg) => showNotice.error(msg),
|
||||
'config_validate::boot_error': (msg) =>
|
||||
showNotice.error('shared.feedback.validation.config.bootFailed', msg),
|
||||
'config_validate::error': (msg) =>
|
||||
showNotice.error('shared.feedback.validation.config.failed', msg),
|
||||
'config_validate::process_terminated': () =>
|
||||
showNotice.error('shared.feedback.validation.config.processTerminated'),
|
||||
'config_validate::script_error': (msg) =>
|
||||
showNotice.error('shared.feedback.validation.script.fileError', msg),
|
||||
'config_validate::script_syntax_error': (msg) =>
|
||||
showNotice.error('shared.feedback.validation.script.syntaxError', msg),
|
||||
'config_validate::script_missing_main': (msg) =>
|
||||
showNotice.error('shared.feedback.validation.script.missingMain', msg),
|
||||
'config_validate::file_not_found': (msg) =>
|
||||
showNotice.error('shared.feedback.validation.script.fileNotFound', msg),
|
||||
'config_validate::yaml_syntax_error': (msg) =>
|
||||
showNotice.error('shared.feedback.validation.yaml.syntaxError', msg),
|
||||
'config_validate::yaml_read_error': (msg) =>
|
||||
showNotice.error('shared.feedback.validation.yaml.readError', msg),
|
||||
'config_validate::yaml_mapping_error': (msg) =>
|
||||
showNotice.error('shared.feedback.validation.yaml.mappingError', msg),
|
||||
'config_validate::merge_syntax_error': (msg) =>
|
||||
showNotice.error('shared.feedback.validation.merge.syntaxError', msg),
|
||||
'config_validate::merge_mapping_error': (msg) =>
|
||||
showNotice.error('shared.feedback.validation.merge.mappingError', msg),
|
||||
'config_core::change_success': (msg) =>
|
||||
showNotice.success(
|
||||
'settings.feedback.notifications.clash.changeSuccess',
|
||||
msg,
|
||||
),
|
||||
'config_core::change_error': (msg) =>
|
||||
showNotice.error('settings.feedback.notifications.clash.changeFailed', msg),
|
||||
'mixed_port::fallback': (msg) => {
|
||||
const [oldPort, newPort] = msg.split(',')
|
||||
showNotice.info('settings.modals.clashPort.messages.automaticFallback', {
|
||||
oldPort,
|
||||
newPort,
|
||||
})
|
||||
},
|
||||
'mixed_port::fallback_error': (msg) =>
|
||||
showNotice.error(
|
||||
'settings.modals.clashPort.messages.automaticFallbackFailed',
|
||||
{ error: msg },
|
||||
),
|
||||
}
|
||||
|
||||
export const handleNoticeMessage = (
|
||||
status: string,
|
||||
msg: string,
|
||||
t: TranslateFunction,
|
||||
navigate: NavigateFunction,
|
||||
) => {
|
||||
const handlers: Record<NoticeStatus, () => void> = {
|
||||
info: () => {
|
||||
if (msg) showNotice.info(msg)
|
||||
},
|
||||
'import_sub_url::ok': () => {
|
||||
// 空 msg 传入,我们不希望导致 后端-前端-后端 死循环,这里只做提醒。
|
||||
// 未来细分事件通知时,可以考虑传入订阅 ID 或其他标识符
|
||||
// navigate("/profile", { state: { current: msg } });
|
||||
navigate('/profile')
|
||||
showNotice.success(
|
||||
'shared.feedback.notifications.importSubscriptionSuccess',
|
||||
)
|
||||
},
|
||||
'import_sub_url::error': () => {
|
||||
navigate('/profile')
|
||||
showNotice.error(msg)
|
||||
},
|
||||
'set_config::error': () => showNotice.error(msg),
|
||||
'core_start::error': () => {
|
||||
const read = ++startupErrorReads
|
||||
void getCoreStartupError()
|
||||
.then(async (failure) => {
|
||||
if (read < settledStartupErrorRead) return
|
||||
settledStartupErrorRead = read
|
||||
if (!failure) {
|
||||
shownStartupError = null
|
||||
return
|
||||
}
|
||||
const window = getCurrentWindow()
|
||||
const [visible, minimized] = await Promise.all([
|
||||
window.isVisible(),
|
||||
window.isMinimized(),
|
||||
])
|
||||
const shown = `${failure.kind}:${failure.detail}`
|
||||
if (
|
||||
read === settledStartupErrorRead &&
|
||||
visible &&
|
||||
!minimized &&
|
||||
shownStartupError !== shown
|
||||
) {
|
||||
shownStartupError = shown
|
||||
showNotice.error(
|
||||
failure.kind === 'serviceCoreStopped'
|
||||
? 'settings.feedback.errors.clash.serviceCoreStopped'
|
||||
: 'settings.feedback.errors.clash.startFailed',
|
||||
failure.detail,
|
||||
)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to read the pending core startup error', error)
|
||||
})
|
||||
},
|
||||
'service_core::repair_required': () => {
|
||||
void takeServiceRepairNotice()
|
||||
.then((pending) => {
|
||||
if (pending) {
|
||||
requestService({ reason: 'serviceLocationRefused' })
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(
|
||||
'Failed to read the pending service repair notice',
|
||||
error,
|
||||
)
|
||||
})
|
||||
},
|
||||
'service_core::app_data_not_owned': () => {
|
||||
void takeServiceOwnerNotice()
|
||||
.then((command) => {
|
||||
if (command) {
|
||||
if (shownOwnerNotice !== null) hideNotice(shownOwnerNotice)
|
||||
shownOwnerNotice = showNotice.warning(
|
||||
'settings.feedback.notifications.clashService.appDataNotOwned',
|
||||
{ command },
|
||||
0,
|
||||
)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(
|
||||
'Failed to read the pending service owner notice',
|
||||
error,
|
||||
)
|
||||
})
|
||||
},
|
||||
'service_core::sidecar_fallback': () => {
|
||||
void takeServiceFallbackNotice()
|
||||
.then((notice) => {
|
||||
if (notice?.kind === 'coreRejected') {
|
||||
showNotice.warning(
|
||||
'settings.feedback.notifications.clashService.permissionFallback',
|
||||
{ reason: notice.reason },
|
||||
0,
|
||||
)
|
||||
} else if (notice?.kind === 'notAutoStarted') {
|
||||
requestService({ reason: 'serviceNotAutoStarted' })
|
||||
} else if (notice) {
|
||||
showNotice.warning(
|
||||
'settings.feedback.notifications.clashService.sidecarFallback',
|
||||
)
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(
|
||||
'Failed to read the pending service fallback notice',
|
||||
error,
|
||||
)
|
||||
})
|
||||
},
|
||||
'dns_override::auto_disabled': () => {
|
||||
void takeDnsOverrideNotice()
|
||||
.then((pending) => {
|
||||
if (pending) {
|
||||
showNotice.info('settings.modals.dns.protection.autoDisabled')
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('Failed to read the pending DNS override notice', error)
|
||||
})
|
||||
},
|
||||
'enhance::discarded_keys': () => {
|
||||
void takeDiscardedKeysNotice()
|
||||
.then((keys) => {
|
||||
if (keys) {
|
||||
showNotice.warning('profiles.page.feedback.notices.discardedKeys', {
|
||||
keys,
|
||||
})
|
||||
}
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error(
|
||||
'Failed to read the pending discarded keys notice',
|
||||
error,
|
||||
)
|
||||
})
|
||||
},
|
||||
'tun_mode::auto_disabled': () =>
|
||||
showNotice.info(
|
||||
'settings.sections.system.notifications.tunMode.autoDisabled',
|
||||
),
|
||||
'tun_mode::auto_disable_failed': () =>
|
||||
showNotice.error(
|
||||
'settings.sections.system.notifications.tunMode.autoDisableFailed',
|
||||
),
|
||||
'app_restart::core_stop_failed': () =>
|
||||
showNotice.error(
|
||||
'layout.feedback.errors.restartCoreStopFailed',
|
||||
msg || undefined,
|
||||
),
|
||||
'app_quit::core_stop_failed': () =>
|
||||
showNotice.error(
|
||||
'layout.feedback.errors.quitCoreStopFailed',
|
||||
msg || undefined,
|
||||
),
|
||||
update_with_clash_proxy: () =>
|
||||
showNotice.success(
|
||||
'settings.feedback.notifications.updater.withClashProxySuccess',
|
||||
msg,
|
||||
),
|
||||
update_failed_even_with_clash: () =>
|
||||
showNotice.error(
|
||||
'settings.feedback.notifications.updater.withClashProxyFailed',
|
||||
msg,
|
||||
),
|
||||
'reactivate_profiles::error': () => showNotice.error(msg),
|
||||
update_failed: () => showNotice.error(msg),
|
||||
'config_validate::boot_error': () =>
|
||||
showNotice.error('shared.feedback.validation.config.bootFailed', msg),
|
||||
'config_validate::error': () =>
|
||||
showNotice.error('shared.feedback.validation.config.failed', msg),
|
||||
'config_validate::process_terminated': () =>
|
||||
showNotice.error('shared.feedback.validation.config.processTerminated'),
|
||||
'config_validate::script_error': () =>
|
||||
showNotice.error('shared.feedback.validation.script.fileError', msg),
|
||||
'config_validate::script_syntax_error': () =>
|
||||
showNotice.error('shared.feedback.validation.script.syntaxError', msg),
|
||||
'config_validate::script_missing_main': () =>
|
||||
showNotice.error('shared.feedback.validation.script.missingMain', msg),
|
||||
'config_validate::file_not_found': () =>
|
||||
showNotice.error('shared.feedback.validation.script.fileNotFound', msg),
|
||||
'config_validate::yaml_syntax_error': () =>
|
||||
showNotice.error('shared.feedback.validation.yaml.syntaxError', msg),
|
||||
'config_validate::yaml_read_error': () =>
|
||||
showNotice.error('shared.feedback.validation.yaml.readError', msg),
|
||||
'config_validate::yaml_mapping_error': () =>
|
||||
showNotice.error('shared.feedback.validation.yaml.mappingError', msg),
|
||||
'config_validate::merge_syntax_error': () =>
|
||||
showNotice.error('shared.feedback.validation.merge.syntaxError', msg),
|
||||
'config_validate::merge_mapping_error': () =>
|
||||
showNotice.error('shared.feedback.validation.merge.mappingError', msg),
|
||||
'config_core::change_success': () =>
|
||||
showNotice.success(
|
||||
'settings.feedback.notifications.clash.changeSuccess',
|
||||
msg,
|
||||
),
|
||||
'config_core::change_error': () =>
|
||||
showNotice.error(
|
||||
'settings.feedback.notifications.clash.changeFailed',
|
||||
msg,
|
||||
),
|
||||
'mixed_port::fallback': () => {
|
||||
const [oldPort, newPort] = msg.split(',')
|
||||
showNotice.info('settings.modals.clashPort.messages.automaticFallback', {
|
||||
oldPort,
|
||||
newPort,
|
||||
})
|
||||
},
|
||||
'mixed_port::fallback_error': () =>
|
||||
showNotice.error(
|
||||
'settings.modals.clashPort.messages.automaticFallbackFailed',
|
||||
{ error: msg },
|
||||
),
|
||||
}
|
||||
|
||||
const handler = handlers[status as NoticeStatus]
|
||||
if (handler) {
|
||||
handler()
|
||||
handler(msg, navigate)
|
||||
} else {
|
||||
console.warn(`未处理的通知状态: ${status}`)
|
||||
}
|
||||
|
||||
@@ -66,6 +66,7 @@ const withLoading = (
|
||||
next.delete(uid)
|
||||
}
|
||||
}
|
||||
if (next.size === state.profileUpdates.loading.size) return state
|
||||
return {
|
||||
...state,
|
||||
profileUpdates: { ...state.profileUpdates, loading: next },
|
||||
|
||||
Reference in New Issue
Block a user