Merge branch 'dev' into chore/i18n
# Conflicts: # src/components/setting/mods/clash-port-viewer.tsx
This commit is contained in:
2
.github/workflows/autobuild.yml
vendored
2
.github/workflows/autobuild.yml
vendored
@@ -103,7 +103,7 @@ jobs:
|
||||
- [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_amd64_linux.deb) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_arm64.deb) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}_armhf.deb)
|
||||
|
||||
#### RPM包(Redhat系) 使用 dnf ./路径 安装
|
||||
- [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}-1.x86_64_linux.rpm) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}-1.aarch64.rpm) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge_${{ env.VERSION }}-1.armhfp.rpm)
|
||||
- [64位](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.x86_64_linux.rpm) | [ARM64](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.aarch64.rpm) | [ARMv7](${{ env.DOWNLOAD_URL }}/Clash.Verge-${{ env.VERSION }}-1.armhfp.rpm)
|
||||
|
||||
### FAQ
|
||||
- [常见问题](https://clash-verge-rev.github.io/faq/windows.html)
|
||||
|
||||
@@ -87,6 +87,7 @@
|
||||
- 在 macOS 10.15 及更高版本默认包含 Mihomo-go122,以解决 Intel 架构 Mac 无法运行内核的问题
|
||||
- Tun 模式不可用时,禁用系统托盘的 Tun 模式菜单
|
||||
- 改进订阅更新方式,仍失败需打开订阅设置 `允许危险证书`
|
||||
- 允许设置 Mihomo 端口范围 1000(含) - 65536(含)
|
||||
|
||||
</details>
|
||||
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
"react-dom": "19.2.0",
|
||||
"react-error-boundary": "6.0.0",
|
||||
"react-hook-form": "^7.66.0",
|
||||
"react-i18next": "16.2.3",
|
||||
"react-i18next": "16.2.4",
|
||||
"react-markdown": "10.1.0",
|
||||
"react-monaco-editor": "0.59.0",
|
||||
"react-router": "^7.9.5",
|
||||
@@ -81,7 +81,7 @@
|
||||
"devDependencies": {
|
||||
"@actions/github": "^6.0.1",
|
||||
"@eslint-react/eslint-plugin": "^2.3.1",
|
||||
"@eslint/js": "^9.39.0",
|
||||
"@eslint/js": "^9.39.1",
|
||||
"@tauri-apps/cli": "2.9.2",
|
||||
"@types/js-yaml": "^4.0.9",
|
||||
"@types/lodash-es": "^4.17.12",
|
||||
@@ -94,7 +94,7 @@
|
||||
"cli-color": "^2.0.4",
|
||||
"commander": "^14.0.2",
|
||||
"cross-env": "^10.1.0",
|
||||
"eslint": "^9.39.0",
|
||||
"eslint": "^9.39.1",
|
||||
"eslint-config-prettier": "^10.1.8",
|
||||
"eslint-import-resolver-typescript": "^4.4.4",
|
||||
"eslint-plugin-import-x": "^4.16.1",
|
||||
@@ -115,7 +115,7 @@
|
||||
"tar": "^7.5.2",
|
||||
"terser": "^5.44.0",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.46.2",
|
||||
"typescript-eslint": "^8.46.3",
|
||||
"vite": "^7.1.12",
|
||||
"vite-plugin-monaco-editor-esm": "^2.0.2",
|
||||
"vite-plugin-svgr": "^4.5.0",
|
||||
|
||||
414
pnpm-lock.yaml
generated
414
pnpm-lock.yaml
generated
@@ -111,8 +111,8 @@ importers:
|
||||
specifier: ^7.66.0
|
||||
version: 7.66.0(react@19.2.0)
|
||||
react-i18next:
|
||||
specifier: 16.2.3
|
||||
version: 16.2.3(i18next@25.6.0(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3)
|
||||
specifier: 16.2.4
|
||||
version: 16.2.4(i18next@25.6.0(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3)
|
||||
react-markdown:
|
||||
specifier: 10.1.0
|
||||
version: 10.1.0(@types/react@19.2.2)(react@19.2.0)
|
||||
@@ -140,10 +140,10 @@ importers:
|
||||
version: 6.0.1
|
||||
'@eslint-react/eslint-plugin':
|
||||
specifier: ^2.3.1
|
||||
version: 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
version: 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint/js':
|
||||
specifier: ^9.39.0
|
||||
version: 9.39.0
|
||||
specifier: ^9.39.1
|
||||
version: 9.39.1
|
||||
'@tauri-apps/cli':
|
||||
specifier: 2.9.2
|
||||
version: 2.9.2
|
||||
@@ -181,29 +181,29 @@ importers:
|
||||
specifier: ^10.1.0
|
||||
version: 10.1.0
|
||||
eslint:
|
||||
specifier: ^9.39.0
|
||||
version: 9.39.0(jiti@2.6.1)
|
||||
specifier: ^9.39.1
|
||||
version: 9.39.1(jiti@2.6.1)
|
||||
eslint-config-prettier:
|
||||
specifier: ^10.1.8
|
||||
version: 10.1.8(eslint@9.39.0(jiti@2.6.1))
|
||||
version: 10.1.8(eslint@9.39.1(jiti@2.6.1))
|
||||
eslint-import-resolver-typescript:
|
||||
specifier: ^4.4.4
|
||||
version: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.0(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.0(jiti@2.6.1))
|
||||
version: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1))
|
||||
eslint-plugin-import-x:
|
||||
specifier: ^4.16.1
|
||||
version: 4.16.1(@typescript-eslint/utils@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.0(jiti@2.6.1))
|
||||
version: 4.16.1(@typescript-eslint/utils@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1))
|
||||
eslint-plugin-prettier:
|
||||
specifier: ^5.5.4
|
||||
version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.39.0(jiti@2.6.1)))(eslint@9.39.0(jiti@2.6.1))(prettier@3.6.2)
|
||||
version: 5.5.4(eslint-config-prettier@10.1.8(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2)
|
||||
eslint-plugin-react-hooks:
|
||||
specifier: ^7.0.1
|
||||
version: 7.0.1(eslint@9.39.0(jiti@2.6.1))
|
||||
version: 7.0.1(eslint@9.39.1(jiti@2.6.1))
|
||||
eslint-plugin-react-refresh:
|
||||
specifier: ^0.4.24
|
||||
version: 0.4.24(eslint@9.39.0(jiti@2.6.1))
|
||||
version: 0.4.24(eslint@9.39.1(jiti@2.6.1))
|
||||
eslint-plugin-unused-imports:
|
||||
specifier: ^4.3.0
|
||||
version: 4.3.0(@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.0(jiti@2.6.1))
|
||||
version: 4.3.0(@typescript-eslint/eslint-plugin@8.46.3(@typescript-eslint/parser@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))
|
||||
glob:
|
||||
specifier: ^11.0.3
|
||||
version: 11.0.3
|
||||
@@ -244,8 +244,8 @@ importers:
|
||||
specifier: ^5.9.3
|
||||
version: 5.9.3
|
||||
typescript-eslint:
|
||||
specifier: ^8.46.2
|
||||
version: 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
specifier: ^8.46.3
|
||||
version: 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
vite:
|
||||
specifier: ^7.1.12
|
||||
version: 7.1.12(@types/node@24.10.0)(jiti@2.6.1)(sass@1.93.3)(terser@5.44.0)(yaml@2.8.1)
|
||||
@@ -1053,8 +1053,8 @@ packages:
|
||||
resolution: {integrity: sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@eslint/js@9.39.0':
|
||||
resolution: {integrity: sha512-BIhe0sW91JGPiaF1mOuPy5v8NflqfjIcDNpC+LbW9f609WVRX1rArrhi6Z2ymvrAry9jw+5POTj4t2t62o8Bmw==}
|
||||
'@eslint/js@9.39.1':
|
||||
resolution: {integrity: sha512-S26Stp4zCy88tH94QbBv3XCuzRQiZ9yXofEILmglYTh/Ug/a9/umqvgFtYBAo3Lp0nsI/5/qH1CCrbdK3AP1Tw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@eslint/object-schema@2.1.7':
|
||||
@@ -1849,63 +1849,63 @@ packages:
|
||||
'@types/unist@3.0.3':
|
||||
resolution: {integrity: sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==}
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.46.2':
|
||||
resolution: {integrity: sha512-ZGBMToy857/NIPaaCucIUQgqueOiq7HeAKkhlvqVV4lm089zUFW6ikRySx2v+cAhKeUCPuWVHeimyk6Dw1iY3w==}
|
||||
'@typescript-eslint/eslint-plugin@8.46.3':
|
||||
resolution: {integrity: sha512-sbaQ27XBUopBkRiuY/P9sWGOWUW4rl8fDoHIUmLpZd8uldsTyB4/Zg6bWTegPoTLnKj9Hqgn3QD6cjPNB32Odw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
'@typescript-eslint/parser': ^8.46.2
|
||||
'@typescript-eslint/parser': ^8.46.3
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/parser@8.46.2':
|
||||
resolution: {integrity: sha512-BnOroVl1SgrPLywqxyqdJ4l3S2MsKVLDVxZvjI1Eoe8ev2r3kGDo+PcMihNmDE+6/KjkTubSJnmqGZZjQSBq/g==}
|
||||
'@typescript-eslint/parser@8.46.3':
|
||||
resolution: {integrity: sha512-6m1I5RmHBGTnUGS113G04DMu3CpSdxCAU/UvtjNWL4Nuf3MW9tQhiJqRlHzChIkhy6kZSAQmc+I1bcGjE3yNKg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/project-service@8.46.2':
|
||||
resolution: {integrity: sha512-PULOLZ9iqwI7hXcmL4fVfIsBi6AN9YxRc0frbvmg8f+4hQAjQ5GYNKK0DIArNo+rOKmR/iBYwkpBmnIwin4wBg==}
|
||||
'@typescript-eslint/project-service@8.46.3':
|
||||
resolution: {integrity: sha512-Fz8yFXsp2wDFeUElO88S9n4w1I4CWDTXDqDr9gYvZgUpwXQqmZBr9+NTTql5R3J7+hrJZPdpiWaB9VNhAKYLuQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/scope-manager@8.46.2':
|
||||
resolution: {integrity: sha512-LF4b/NmGvdWEHD2H4MsHD8ny6JpiVNDzrSZr3CsckEgCbAGZbYM4Cqxvi9L+WqDMT+51Ozy7lt2M+d0JLEuBqA==}
|
||||
'@typescript-eslint/scope-manager@8.46.3':
|
||||
resolution: {integrity: sha512-FCi7Y1zgrmxp3DfWfr+3m9ansUUFoy8dkEdeQSgA9gbm8DaHYvZCdkFRQrtKiedFf3Ha6VmoqoAaP68+i+22kg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/tsconfig-utils@8.46.2':
|
||||
resolution: {integrity: sha512-a7QH6fw4S57+F5y2FIxxSDyi5M4UfGF+Jl1bCGd7+L4KsaUY80GsiF/t0UoRFDHAguKlBaACWJRmdrc6Xfkkag==}
|
||||
'@typescript-eslint/tsconfig-utils@8.46.3':
|
||||
resolution: {integrity: sha512-GLupljMniHNIROP0zE7nCcybptolcH8QZfXOpCfhQDAdwJ/ZTlcaBOYebSOZotpti/3HrHSw7D3PZm75gYFsOA==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/type-utils@8.46.2':
|
||||
resolution: {integrity: sha512-HbPM4LbaAAt/DjxXaG9yiS9brOOz6fabal4uvUmaUYe6l3K1phQDMQKBRUrr06BQkxkvIZVVHttqiybM9nJsLA==}
|
||||
'@typescript-eslint/type-utils@8.46.3':
|
||||
resolution: {integrity: sha512-ZPCADbr+qfz3aiTTYNNkCbUt+cjNwI/5McyANNrFBpVxPt7GqpEYz5ZfdwuFyGUnJ9FdDXbGODUu6iRCI6XRXw==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/types@8.46.2':
|
||||
resolution: {integrity: sha512-lNCWCbq7rpg7qDsQrd3D6NyWYu+gkTENkG5IKYhUIcxSb59SQC/hEQ+MrG4sTgBVghTonNWq42bA/d4yYumldQ==}
|
||||
'@typescript-eslint/types@8.46.3':
|
||||
resolution: {integrity: sha512-G7Ok9WN/ggW7e/tOf8TQYMaxgID3Iujn231hfi0Pc7ZheztIJVpO44ekY00b7akqc6nZcvregk0Jpah3kep6hA==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.46.2':
|
||||
resolution: {integrity: sha512-f7rW7LJ2b7Uh2EiQ+7sza6RDZnajbNbemn54Ob6fRwQbgcIn+GWfyuHDHRYgRoZu1P4AayVScrRW+YfbTvPQoQ==}
|
||||
'@typescript-eslint/typescript-estree@8.46.3':
|
||||
resolution: {integrity: sha512-f/NvtRjOm80BtNM5OQtlaBdM5BRFUv7gf381j9wygDNL+qOYSNOgtQ/DCndiYi80iIOv76QqaTmp4fa9hwI0OA==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/utils@8.46.2':
|
||||
resolution: {integrity: sha512-sExxzucx0Tud5tE0XqR0lT0psBQvEpnpiul9XbGUB1QwpWJJAps1O/Z7hJxLGiZLBKMCutjTzDgmd1muEhBnVg==}
|
||||
'@typescript-eslint/utils@8.46.3':
|
||||
resolution: {integrity: sha512-VXw7qmdkucEx9WkmR3ld/u6VhRyKeiF1uxWwCy/iuNfokjJ7VhsgLSOTjsol8BunSw190zABzpwdNsze2Kpo4g==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
typescript: '>=4.8.4 <6.0.0'
|
||||
|
||||
'@typescript-eslint/visitor-keys@8.46.2':
|
||||
resolution: {integrity: sha512-tUFMXI4gxzzMXt4xpGJEsBsTox0XbNQ1y94EwlD/CuZwFcQP79xfQqMhau9HsRc/J0cAPA/HZt1dZPtGn9V/7w==}
|
||||
'@typescript-eslint/visitor-keys@8.46.3':
|
||||
resolution: {integrity: sha512-uk574k8IU0rOF/AjniX8qbLSGURJVUCeM5e4MIMKBFFi8weeiLrG1fyQejyLXQpRZbU/1BuQasleV/RfHC3hHg==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
'@ungap/structured-clone@1.3.0':
|
||||
@@ -2670,8 +2670,8 @@ packages:
|
||||
resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
|
||||
eslint@9.39.0:
|
||||
resolution: {integrity: sha512-iy2GE3MHrYTL5lrCtMZ0X1KLEKKUjmK0kzwcnefhR66txcEmXZD2YWgR5GNdcEwkNx3a0siYkSvl0vIC+Svjmg==}
|
||||
eslint@9.39.1:
|
||||
resolution: {integrity: sha512-BhHmn2yNOFA9H9JmmIVKJmd288g9hrVRDkdoIgRCRuSySRUHH7r/DI6aAXW9T1WwUuY3DFgrcaqB+deURBLR5g==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
hasBin: true
|
||||
peerDependencies:
|
||||
@@ -3624,8 +3624,8 @@ packages:
|
||||
peerDependencies:
|
||||
react: ^16.8.0 || ^17 || ^18 || ^19
|
||||
|
||||
react-i18next@16.2.3:
|
||||
resolution: {integrity: sha512-O0t2zvmIz7nHWKNfIL+O/NTIbpTaOPY0vZov779hegbep3IZ+xcqkeVPKWBSXwzdkiv77q8zmq9toKIUys1x3A==}
|
||||
react-i18next@16.2.4:
|
||||
resolution: {integrity: sha512-pvbcPQ+YuQQoRkKBA4VCU9aO8dOgP/vdKEizIYXcAk3+AmI8yQKSJaCzxQQu4Kgg2zWZm3ax9KqHv8ItUlRY0A==}
|
||||
peerDependencies:
|
||||
i18next: '>= 25.5.2'
|
||||
react: '>= 16.8.0'
|
||||
@@ -4059,8 +4059,8 @@ packages:
|
||||
types-pac@1.0.3:
|
||||
resolution: {integrity: sha512-MF2UAZGvGMOM+vHi9Zj/LvQqdNN1m1xSB+PjAW9B/GvFqaB4GwR18YaIbGIGDRTW/J8iqFXQHLZd5eJVtho46w==}
|
||||
|
||||
typescript-eslint@8.46.2:
|
||||
resolution: {integrity: sha512-vbw8bOmiuYNdzzV3lsiWv6sRwjyuKJMQqWulBOU7M0RrxedXledX8G8kBbQeiOYDnTfiXz0Y4081E1QMNB6iQg==}
|
||||
typescript-eslint@8.46.3:
|
||||
resolution: {integrity: sha512-bAfgMavTuGo+8n6/QQDVQz4tZ4f7Soqg53RbrlZQEoAltYop/XR4RAts/I0BrO3TTClTSTFJ0wYbla+P8cEWJA==}
|
||||
engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0}
|
||||
peerDependencies:
|
||||
eslint: ^8.57.0 || ^9.0.0
|
||||
@@ -5199,34 +5199,34 @@ snapshots:
|
||||
'@esbuild/win32-x64@0.25.4':
|
||||
optional: true
|
||||
|
||||
'@eslint-community/eslint-utils@4.8.0(eslint@9.39.0(jiti@2.6.1))':
|
||||
'@eslint-community/eslint-utils@4.8.0(eslint@9.39.1(jiti@2.6.1))':
|
||||
dependencies:
|
||||
eslint: 9.39.0(jiti@2.6.1)
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
eslint-visitor-keys: 3.4.3
|
||||
|
||||
'@eslint-community/regexpp@4.12.1': {}
|
||||
|
||||
'@eslint-react/ast@2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)':
|
||||
'@eslint-react/ast@2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@eslint-react/eff': 2.3.1
|
||||
'@typescript-eslint/types': 8.46.2
|
||||
'@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3)
|
||||
'@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.46.3
|
||||
'@typescript-eslint/typescript-estree': 8.46.3(typescript@5.9.3)
|
||||
'@typescript-eslint/utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
string-ts: 2.2.1
|
||||
transitivePeerDependencies:
|
||||
- eslint
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@eslint-react/core@2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)':
|
||||
'@eslint-react/core@2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@eslint-react/ast': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/ast': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/eff': 2.3.1
|
||||
'@eslint-react/shared': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/var': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/scope-manager': 8.46.2
|
||||
'@typescript-eslint/types': 8.46.2
|
||||
'@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/shared': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/var': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/scope-manager': 8.46.3
|
||||
'@typescript-eslint/types': 8.46.3
|
||||
'@typescript-eslint/utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
birecord: 0.1.1
|
||||
ts-pattern: 5.9.0
|
||||
transitivePeerDependencies:
|
||||
@@ -5236,29 +5236,29 @@ snapshots:
|
||||
|
||||
'@eslint-react/eff@2.3.1': {}
|
||||
|
||||
'@eslint-react/eslint-plugin@2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)':
|
||||
'@eslint-react/eslint-plugin@2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@eslint-react/eff': 2.3.1
|
||||
'@eslint-react/shared': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/scope-manager': 8.46.2
|
||||
'@typescript-eslint/type-utils': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.46.2
|
||||
'@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint: 9.39.0(jiti@2.6.1)
|
||||
eslint-plugin-react-dom: 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint-plugin-react-hooks-extra: 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint-plugin-react-naming-convention: 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint-plugin-react-web-api: 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint-plugin-react-x: 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/shared': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/scope-manager': 8.46.3
|
||||
'@typescript-eslint/type-utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.46.3
|
||||
'@typescript-eslint/utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
eslint-plugin-react-dom: 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint-plugin-react-hooks-extra: 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint-plugin-react-naming-convention: 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint-plugin-react-web-api: 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint-plugin-react-x: 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
ts-api-utils: 2.1.0(typescript@5.9.3)
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@eslint-react/shared@2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)':
|
||||
'@eslint-react/shared@2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@eslint-react/eff': 2.3.1
|
||||
'@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
ts-pattern: 5.9.0
|
||||
zod: 4.1.12
|
||||
transitivePeerDependencies:
|
||||
@@ -5266,13 +5266,13 @@ snapshots:
|
||||
- supports-color
|
||||
- typescript
|
||||
|
||||
'@eslint-react/var@2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)':
|
||||
'@eslint-react/var@2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@eslint-react/ast': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/ast': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/eff': 2.3.1
|
||||
'@typescript-eslint/scope-manager': 8.46.2
|
||||
'@typescript-eslint/types': 8.46.2
|
||||
'@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/scope-manager': 8.46.3
|
||||
'@typescript-eslint/types': 8.46.3
|
||||
'@typescript-eslint/utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
ts-pattern: 5.9.0
|
||||
transitivePeerDependencies:
|
||||
- eslint
|
||||
@@ -5309,7 +5309,7 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@eslint/js@9.39.0': {}
|
||||
'@eslint/js@9.39.1': {}
|
||||
|
||||
'@eslint/object-schema@2.1.7': {}
|
||||
|
||||
@@ -6006,15 +6006,15 @@ snapshots:
|
||||
|
||||
'@types/unist@3.0.3': {}
|
||||
|
||||
'@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)':
|
||||
'@typescript-eslint/eslint-plugin@8.46.3(@typescript-eslint/parser@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@eslint-community/regexpp': 4.12.1
|
||||
'@typescript-eslint/parser': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/scope-manager': 8.46.2
|
||||
'@typescript-eslint/type-utils': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/visitor-keys': 8.46.2
|
||||
eslint: 9.39.0(jiti@2.6.1)
|
||||
'@typescript-eslint/parser': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/scope-manager': 8.46.3
|
||||
'@typescript-eslint/type-utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/visitor-keys': 8.46.3
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
graphemer: 1.4.0
|
||||
ignore: 7.0.5
|
||||
natural-compare: 1.4.0
|
||||
@@ -6023,56 +6023,56 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)':
|
||||
'@typescript-eslint/parser@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/scope-manager': 8.46.2
|
||||
'@typescript-eslint/types': 8.46.2
|
||||
'@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3)
|
||||
'@typescript-eslint/visitor-keys': 8.46.2
|
||||
'@typescript-eslint/scope-manager': 8.46.3
|
||||
'@typescript-eslint/types': 8.46.3
|
||||
'@typescript-eslint/typescript-estree': 8.46.3(typescript@5.9.3)
|
||||
'@typescript-eslint/visitor-keys': 8.46.3
|
||||
debug: 4.4.3
|
||||
eslint: 9.39.0(jiti@2.6.1)
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/project-service@8.46.2(typescript@5.9.3)':
|
||||
'@typescript-eslint/project-service@8.46.3(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.46.2
|
||||
'@typescript-eslint/tsconfig-utils': 8.46.3(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.46.3
|
||||
debug: 4.4.3
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/scope-manager@8.46.2':
|
||||
'@typescript-eslint/scope-manager@8.46.3':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.46.2
|
||||
'@typescript-eslint/visitor-keys': 8.46.2
|
||||
'@typescript-eslint/types': 8.46.3
|
||||
'@typescript-eslint/visitor-keys': 8.46.3
|
||||
|
||||
'@typescript-eslint/tsconfig-utils@8.46.2(typescript@5.9.3)':
|
||||
'@typescript-eslint/tsconfig-utils@8.46.3(typescript@5.9.3)':
|
||||
dependencies:
|
||||
typescript: 5.9.3
|
||||
|
||||
'@typescript-eslint/type-utils@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)':
|
||||
'@typescript-eslint/type-utils@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.46.2
|
||||
'@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3)
|
||||
'@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.46.3
|
||||
'@typescript-eslint/typescript-estree': 8.46.3(typescript@5.9.3)
|
||||
'@typescript-eslint/utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
debug: 4.4.3
|
||||
eslint: 9.39.0(jiti@2.6.1)
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
ts-api-utils: 2.1.0(typescript@5.9.3)
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/types@8.46.2': {}
|
||||
'@typescript-eslint/types@8.46.3': {}
|
||||
|
||||
'@typescript-eslint/typescript-estree@8.46.2(typescript@5.9.3)':
|
||||
'@typescript-eslint/typescript-estree@8.46.3(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@typescript-eslint/project-service': 8.46.2(typescript@5.9.3)
|
||||
'@typescript-eslint/tsconfig-utils': 8.46.2(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.46.2
|
||||
'@typescript-eslint/visitor-keys': 8.46.2
|
||||
'@typescript-eslint/project-service': 8.46.3(typescript@5.9.3)
|
||||
'@typescript-eslint/tsconfig-utils': 8.46.3(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.46.3
|
||||
'@typescript-eslint/visitor-keys': 8.46.3
|
||||
debug: 4.4.3
|
||||
fast-glob: 3.3.3
|
||||
is-glob: 4.0.3
|
||||
@@ -6083,20 +6083,20 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/utils@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)':
|
||||
'@typescript-eslint/utils@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)':
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.8.0(eslint@9.39.0(jiti@2.6.1))
|
||||
'@typescript-eslint/scope-manager': 8.46.2
|
||||
'@typescript-eslint/types': 8.46.2
|
||||
'@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3)
|
||||
eslint: 9.39.0(jiti@2.6.1)
|
||||
'@eslint-community/eslint-utils': 4.8.0(eslint@9.39.1(jiti@2.6.1))
|
||||
'@typescript-eslint/scope-manager': 8.46.3
|
||||
'@typescript-eslint/types': 8.46.3
|
||||
'@typescript-eslint/typescript-estree': 8.46.3(typescript@5.9.3)
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@typescript-eslint/visitor-keys@8.46.2':
|
||||
'@typescript-eslint/visitor-keys@8.46.3':
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.46.2
|
||||
'@typescript-eslint/types': 8.46.3
|
||||
eslint-visitor-keys: 4.2.1
|
||||
|
||||
'@ungap/structured-clone@1.3.0': {}
|
||||
@@ -6805,9 +6805,9 @@ snapshots:
|
||||
|
||||
escape-string-regexp@4.0.0: {}
|
||||
|
||||
eslint-config-prettier@10.1.8(eslint@9.39.0(jiti@2.6.1)):
|
||||
eslint-config-prettier@10.1.8(eslint@9.39.1(jiti@2.6.1)):
|
||||
dependencies:
|
||||
eslint: 9.39.0(jiti@2.6.1)
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
|
||||
eslint-import-context@0.1.9(unrs-resolver@1.11.1):
|
||||
dependencies:
|
||||
@@ -6825,10 +6825,10 @@ snapshots:
|
||||
- supports-color
|
||||
optional: true
|
||||
|
||||
eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.0(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.0(jiti@2.6.1)):
|
||||
eslint-import-resolver-typescript@4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1)):
|
||||
dependencies:
|
||||
debug: 4.4.3
|
||||
eslint: 9.39.0(jiti@2.6.1)
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
eslint-import-context: 0.1.9(unrs-resolver@1.11.1)
|
||||
get-tsconfig: 4.10.1
|
||||
is-bun-module: 2.0.0
|
||||
@@ -6836,29 +6836,29 @@ snapshots:
|
||||
tinyglobby: 0.2.15
|
||||
unrs-resolver: 1.11.1
|
||||
optionalDependencies:
|
||||
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.0(jiti@2.6.1))
|
||||
eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.0(jiti@2.6.1))
|
||||
eslint-plugin-import: 2.32.0(@typescript-eslint/parser@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1))
|
||||
eslint-plugin-import-x: 4.16.1(@typescript-eslint/utils@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1))
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.0(jiti@2.6.1)):
|
||||
eslint-module-utils@2.12.1(@typescript-eslint/parser@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)):
|
||||
dependencies:
|
||||
debug: 3.2.7
|
||||
optionalDependencies:
|
||||
'@typescript-eslint/parser': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint: 9.39.0(jiti@2.6.1)
|
||||
'@typescript-eslint/parser': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.0(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.0(jiti@2.6.1))
|
||||
eslint-import-resolver-typescript: 4.4.4(eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)))(eslint-plugin-import@2.32.0)(eslint@9.39.1(jiti@2.6.1))
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
optional: true
|
||||
|
||||
eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.0(jiti@2.6.1)):
|
||||
eslint-plugin-import-x@4.16.1(@typescript-eslint/utils@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint@9.39.1(jiti@2.6.1)):
|
||||
dependencies:
|
||||
'@typescript-eslint/types': 8.46.2
|
||||
'@typescript-eslint/types': 8.46.3
|
||||
comment-parser: 1.4.1
|
||||
debug: 4.4.3
|
||||
eslint: 9.39.0(jiti@2.6.1)
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
eslint-import-context: 0.1.9(unrs-resolver@1.11.1)
|
||||
is-glob: 4.0.3
|
||||
minimatch: 10.0.3
|
||||
@@ -6866,12 +6866,12 @@ snapshots:
|
||||
stable-hash-x: 0.2.0
|
||||
unrs-resolver: 1.11.1
|
||||
optionalDependencies:
|
||||
'@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.0(jiti@2.6.1)):
|
||||
eslint-plugin-import@2.32.0(@typescript-eslint/parser@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1)):
|
||||
dependencies:
|
||||
'@rtsao/scc': 1.1.0
|
||||
array-includes: 3.1.9
|
||||
@@ -6880,9 +6880,9 @@ snapshots:
|
||||
array.prototype.flatmap: 1.3.3
|
||||
debug: 3.2.7
|
||||
doctrine: 2.1.0
|
||||
eslint: 9.39.0(jiti@2.6.1)
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
eslint-import-resolver-node: 0.3.9
|
||||
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.0(jiti@2.6.1))
|
||||
eslint-module-utils: 2.12.1(@typescript-eslint/parser@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@4.4.4)(eslint@9.39.1(jiti@2.6.1))
|
||||
hasown: 2.0.2
|
||||
is-core-module: 2.16.1
|
||||
is-glob: 4.0.3
|
||||
@@ -6894,122 +6894,122 @@ snapshots:
|
||||
string.prototype.trimend: 1.0.9
|
||||
tsconfig-paths: 3.15.0
|
||||
optionalDependencies:
|
||||
'@typescript-eslint/parser': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/parser': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
transitivePeerDependencies:
|
||||
- eslint-import-resolver-typescript
|
||||
- eslint-import-resolver-webpack
|
||||
- supports-color
|
||||
optional: true
|
||||
|
||||
eslint-plugin-prettier@5.5.4(eslint-config-prettier@10.1.8(eslint@9.39.0(jiti@2.6.1)))(eslint@9.39.0(jiti@2.6.1))(prettier@3.6.2):
|
||||
eslint-plugin-prettier@5.5.4(eslint-config-prettier@10.1.8(eslint@9.39.1(jiti@2.6.1)))(eslint@9.39.1(jiti@2.6.1))(prettier@3.6.2):
|
||||
dependencies:
|
||||
eslint: 9.39.0(jiti@2.6.1)
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
prettier: 3.6.2
|
||||
prettier-linter-helpers: 1.0.0
|
||||
synckit: 0.11.11
|
||||
optionalDependencies:
|
||||
eslint-config-prettier: 10.1.8(eslint@9.39.0(jiti@2.6.1))
|
||||
eslint-config-prettier: 10.1.8(eslint@9.39.1(jiti@2.6.1))
|
||||
|
||||
eslint-plugin-react-dom@2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3):
|
||||
eslint-plugin-react-dom@2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@eslint-react/ast': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/core': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/ast': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/core': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/eff': 2.3.1
|
||||
'@eslint-react/shared': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/var': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/scope-manager': 8.46.2
|
||||
'@typescript-eslint/types': 8.46.2
|
||||
'@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/shared': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/var': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/scope-manager': 8.46.3
|
||||
'@typescript-eslint/types': 8.46.3
|
||||
'@typescript-eslint/utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
compare-versions: 6.1.1
|
||||
eslint: 9.39.0(jiti@2.6.1)
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
string-ts: 2.2.1
|
||||
ts-pattern: 5.9.0
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-plugin-react-hooks-extra@2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3):
|
||||
eslint-plugin-react-hooks-extra@2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@eslint-react/ast': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/core': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/ast': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/core': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/eff': 2.3.1
|
||||
'@eslint-react/shared': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/var': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/scope-manager': 8.46.2
|
||||
'@typescript-eslint/type-utils': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.46.2
|
||||
'@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint: 9.39.0(jiti@2.6.1)
|
||||
'@eslint-react/shared': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/var': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/scope-manager': 8.46.3
|
||||
'@typescript-eslint/type-utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.46.3
|
||||
'@typescript-eslint/utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
string-ts: 2.2.1
|
||||
ts-pattern: 5.9.0
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-plugin-react-hooks@7.0.1(eslint@9.39.0(jiti@2.6.1)):
|
||||
eslint-plugin-react-hooks@7.0.1(eslint@9.39.1(jiti@2.6.1)):
|
||||
dependencies:
|
||||
'@babel/core': 7.28.4
|
||||
'@babel/parser': 7.28.4
|
||||
eslint: 9.39.0(jiti@2.6.1)
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
hermes-parser: 0.25.1
|
||||
zod: 4.1.12
|
||||
zod-validation-error: 4.0.2(zod@4.1.12)
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-plugin-react-naming-convention@2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3):
|
||||
eslint-plugin-react-naming-convention@2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@eslint-react/ast': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/core': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/ast': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/core': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/eff': 2.3.1
|
||||
'@eslint-react/shared': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/var': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/scope-manager': 8.46.2
|
||||
'@typescript-eslint/type-utils': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.46.2
|
||||
'@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint: 9.39.0(jiti@2.6.1)
|
||||
'@eslint-react/shared': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/var': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/scope-manager': 8.46.3
|
||||
'@typescript-eslint/type-utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.46.3
|
||||
'@typescript-eslint/utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
string-ts: 2.2.1
|
||||
ts-pattern: 5.9.0
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-plugin-react-refresh@0.4.24(eslint@9.39.0(jiti@2.6.1)):
|
||||
eslint-plugin-react-refresh@0.4.24(eslint@9.39.1(jiti@2.6.1)):
|
||||
dependencies:
|
||||
eslint: 9.39.0(jiti@2.6.1)
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
|
||||
eslint-plugin-react-web-api@2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3):
|
||||
eslint-plugin-react-web-api@2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@eslint-react/ast': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/core': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/ast': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/core': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/eff': 2.3.1
|
||||
'@eslint-react/shared': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/var': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/scope-manager': 8.46.2
|
||||
'@typescript-eslint/types': 8.46.2
|
||||
'@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint: 9.39.0(jiti@2.6.1)
|
||||
'@eslint-react/shared': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/var': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/scope-manager': 8.46.3
|
||||
'@typescript-eslint/types': 8.46.3
|
||||
'@typescript-eslint/utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
string-ts: 2.2.1
|
||||
ts-pattern: 5.9.0
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-plugin-react-x@2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3):
|
||||
eslint-plugin-react-x@2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@eslint-react/ast': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/core': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/ast': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/core': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/eff': 2.3.1
|
||||
'@eslint-react/shared': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/var': 2.3.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/scope-manager': 8.46.2
|
||||
'@typescript-eslint/type-utils': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.46.2
|
||||
'@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/shared': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@eslint-react/var': 2.3.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/scope-manager': 8.46.3
|
||||
'@typescript-eslint/type-utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/types': 8.46.3
|
||||
'@typescript-eslint/utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
compare-versions: 6.1.1
|
||||
eslint: 9.39.0(jiti@2.6.1)
|
||||
is-immutable-type: 5.0.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
is-immutable-type: 5.0.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
string-ts: 2.2.1
|
||||
ts-api-utils: 2.1.0(typescript@5.9.3)
|
||||
ts-pattern: 5.9.0
|
||||
@@ -7017,11 +7017,11 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
eslint-plugin-unused-imports@4.3.0(@typescript-eslint/eslint-plugin@8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.0(jiti@2.6.1)):
|
||||
eslint-plugin-unused-imports@4.3.0(@typescript-eslint/eslint-plugin@8.46.3(@typescript-eslint/parser@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1)):
|
||||
dependencies:
|
||||
eslint: 9.39.0(jiti@2.6.1)
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
optionalDependencies:
|
||||
'@typescript-eslint/eslint-plugin': 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/eslint-plugin': 8.46.3(@typescript-eslint/parser@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
|
||||
eslint-scope@8.4.0:
|
||||
dependencies:
|
||||
@@ -7032,15 +7032,15 @@ snapshots:
|
||||
|
||||
eslint-visitor-keys@4.2.1: {}
|
||||
|
||||
eslint@9.39.0(jiti@2.6.1):
|
||||
eslint@9.39.1(jiti@2.6.1):
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.8.0(eslint@9.39.0(jiti@2.6.1))
|
||||
'@eslint-community/eslint-utils': 4.8.0(eslint@9.39.1(jiti@2.6.1))
|
||||
'@eslint-community/regexpp': 4.12.1
|
||||
'@eslint/config-array': 0.21.1
|
||||
'@eslint/config-helpers': 0.4.2
|
||||
'@eslint/core': 0.17.0
|
||||
'@eslint/eslintrc': 3.3.1
|
||||
'@eslint/js': 9.39.0
|
||||
'@eslint/js': 9.39.1
|
||||
'@eslint/plugin-kit': 0.4.1
|
||||
'@humanfs/node': 0.16.6
|
||||
'@humanwhocodes/module-importer': 1.0.1
|
||||
@@ -7478,10 +7478,10 @@ snapshots:
|
||||
|
||||
is-hexadecimal@2.0.1: {}
|
||||
|
||||
is-immutable-type@5.0.1(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3):
|
||||
is-immutable-type@5.0.1(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@typescript-eslint/type-utils': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint: 9.39.0(jiti@2.6.1)
|
||||
'@typescript-eslint/type-utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
ts-api-utils: 2.1.0(typescript@5.9.3)
|
||||
ts-declaration-location: 1.0.7(typescript@5.9.3)
|
||||
typescript: 5.9.3
|
||||
@@ -8183,7 +8183,7 @@ snapshots:
|
||||
dependencies:
|
||||
react: 19.2.0
|
||||
|
||||
react-i18next@16.2.3(i18next@25.6.0(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3):
|
||||
react-i18next@16.2.4(i18next@25.6.0(typescript@5.9.3))(react-dom@19.2.0(react@19.2.0))(react@19.2.0)(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@babel/runtime': 7.28.4
|
||||
html-parse-stringify: 3.0.1
|
||||
@@ -8719,13 +8719,13 @@ snapshots:
|
||||
|
||||
types-pac@1.0.3: {}
|
||||
|
||||
typescript-eslint@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3):
|
||||
typescript-eslint@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3):
|
||||
dependencies:
|
||||
'@typescript-eslint/eslint-plugin': 8.46.2(@typescript-eslint/parser@8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/parser': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/typescript-estree': 8.46.2(typescript@5.9.3)
|
||||
'@typescript-eslint/utils': 8.46.2(eslint@9.39.0(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint: 9.39.0(jiti@2.6.1)
|
||||
'@typescript-eslint/eslint-plugin': 8.46.3(@typescript-eslint/parser@8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3))(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/parser': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
'@typescript-eslint/typescript-estree': 8.46.3(typescript@5.9.3)
|
||||
'@typescript-eslint/utils': 8.46.3(eslint@9.39.1(jiti@2.6.1))(typescript@5.9.3)
|
||||
eslint: 9.39.1(jiti@2.6.1)
|
||||
typescript: 5.9.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -7,13 +7,13 @@ use app_lib::config::IVerge;
|
||||
use app_lib::utils::Draft as DraftNew;
|
||||
|
||||
/// 创建测试数据
|
||||
fn make_draft() -> DraftNew<Box<IVerge>> {
|
||||
let verge = Box::new(IVerge {
|
||||
fn make_draft() -> DraftNew<IVerge> {
|
||||
let verge = IVerge {
|
||||
enable_auto_launch: Some(true),
|
||||
enable_tun_mode: Some(false),
|
||||
..Default::default()
|
||||
});
|
||||
DraftNew::from(verge)
|
||||
};
|
||||
DraftNew::new(verge)
|
||||
}
|
||||
|
||||
pub fn bench_draft(c: &mut Criterion) {
|
||||
@@ -30,18 +30,17 @@ pub fn bench_draft(c: &mut Criterion) {
|
||||
group.bench_function("data_mut", |b| {
|
||||
b.iter(|| {
|
||||
let draft = black_box(make_draft());
|
||||
let mut data = draft.data_mut();
|
||||
data.enable_tun_mode = Some(true);
|
||||
black_box(&data.enable_tun_mode);
|
||||
draft.edit_draft(|d| d.enable_tun_mode = Some(true));
|
||||
black_box(&draft.latest_arc().enable_tun_mode);
|
||||
});
|
||||
});
|
||||
|
||||
group.bench_function("draft_mut_first", |b| {
|
||||
b.iter(|| {
|
||||
let draft = black_box(make_draft());
|
||||
let mut d = draft.draft_mut();
|
||||
d.enable_auto_launch = Some(false);
|
||||
black_box(&d.enable_auto_launch);
|
||||
draft.edit_draft(|d| d.enable_auto_launch = Some(false));
|
||||
let latest = draft.latest_arc();
|
||||
black_box(&latest.enable_auto_launch);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -49,20 +48,24 @@ pub fn bench_draft(c: &mut Criterion) {
|
||||
b.iter(|| {
|
||||
let draft = black_box(make_draft());
|
||||
{
|
||||
let mut first = draft.draft_mut();
|
||||
first.enable_tun_mode = Some(true);
|
||||
black_box(&first.enable_tun_mode);
|
||||
draft.edit_draft(|d| {
|
||||
d.enable_tun_mode = Some(true);
|
||||
});
|
||||
let latest1 = draft.latest_arc();
|
||||
black_box(&latest1.enable_tun_mode);
|
||||
}
|
||||
let mut second = draft.draft_mut();
|
||||
second.enable_tun_mode = Some(false);
|
||||
black_box(&second.enable_tun_mode);
|
||||
draft.edit_draft(|d| {
|
||||
d.enable_tun_mode = Some(false);
|
||||
});
|
||||
let latest2 = draft.latest_arc();
|
||||
black_box(&latest2.enable_tun_mode);
|
||||
});
|
||||
});
|
||||
|
||||
group.bench_function("latest_ref", |b| {
|
||||
group.bench_function("latest_arc", |b| {
|
||||
b.iter(|| {
|
||||
let draft = black_box(make_draft());
|
||||
let latest = draft.latest_ref();
|
||||
let latest = draft.latest_arc();
|
||||
black_box(&latest.enable_auto_launch);
|
||||
});
|
||||
});
|
||||
@@ -71,8 +74,9 @@ pub fn bench_draft(c: &mut Criterion) {
|
||||
b.iter(|| {
|
||||
let draft = black_box(make_draft());
|
||||
{
|
||||
let mut d = draft.draft_mut();
|
||||
d.enable_auto_launch = Some(false);
|
||||
draft.edit_draft(|d| {
|
||||
d.enable_auto_launch = Some(false);
|
||||
});
|
||||
}
|
||||
draft.apply();
|
||||
black_box(&draft);
|
||||
@@ -83,8 +87,9 @@ pub fn bench_draft(c: &mut Criterion) {
|
||||
b.iter(|| {
|
||||
let draft = black_box(make_draft());
|
||||
{
|
||||
let mut d = draft.draft_mut();
|
||||
d.enable_auto_launch = Some(false);
|
||||
draft.edit_draft(|d| {
|
||||
d.enable_auto_launch = Some(false);
|
||||
});
|
||||
}
|
||||
draft.discard();
|
||||
black_box(&draft);
|
||||
@@ -95,7 +100,7 @@ pub fn bench_draft(c: &mut Criterion) {
|
||||
b.to_async(&rt).iter(|| async {
|
||||
let draft = black_box(make_draft());
|
||||
let _: Result<(), anyhow::Error> = draft
|
||||
.with_data_modify::<_, _, _, anyhow::Error>(|mut box_data| async move {
|
||||
.with_data_modify::<_, _, _>(|mut box_data| async move {
|
||||
box_data.enable_auto_launch =
|
||||
Some(!box_data.enable_auto_launch.unwrap_or(false));
|
||||
Ok((box_data, ()))
|
||||
|
||||
@@ -2,11 +2,11 @@ use super::CmdResult;
|
||||
use crate::utils::dirs;
|
||||
use crate::{
|
||||
cmd::StringifyErr,
|
||||
config::Config,
|
||||
config::{ClashInfo, Config},
|
||||
constants,
|
||||
core::{CoreManager, handle, validate::CoreConfigValidator},
|
||||
};
|
||||
use crate::{config::*, feat, logging, utils::logging::Type};
|
||||
use crate::{feat, logging, utils::logging::Type};
|
||||
use compact_str::CompactString;
|
||||
use serde_yaml_ng::Mapping;
|
||||
use smartstring::alias::String;
|
||||
@@ -22,7 +22,7 @@ pub async fn copy_clash_env() -> CmdResult {
|
||||
/// 获取Clash信息
|
||||
#[tauri::command]
|
||||
pub async fn get_clash_info() -> CmdResult<ClashInfo> {
|
||||
Ok(Config::clash().await.latest_ref().get_client_info())
|
||||
Ok(Config::clash().await.latest_arc().get_client_info())
|
||||
}
|
||||
|
||||
/// 修改Clash配置
|
||||
@@ -141,12 +141,6 @@ pub async fn save_dns_config(dns_config: Mapping) -> CmdResult {
|
||||
/// 应用或撤销DNS配置
|
||||
#[tauri::command]
|
||||
pub async fn apply_dns_config(apply: bool) -> CmdResult {
|
||||
use crate::{
|
||||
config::Config,
|
||||
core::{CoreManager, handle},
|
||||
utils::dirs,
|
||||
};
|
||||
|
||||
if apply {
|
||||
// 读取DNS配置文件
|
||||
let dns_path = dirs::app_home_dir()
|
||||
@@ -175,7 +169,9 @@ pub async fn apply_dns_config(apply: bool) -> CmdResult {
|
||||
patch.insert("dns".into(), patch_config.into());
|
||||
|
||||
// 应用DNS配置到运行时配置
|
||||
Config::runtime().await.draft_mut().patch_config(patch);
|
||||
Config::runtime().await.edit_draft(|d| {
|
||||
d.patch_config(patch);
|
||||
});
|
||||
|
||||
// 重新生成配置
|
||||
Config::generate().await.stringify_err_log(|err| {
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
use super::CmdResult;
|
||||
use super::StringifyErr;
|
||||
use crate::utils::draft::SharedBox;
|
||||
use crate::{
|
||||
config::{
|
||||
Config, IProfiles, PrfItem, PrfOption,
|
||||
@@ -23,11 +24,11 @@ use std::time::Duration;
|
||||
static CURRENT_SWITCHING_PROFILE: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_profiles() -> CmdResult<IProfiles> {
|
||||
pub async fn get_profiles() -> CmdResult<SharedBox<IProfiles>> {
|
||||
logging!(debug, Type::Cmd, "获取配置文件列表");
|
||||
let draft = Config::profiles().await;
|
||||
let latest = draft.latest_ref();
|
||||
Ok((**latest).clone())
|
||||
let data = draft.data_arc();
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
/// 增强配置文件
|
||||
@@ -99,9 +100,11 @@ pub async fn reorder_profile(active_id: String, over_id: String) -> CmdResult {
|
||||
match profiles_reorder_safe(&active_id, &over_id).await {
|
||||
Ok(_) => {
|
||||
logging!(info, Type::Cmd, "重新排序配置文件");
|
||||
Config::profiles().await.apply();
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => {
|
||||
Config::profiles().await.discard();
|
||||
logging!(error, Type::Cmd, "重新排序配置文件失败: {}", err);
|
||||
Err(format!("重新排序配置文件失败: {}", err).into())
|
||||
}
|
||||
@@ -119,12 +122,16 @@ pub async fn create_profile(item: PrfItem, file_data: Option<String>) -> CmdResu
|
||||
logging!(info, Type::Cmd, "[创建订阅] 发送配置变更通知: {}", uid);
|
||||
handle::Handle::notify_profile_changed(uid.clone());
|
||||
}
|
||||
Config::profiles().await.apply();
|
||||
Ok(())
|
||||
}
|
||||
Err(err) => match err.to_string().as_str() {
|
||||
"the file already exists" => Err("the file already exists".into()),
|
||||
_ => Err(format!("add profile error: {err}").into()),
|
||||
},
|
||||
Err(err) => {
|
||||
Config::profiles().await.discard();
|
||||
match err.to_string().as_str() {
|
||||
"the file already exists" => Err("the file already exists".into()),
|
||||
_ => Err(format!("add profile error: {err}").into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,8 +139,12 @@ pub async fn create_profile(item: PrfItem, file_data: Option<String>) -> CmdResu
|
||||
#[tauri::command]
|
||||
pub async fn update_profile(index: String, option: Option<PrfOption>) -> CmdResult {
|
||||
match feat::update_profile(&index, option.as_ref(), true, true).await {
|
||||
Ok(_) => Ok(()),
|
||||
Ok(_) => {
|
||||
let _: () = Config::profiles().await.apply();
|
||||
Ok(())
|
||||
}
|
||||
Err(e) => {
|
||||
Config::profiles().await.discard();
|
||||
logging!(error, Type::Cmd, "{}", e);
|
||||
Err(e.to_string().into())
|
||||
}
|
||||
@@ -143,12 +154,11 @@ pub async fn update_profile(index: String, option: Option<PrfOption>) -> CmdResu
|
||||
/// 删除配置文件
|
||||
#[tauri::command]
|
||||
pub async fn delete_profile(index: String) -> CmdResult {
|
||||
println!("delete_profile: {}", index);
|
||||
// 使用Send-safe helper函数
|
||||
let should_update = profiles_delete_item_safe(&index).await.stringify_err()?;
|
||||
profiles_save_file_safe().await.stringify_err()?;
|
||||
|
||||
if should_update {
|
||||
Config::profiles().await.apply();
|
||||
match CoreManager::global().update_config().await {
|
||||
Ok(_) => {
|
||||
handle::Handle::refresh_clash();
|
||||
@@ -172,7 +182,7 @@ async fn validate_new_profile(new_profile: &String) -> Result<(), ()> {
|
||||
// 获取目标配置文件路径
|
||||
let config_file_result = {
|
||||
let profiles_config = Config::profiles().await;
|
||||
let profiles_data = profiles_config.latest_ref();
|
||||
let profiles_data = profiles_config.latest_arc();
|
||||
match profiles_data.get_item(new_profile) {
|
||||
Ok(item) => {
|
||||
if let Some(file) = &item.file {
|
||||
@@ -274,16 +284,15 @@ async fn validate_new_profile(new_profile: &String) -> Result<(), ()> {
|
||||
}
|
||||
|
||||
/// 执行配置更新并处理结果
|
||||
async fn restore_previous_profile(prev_profile: String) -> CmdResult<()> {
|
||||
async fn restore_previous_profile(prev_profile: &String) -> CmdResult<()> {
|
||||
logging!(info, Type::Cmd, "尝试恢复到之前的配置: {}", prev_profile);
|
||||
let restore_profiles = IProfiles {
|
||||
current: Some(prev_profile),
|
||||
current: Some(prev_profile.to_owned()),
|
||||
items: None,
|
||||
};
|
||||
Config::profiles()
|
||||
.await
|
||||
.draft_mut()
|
||||
.patch_config(&restore_profiles)
|
||||
.edit_draft(|d| d.patch_config(&restore_profiles))
|
||||
.stringify_err()?;
|
||||
Config::profiles().await.apply();
|
||||
crate::process::AsyncHandler::spawn(|| async move {
|
||||
@@ -295,7 +304,7 @@ async fn restore_previous_profile(prev_profile: String) -> CmdResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn handle_success(current_value: Option<String>) -> CmdResult<bool> {
|
||||
async fn handle_success(current_value: Option<&String>) -> CmdResult<bool> {
|
||||
Config::profiles().await.apply();
|
||||
handle::Handle::refresh_clash();
|
||||
|
||||
@@ -311,9 +320,9 @@ async fn handle_success(current_value: Option<String>) -> CmdResult<bool> {
|
||||
logging!(warn, Type::Cmd, "Warning: 异步保存配置文件失败: {e}");
|
||||
}
|
||||
|
||||
if let Some(current) = ¤t_value {
|
||||
logging!(info, Type::Cmd, "向前端发送配置变更事件: {}", current,);
|
||||
handle::Handle::notify_profile_changed(current.clone());
|
||||
if let Some(current) = current_value {
|
||||
logging!(info, Type::Cmd, "向前端发送配置变更事件: {}", current);
|
||||
handle::Handle::notify_profile_changed(current.to_owned());
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
@@ -321,7 +330,7 @@ async fn handle_success(current_value: Option<String>) -> CmdResult<bool> {
|
||||
|
||||
async fn handle_validation_failure(
|
||||
error_msg: String,
|
||||
current_profile: Option<String>,
|
||||
current_profile: Option<&String>,
|
||||
) -> CmdResult<bool> {
|
||||
logging!(warn, Type::Cmd, "配置验证失败: {}", error_msg);
|
||||
Config::profiles().await.discard();
|
||||
@@ -339,7 +348,7 @@ async fn handle_update_error<E: std::fmt::Display>(e: E) -> CmdResult<bool> {
|
||||
Ok(false)
|
||||
}
|
||||
|
||||
async fn handle_timeout(current_profile: Option<String>) -> CmdResult<bool> {
|
||||
async fn handle_timeout(current_profile: Option<&String>) -> CmdResult<bool> {
|
||||
let timeout_msg = "配置更新超时(30秒),可能是配置验证或核心通信阻塞";
|
||||
logging!(error, Type::Cmd, "{}", timeout_msg);
|
||||
Config::profiles().await.discard();
|
||||
@@ -351,8 +360,8 @@ async fn handle_timeout(current_profile: Option<String>) -> CmdResult<bool> {
|
||||
}
|
||||
|
||||
async fn perform_config_update(
|
||||
current_value: Option<String>,
|
||||
current_profile: Option<String>,
|
||||
current_value: Option<&String>,
|
||||
current_profile: Option<&String>,
|
||||
) -> CmdResult<bool> {
|
||||
let update_result = tokio::time::timeout(
|
||||
Duration::from_secs(30),
|
||||
@@ -376,13 +385,14 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<bool> {
|
||||
.is_err()
|
||||
{
|
||||
logging!(info, Type::Cmd, "当前正在切换配置,放弃请求");
|
||||
return Err("switch_in_progress".into());
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
defer! {
|
||||
CURRENT_SWITCHING_PROFILE.store(false, Ordering::Release);
|
||||
}
|
||||
let target_profile = profiles.current.clone();
|
||||
|
||||
let target_profile = profiles.current.as_ref();
|
||||
|
||||
logging!(
|
||||
info,
|
||||
@@ -392,21 +402,21 @@ pub async fn patch_profiles_config(profiles: IProfiles) -> CmdResult<bool> {
|
||||
);
|
||||
|
||||
// 保存当前配置,以便在验证失败时恢复
|
||||
let current_profile = Config::profiles().await.latest_ref().current.clone();
|
||||
logging!(info, Type::Cmd, "当前配置: {:?}", current_profile);
|
||||
let previous_profile = Config::profiles().await.data_arc().current.clone();
|
||||
logging!(info, Type::Cmd, "当前配置: {:?}", previous_profile);
|
||||
|
||||
// 如果要切换配置,先检查目标配置文件是否有语法错误
|
||||
if let Some(new_profile) = profiles.current.as_ref()
|
||||
&& current_profile.as_ref() != Some(new_profile)
|
||||
&& validate_new_profile(new_profile).await.is_err()
|
||||
if let Some(switch_to_profile) = target_profile
|
||||
&& previous_profile.as_ref() != Some(switch_to_profile)
|
||||
&& validate_new_profile(switch_to_profile).await.is_err()
|
||||
{
|
||||
return Ok(false);
|
||||
}
|
||||
let _ = Config::profiles()
|
||||
.await
|
||||
.edit_draft(|d| d.patch_config(&profiles));
|
||||
|
||||
let _ = Config::profiles().await.draft_mut().patch_config(&profiles);
|
||||
let current_value = profiles.current.clone();
|
||||
|
||||
perform_config_update(current_value, current_profile).await
|
||||
perform_config_update(target_profile, previous_profile.as_ref()).await
|
||||
}
|
||||
|
||||
/// 根据profile name修改profiles
|
||||
@@ -426,7 +436,7 @@ pub async fn patch_profiles_config_by_profile_index(profile_index: String) -> Cm
|
||||
pub async fn patch_profile(index: String, profile: PrfItem) -> CmdResult {
|
||||
// 保存修改前检查是否有更新 update_interval
|
||||
let profiles = Config::profiles().await;
|
||||
let should_refresh_timer = if let Ok(old_profile) = profiles.latest_ref().get_item(&index)
|
||||
let should_refresh_timer = if let Ok(old_profile) = profiles.latest_arc().get_item(&index)
|
||||
&& let Some(new_option) = profile.option.as_ref()
|
||||
{
|
||||
let old_interval = old_profile.option.as_ref().and_then(|o| o.update_interval);
|
||||
@@ -465,7 +475,7 @@ pub async fn patch_profile(index: String, profile: PrfItem) -> CmdResult {
|
||||
#[tauri::command]
|
||||
pub async fn view_profile(index: String) -> CmdResult {
|
||||
let profiles = Config::profiles().await;
|
||||
let profiles_ref = profiles.latest_ref();
|
||||
let profiles_ref = profiles.latest_arc();
|
||||
let file = profiles_ref
|
||||
.get_item(&index)
|
||||
.stringify_err()?
|
||||
@@ -488,7 +498,7 @@ pub async fn view_profile(index: String) -> CmdResult {
|
||||
pub async fn read_profile_file(index: String) -> CmdResult<String> {
|
||||
let item = {
|
||||
let profiles = Config::profiles().await;
|
||||
let profiles_ref = profiles.latest_ref();
|
||||
let profiles_ref = profiles.latest_arc();
|
||||
PrfItem {
|
||||
file: profiles_ref.get_item(&index).stringify_err()?.file.clone(),
|
||||
..Default::default()
|
||||
|
||||
@@ -8,14 +8,14 @@ use std::collections::HashMap;
|
||||
/// 获取运行时配置
|
||||
#[tauri::command]
|
||||
pub async fn get_runtime_config() -> CmdResult<Option<Mapping>> {
|
||||
Ok(Config::runtime().await.latest_ref().config.clone())
|
||||
Ok(Config::runtime().await.latest_arc().config.clone())
|
||||
}
|
||||
|
||||
/// 获取运行时YAML配置
|
||||
#[tauri::command]
|
||||
pub async fn get_runtime_yaml() -> CmdResult<String> {
|
||||
let runtime = Config::runtime().await;
|
||||
let runtime = runtime.latest_ref();
|
||||
let runtime = runtime.latest_arc();
|
||||
|
||||
let config = runtime.config.as_ref();
|
||||
config
|
||||
@@ -31,19 +31,19 @@ pub async fn get_runtime_yaml() -> CmdResult<String> {
|
||||
/// 获取运行时存在的键
|
||||
#[tauri::command]
|
||||
pub async fn get_runtime_exists() -> CmdResult<Vec<String>> {
|
||||
Ok(Config::runtime().await.latest_ref().exists_keys.clone())
|
||||
Ok(Config::runtime().await.latest_arc().exists_keys.clone())
|
||||
}
|
||||
|
||||
/// 获取运行时日志
|
||||
#[tauri::command]
|
||||
pub async fn get_runtime_logs() -> CmdResult<HashMap<String, Vec<(String, String)>>> {
|
||||
Ok(Config::runtime().await.latest_ref().chain_logs.clone())
|
||||
Ok(Config::runtime().await.latest_arc().chain_logs.clone())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
pub async fn get_runtime_proxy_chain_config(proxy_chain_exit_node: String) -> CmdResult<String> {
|
||||
let runtime = Config::runtime().await;
|
||||
let runtime = runtime.latest_ref();
|
||||
let runtime = runtime.latest_arc();
|
||||
|
||||
let config = runtime
|
||||
.config
|
||||
@@ -98,9 +98,7 @@ pub async fn update_proxy_chain_config_in_runtime(
|
||||
) -> CmdResult<()> {
|
||||
{
|
||||
let runtime = Config::runtime().await;
|
||||
let mut draft = runtime.draft_mut();
|
||||
draft.update_proxy_chain_config(proxy_chain_config);
|
||||
drop(draft);
|
||||
runtime.edit_draft(|d| d.update_proxy_chain_config(proxy_chain_config));
|
||||
runtime.apply();
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ pub async fn save_profile_file(index: String, file_data: Option<String>) -> CmdR
|
||||
// 在异步操作前获取必要元数据并释放锁
|
||||
let (rel_path, is_merge_file) = {
|
||||
let profiles = Config::profiles().await;
|
||||
let profiles_guard = profiles.latest_ref();
|
||||
let profiles_guard = profiles.latest_arc();
|
||||
let item = profiles_guard.get_item(&index).stringify_err()?;
|
||||
let is_merge = item.itype.as_ref().is_some_and(|t| t == "merge");
|
||||
let path = item.file.clone().ok_or("file field is null")?;
|
||||
|
||||
@@ -1,16 +1,10 @@
|
||||
use super::CmdResult;
|
||||
use crate::{cmd::StringifyErr, config::*, feat};
|
||||
use crate::{cmd::StringifyErr, config::IVerge, feat, utils::draft::SharedBox};
|
||||
|
||||
/// 获取Verge配置
|
||||
#[tauri::command]
|
||||
pub async fn get_verge_config() -> CmdResult<IVergeResponse> {
|
||||
let verge = Config::verge().await;
|
||||
let verge_data = {
|
||||
let ref_data = verge.latest_ref();
|
||||
ref_data.clone()
|
||||
};
|
||||
let verge_response = IVergeResponse::from(verge_data);
|
||||
Ok(verge_response)
|
||||
pub async fn get_verge_config() -> CmdResult<SharedBox<IVerge>> {
|
||||
feat::fetch_verge_config().await.stringify_err()
|
||||
}
|
||||
|
||||
/// 修改Verge配置
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
use super::CmdResult;
|
||||
use crate::{cmd::StringifyErr, config::*, core, feat};
|
||||
use crate::{
|
||||
cmd::StringifyErr,
|
||||
config::{Config, IVerge},
|
||||
core, feat,
|
||||
};
|
||||
use reqwest_dav::list_cmd::ListFile;
|
||||
use smartstring::alias::String;
|
||||
|
||||
@@ -12,15 +16,11 @@ pub async fn save_webdav_config(url: String, username: String, password: String)
|
||||
webdav_password: Some(password),
|
||||
..IVerge::default()
|
||||
};
|
||||
Config::verge().await.draft_mut().patch_config(&patch);
|
||||
Config::verge().await.edit_draft(|e| e.patch_config(&patch));
|
||||
Config::verge().await.apply();
|
||||
|
||||
// 分离数据获取和异步调用
|
||||
let verge_data = Config::verge().await.latest_ref().clone();
|
||||
verge_data
|
||||
.save_file()
|
||||
.await
|
||||
.map_err(|err| err.to_string())?;
|
||||
let verge_data = Config::verge().await.latest_arc();
|
||||
verge_data.save_file().await.stringify_err()?;
|
||||
core::backup::WebDavClient::global().reset();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -300,7 +300,7 @@ impl IClashTemp {
|
||||
// 检查 enable_external_controller 设置,用于运行时配置生成
|
||||
let enable_external_controller = Config::verge()
|
||||
.await
|
||||
.latest_ref()
|
||||
.latest_arc()
|
||||
.enable_external_controller
|
||||
.unwrap_or(false);
|
||||
|
||||
|
||||
@@ -15,10 +15,10 @@ use tokio::sync::OnceCell;
|
||||
use tokio::time::sleep;
|
||||
|
||||
pub struct Config {
|
||||
clash_config: Draft<Box<IClashTemp>>,
|
||||
verge_config: Draft<Box<IVerge>>,
|
||||
profiles_config: Draft<Box<IProfiles>>,
|
||||
runtime_config: Draft<Box<IRuntime>>,
|
||||
clash_config: Draft<IClashTemp>,
|
||||
verge_config: Draft<IVerge>,
|
||||
profiles_config: Draft<IProfiles>,
|
||||
runtime_config: Draft<IRuntime>,
|
||||
}
|
||||
|
||||
impl Config {
|
||||
@@ -27,28 +27,28 @@ impl Config {
|
||||
CONFIG
|
||||
.get_or_init(|| async {
|
||||
Config {
|
||||
clash_config: Draft::from(Box::new(IClashTemp::new().await)),
|
||||
verge_config: Draft::from(Box::new(IVerge::new().await)),
|
||||
profiles_config: Draft::from(Box::new(IProfiles::new().await)),
|
||||
runtime_config: Draft::from(Box::new(IRuntime::new())),
|
||||
clash_config: Draft::new(IClashTemp::new().await),
|
||||
verge_config: Draft::new(IVerge::new().await),
|
||||
profiles_config: Draft::new(IProfiles::new().await),
|
||||
runtime_config: Draft::new(IRuntime::new()),
|
||||
}
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
pub async fn clash() -> Draft<Box<IClashTemp>> {
|
||||
pub async fn clash() -> Draft<IClashTemp> {
|
||||
Self::global().await.clash_config.clone()
|
||||
}
|
||||
|
||||
pub async fn verge() -> Draft<Box<IVerge>> {
|
||||
pub async fn verge() -> Draft<IVerge> {
|
||||
Self::global().await.verge_config.clone()
|
||||
}
|
||||
|
||||
pub async fn profiles() -> Draft<Box<IProfiles>> {
|
||||
pub async fn profiles() -> Draft<IProfiles> {
|
||||
Self::global().await.profiles_config.clone()
|
||||
}
|
||||
|
||||
pub async fn runtime() -> Draft<Box<IRuntime>> {
|
||||
pub async fn runtime() -> Draft<IRuntime> {
|
||||
Self::global().await.runtime_config.clone()
|
||||
}
|
||||
|
||||
@@ -61,12 +61,14 @@ impl Config {
|
||||
&& service::is_service_available().await.is_err()
|
||||
{
|
||||
let verge = Config::verge().await;
|
||||
verge.draft_mut().enable_tun_mode = Some(false);
|
||||
verge.edit_draft(|d| {
|
||||
d.enable_tun_mode = Some(false);
|
||||
});
|
||||
verge.apply();
|
||||
let _ = tray::Tray::global().update_tray_display().await;
|
||||
let _ = tray::Tray::global().update_menu().await;
|
||||
|
||||
// 分离数据获取和异步调用避免Send问题
|
||||
let verge_data = Config::verge().await.latest_ref().clone();
|
||||
let verge_data = Config::verge().await.latest_arc();
|
||||
logging_error!(Type::Core, verge_data.save_file().await);
|
||||
}
|
||||
|
||||
@@ -83,11 +85,11 @@ impl Config {
|
||||
// Ensure "Merge" and "Script" profile items exist, adding them if missing.
|
||||
async fn ensure_default_profile_items() -> Result<()> {
|
||||
let profiles = Self::profiles().await;
|
||||
if profiles.latest_ref().get_item("Merge").is_err() {
|
||||
if profiles.latest_arc().get_item("Merge").is_err() {
|
||||
let merge_item = &mut PrfItem::from_merge(Some("Merge".into()))?;
|
||||
profiles_append_item_safe(merge_item).await?;
|
||||
}
|
||||
if profiles.latest_ref().get_item("Script").is_err() {
|
||||
if profiles.latest_arc().get_item("Script").is_err() {
|
||||
let script_item = &mut PrfItem::from_script(Some("Script".into()))?;
|
||||
profiles_append_item_safe(script_item).await?;
|
||||
}
|
||||
@@ -154,7 +156,7 @@ impl Config {
|
||||
|
||||
let runtime = Config::runtime().await;
|
||||
let config = runtime
|
||||
.latest_ref()
|
||||
.latest_arc()
|
||||
.config
|
||||
.as_ref()
|
||||
.ok_or_else(|| anyhow!("failed to get runtime config"))?
|
||||
@@ -168,11 +170,13 @@ impl Config {
|
||||
pub async fn generate() -> Result<()> {
|
||||
let (config, exists_keys, logs) = enhance::enhance().await;
|
||||
|
||||
**Config::runtime().await.draft_mut() = IRuntime {
|
||||
config: Some(config),
|
||||
exists_keys,
|
||||
chain_logs: logs,
|
||||
};
|
||||
Config::runtime().await.edit_draft(|d| {
|
||||
*d = IRuntime {
|
||||
config: Some(config),
|
||||
exists_keys,
|
||||
chain_logs: logs,
|
||||
}
|
||||
});
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@@ -187,7 +191,7 @@ impl Config {
|
||||
};
|
||||
|
||||
let operation = || async {
|
||||
if Config::runtime().await.latest_ref().config.is_some() {
|
||||
if Config::runtime().await.latest_arc().config.is_some() {
|
||||
return Ok::<(), BackoffError<anyhow::Error>>(());
|
||||
}
|
||||
|
||||
@@ -228,7 +232,7 @@ mod tests {
|
||||
#[test]
|
||||
#[allow(unused_variables)]
|
||||
fn test_draft_size_non_boxed() {
|
||||
let draft = Draft::from(IRuntime::new());
|
||||
let draft = Draft::new(IRuntime::new());
|
||||
let iruntime_size = std::mem::size_of_val(&draft);
|
||||
assert_eq!(iruntime_size, std::mem::size_of::<Draft<IRuntime>>());
|
||||
}
|
||||
@@ -236,7 +240,7 @@ mod tests {
|
||||
#[test]
|
||||
#[allow(unused_variables)]
|
||||
fn test_draft_size_boxed() {
|
||||
let draft = Draft::from(Box::new(IRuntime::new()));
|
||||
let draft = Draft::new(Box::new(IRuntime::new()));
|
||||
let box_iruntime_size = std::mem::size_of_val(&draft);
|
||||
assert_eq!(
|
||||
box_iruntime_size,
|
||||
|
||||
@@ -584,6 +584,33 @@ impl PrfItem {
|
||||
}
|
||||
}
|
||||
|
||||
impl PrfItem {
|
||||
/// 获取current指向的订阅的merge
|
||||
pub fn current_merge(&self) -> Option<String> {
|
||||
self.option.as_ref().and_then(|o| o.merge.clone())
|
||||
}
|
||||
|
||||
/// 获取current指向的订阅的script
|
||||
pub fn current_script(&self) -> Option<String> {
|
||||
self.option.as_ref().and_then(|o| o.script.clone())
|
||||
}
|
||||
|
||||
/// 获取current指向的订阅的rules
|
||||
pub fn current_rules(&self) -> Option<String> {
|
||||
self.option.as_ref().and_then(|o| o.rules.clone())
|
||||
}
|
||||
|
||||
/// 获取current指向的订阅的proxies
|
||||
pub fn current_proxies(&self) -> Option<String> {
|
||||
self.option.as_ref().and_then(|o| o.proxies.clone())
|
||||
}
|
||||
|
||||
/// 获取current指向的订阅的groups
|
||||
pub fn current_groups(&self) -> Option<String> {
|
||||
self.option.as_ref().and_then(|o| o.groups.clone())
|
||||
}
|
||||
}
|
||||
|
||||
// 向前兼容,默认为订阅启用自动更新
|
||||
fn default_allow_auto_update() -> Option<bool> {
|
||||
Some(true)
|
||||
|
||||
@@ -8,7 +8,7 @@ use anyhow::{Context, Result, bail};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_yaml_ng::Mapping;
|
||||
use smartstring::alias::String;
|
||||
use std::collections::HashSet;
|
||||
use std::{collections::HashSet, sync::Arc};
|
||||
use tokio::fs;
|
||||
|
||||
/// Define the `profiles.yaml` schema
|
||||
@@ -32,7 +32,7 @@ pub struct CleanupResult {
|
||||
macro_rules! patch {
|
||||
($lv: expr, $rv: expr, $key: tt) => {
|
||||
if ($rv.$key).is_some() {
|
||||
$lv.$key = $rv.$key.clone();
|
||||
$lv.$key = $rv.$key.to_owned();
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -50,28 +50,26 @@ impl IProfiles {
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub async fn new() -> Self {
|
||||
match dirs::profiles_path() {
|
||||
Ok(path) => match help::read_yaml::<Self>(&path).await {
|
||||
Ok(mut profiles) => {
|
||||
if profiles.items.is_none() {
|
||||
profiles.items = Some(vec![]);
|
||||
let path = match dirs::profiles_path() {
|
||||
Ok(p) => p,
|
||||
Err(err) => {
|
||||
logging!(error, Type::Config, "{err}");
|
||||
return Self::default();
|
||||
}
|
||||
};
|
||||
|
||||
match help::read_yaml::<Self>(&path).await {
|
||||
Ok(mut profiles) => {
|
||||
let items = profiles.items.get_or_insert_with(Vec::new);
|
||||
for item in items.iter_mut() {
|
||||
if item.uid.is_none() {
|
||||
item.uid = Some(help::get_uid("d").into());
|
||||
}
|
||||
// compatible with the old old old version
|
||||
if let Some(items) = profiles.items.as_mut() {
|
||||
for item in items.iter_mut() {
|
||||
if item.uid.is_none() {
|
||||
item.uid = Some(help::get_uid("d").into());
|
||||
}
|
||||
}
|
||||
}
|
||||
profiles
|
||||
}
|
||||
Err(err) => {
|
||||
logging!(error, Type::Config, "{err}");
|
||||
Self::default()
|
||||
}
|
||||
},
|
||||
profiles
|
||||
}
|
||||
Err(err) => {
|
||||
logging!(error, Type::Config, "{err}");
|
||||
Self::default()
|
||||
@@ -106,8 +104,8 @@ impl IProfiles {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub fn get_current(&self) -> Option<String> {
|
||||
self.current.clone()
|
||||
pub fn get_current(&self) -> Option<&String> {
|
||||
self.current.as_ref()
|
||||
}
|
||||
|
||||
/// get items ref
|
||||
@@ -132,6 +130,15 @@ impl IProfiles {
|
||||
bail!("failed to get the profile item \"uid:{}\"", uid_str);
|
||||
}
|
||||
|
||||
pub fn get_item_arc(&self, uid: &str) -> Option<Arc<PrfItem>> {
|
||||
self.items.as_ref().and_then(|items| {
|
||||
items
|
||||
.iter()
|
||||
.find(|it| it.uid.as_deref() == Some(uid))
|
||||
.map(|it| Arc::new(it.clone()))
|
||||
})
|
||||
}
|
||||
|
||||
/// append new item
|
||||
/// if the file_data is some
|
||||
/// then should save the data to file
|
||||
@@ -357,88 +364,18 @@ impl IProfiles {
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取current指向的订阅的merge
|
||||
pub fn current_merge(&self) -> Option<String> {
|
||||
match (self.current.as_ref(), self.items.as_ref()) {
|
||||
(Some(current), Some(items)) => {
|
||||
if let Some(item) = items.iter().find(|e| e.uid.as_ref() == Some(current)) {
|
||||
let merge = item.option.as_ref().and_then(|e| e.merge.clone());
|
||||
return merge;
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取current指向的订阅的script
|
||||
pub fn current_script(&self) -> Option<String> {
|
||||
match (self.current.as_ref(), self.items.as_ref()) {
|
||||
(Some(current), Some(items)) => {
|
||||
if let Some(item) = items.iter().find(|e| e.uid.as_ref() == Some(current)) {
|
||||
let script = item.option.as_ref().and_then(|e| e.script.clone());
|
||||
return script;
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取current指向的订阅的rules
|
||||
pub fn current_rules(&self) -> Option<String> {
|
||||
match (self.current.as_ref(), self.items.as_ref()) {
|
||||
(Some(current), Some(items)) => {
|
||||
if let Some(item) = items.iter().find(|e| e.uid.as_ref() == Some(current)) {
|
||||
let rules = item.option.as_ref().and_then(|e| e.rules.clone());
|
||||
return rules;
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取current指向的订阅的proxies
|
||||
pub fn current_proxies(&self) -> Option<String> {
|
||||
match (self.current.as_ref(), self.items.as_ref()) {
|
||||
(Some(current), Some(items)) => {
|
||||
if let Some(item) = items.iter().find(|e| e.uid.as_ref() == Some(current)) {
|
||||
let proxies = item.option.as_ref().and_then(|e| e.proxies.clone());
|
||||
return proxies;
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 获取current指向的订阅的groups
|
||||
pub fn current_groups(&self) -> Option<String> {
|
||||
match (self.current.as_ref(), self.items.as_ref()) {
|
||||
(Some(current), Some(items)) => {
|
||||
if let Some(item) = items.iter().find(|e| e.uid.as_ref() == Some(current)) {
|
||||
let groups = item.option.as_ref().and_then(|e| e.groups.clone());
|
||||
return groups;
|
||||
}
|
||||
None
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// 判断profile是否是current指向的
|
||||
pub fn is_current_profile_index(&self, index: &String) -> bool {
|
||||
self.current.as_ref() == Some(index)
|
||||
}
|
||||
|
||||
/// 获取所有的profiles(uid,名称)
|
||||
pub fn all_profile_uid_and_name(&self) -> Option<Vec<(String, String)>> {
|
||||
pub fn all_profile_uid_and_name(&self) -> Option<Vec<(&String, &String)>> {
|
||||
self.items.as_ref().map(|items| {
|
||||
items
|
||||
.iter()
|
||||
.filter_map(|e| {
|
||||
if let (Some(uid), Some(name)) = (e.uid.clone(), e.name.clone()) {
|
||||
if let (Some(uid), Some(name)) = (e.uid.as_ref(), e.name.as_ref()) {
|
||||
Some((uid, name))
|
||||
} else {
|
||||
None
|
||||
@@ -449,11 +386,11 @@ impl IProfiles {
|
||||
}
|
||||
|
||||
/// 通过 uid 获取名称
|
||||
pub fn get_name_by_uid(&self, uid: &String) -> Option<String> {
|
||||
pub fn get_name_by_uid(&self, uid: &String) -> Option<&String> {
|
||||
if let Some(items) = &self.items {
|
||||
for item in items {
|
||||
if item.uid.as_ref() == Some(uid) {
|
||||
return item.name.clone();
|
||||
return item.name.as_ref();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -551,14 +488,14 @@ impl IProfiles {
|
||||
}
|
||||
|
||||
/// 获取所有 active profile 关联的文件名
|
||||
fn get_all_active_files(&self) -> HashSet<String> {
|
||||
let mut active_files = HashSet::new();
|
||||
fn get_all_active_files(&self) -> HashSet<&str> {
|
||||
let mut active_files: HashSet<&str> = HashSet::new();
|
||||
|
||||
if let Some(items) = &self.items {
|
||||
for item in items {
|
||||
// 收集所有类型 profile 的文件
|
||||
if let Some(file) = &item.file {
|
||||
active_files.insert(file.clone());
|
||||
active_files.insert(file);
|
||||
}
|
||||
|
||||
// 对于主 profile 类型(remote/local),还需要收集其关联的扩展文件
|
||||
@@ -571,35 +508,35 @@ impl IProfiles {
|
||||
&& let Ok(merge_item) = self.get_item(merge_uid)
|
||||
&& let Some(file) = &merge_item.file
|
||||
{
|
||||
active_files.insert(file.clone());
|
||||
active_files.insert(file);
|
||||
}
|
||||
|
||||
if let Some(script_uid) = &option.script
|
||||
&& let Ok(script_item) = self.get_item(script_uid)
|
||||
&& let Some(file) = &script_item.file
|
||||
{
|
||||
active_files.insert(file.clone());
|
||||
active_files.insert(file);
|
||||
}
|
||||
|
||||
if let Some(rules_uid) = &option.rules
|
||||
&& let Ok(rules_item) = self.get_item(rules_uid)
|
||||
&& let Some(file) = &rules_item.file
|
||||
{
|
||||
active_files.insert(file.clone());
|
||||
active_files.insert(file);
|
||||
}
|
||||
|
||||
if let Some(proxies_uid) = &option.proxies
|
||||
&& let Ok(proxies_item) = self.get_item(proxies_uid)
|
||||
&& let Some(file) = &proxies_item.file
|
||||
{
|
||||
active_files.insert(file.clone());
|
||||
active_files.insert(file);
|
||||
}
|
||||
|
||||
if let Some(groups_uid) = &option.groups
|
||||
&& let Ok(groups_item) = self.get_item(groups_uid)
|
||||
&& let Some(file) = &groups_item.file
|
||||
{
|
||||
active_files.insert(file.clone());
|
||||
active_files.insert(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -46,19 +46,24 @@ pub struct IVerge {
|
||||
pub enable_memory_usage: Option<bool>,
|
||||
|
||||
/// enable group icon
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub enable_group_icon: Option<bool>,
|
||||
|
||||
/// common tray icon
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub common_tray_icon: Option<bool>,
|
||||
|
||||
/// tray icon
|
||||
#[cfg(target_os = "macos")]
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub tray_icon: Option<String>,
|
||||
|
||||
/// menu icon
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub menu_icon: Option<String>,
|
||||
|
||||
/// menu order
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub menu_order: Option<Vec<String>>,
|
||||
|
||||
/// sysproxy tray icon
|
||||
@@ -115,6 +120,7 @@ pub struct IVerge {
|
||||
|
||||
/// hotkey map
|
||||
/// format: {func},{key}
|
||||
#[serde(skip_serializing_if = "Option::is_none")]
|
||||
pub hotkeys: Option<Vec<String>>,
|
||||
|
||||
/// enable global hotkey
|
||||
@@ -134,7 +140,7 @@ pub struct IVerge {
|
||||
pub default_latency_test: Option<String>,
|
||||
|
||||
/// 默认的延迟测试超时时间
|
||||
pub default_latency_timeout: Option<i32>,
|
||||
pub default_latency_timeout: Option<i16>,
|
||||
|
||||
/// 是否自动检测当前节点延迟
|
||||
pub enable_auto_delay_detection: Option<bool>,
|
||||
@@ -143,7 +149,7 @@ pub struct IVerge {
|
||||
pub enable_builtin_enhanced: Option<bool>,
|
||||
|
||||
/// proxy 页面布局 列数
|
||||
pub proxy_layout_column: Option<i32>,
|
||||
pub proxy_layout_column: Option<u8>,
|
||||
|
||||
/// 测试站列表
|
||||
pub test_list: Option<Vec<IVergeTestItem>>,
|
||||
@@ -202,6 +208,7 @@ pub struct IVerge {
|
||||
)]
|
||||
pub webdav_password: Option<String>,
|
||||
|
||||
#[serde(skip)]
|
||||
pub enable_tray_speed: Option<bool>,
|
||||
|
||||
// pub enable_tray_icon: Option<bool>,
|
||||
@@ -313,7 +320,9 @@ impl IVerge {
|
||||
);
|
||||
|
||||
let config_draft = Config::verge().await;
|
||||
**config_draft.draft_mut() = updated_config;
|
||||
config_draft.edit_draft(|d| {
|
||||
*d = updated_config;
|
||||
});
|
||||
config_draft.apply();
|
||||
|
||||
Ok(())
|
||||
@@ -584,10 +593,10 @@ pub struct IVergeResponse {
|
||||
pub auto_close_connection: Option<bool>,
|
||||
pub auto_check_update: Option<bool>,
|
||||
pub default_latency_test: Option<String>,
|
||||
pub default_latency_timeout: Option<i32>,
|
||||
pub default_latency_timeout: Option<i16>,
|
||||
pub enable_auto_delay_detection: Option<bool>,
|
||||
pub enable_builtin_enhanced: Option<bool>,
|
||||
pub proxy_layout_column: Option<i32>,
|
||||
pub proxy_layout_column: Option<u8>,
|
||||
pub test_list: Option<Vec<IVergeTestItem>>,
|
||||
pub auto_log_clean: Option<i32>,
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
@@ -696,9 +705,3 @@ impl From<IVerge> for IVergeResponse {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Box<IVerge>> for IVergeResponse {
|
||||
fn from(verge: Box<IVerge>) -> Self {
|
||||
IVergeResponse::from(*verge)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ impl WebDavClient {
|
||||
(*cfg_arc).clone()
|
||||
} else {
|
||||
// 释放锁后获取异步配置
|
||||
let verge = Config::verge().await.latest_ref().clone();
|
||||
let verge = Config::verge().await.latest_arc();
|
||||
if verge.webdav_url.is_none()
|
||||
|| verge.webdav_username.is_none()
|
||||
|| verge.webdav_password.is_none()
|
||||
@@ -99,11 +99,13 @@ impl WebDavClient {
|
||||
let config = WebDavConfig {
|
||||
url: verge
|
||||
.webdav_url
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.unwrap_or_default()
|
||||
.trim_end_matches('/')
|
||||
.into(),
|
||||
username: verge.webdav_username.unwrap_or_default(),
|
||||
password: verge.webdav_password.unwrap_or_default(),
|
||||
username: verge.webdav_username.as_ref().cloned().unwrap_or_default(),
|
||||
password: verge.webdav_password.as_ref().cloned().unwrap_or_default(),
|
||||
};
|
||||
|
||||
// 存储配置到 ArcSwapOption
|
||||
|
||||
@@ -387,7 +387,7 @@ impl EventDrivenProxyManager {
|
||||
async fn get_proxy_config() -> ProxyConfig {
|
||||
let (sys_enabled, pac_enabled, guard_enabled, guard_duration) = {
|
||||
let verge_config = Config::verge().await;
|
||||
let verge = verge_config.latest_ref();
|
||||
let verge = verge_config.latest_arc();
|
||||
(
|
||||
verge.enable_system_proxy.unwrap_or(false),
|
||||
verge.proxy_auto_config.unwrap_or(false),
|
||||
@@ -406,7 +406,7 @@ impl EventDrivenProxyManager {
|
||||
async fn get_expected_pac_config() -> Autoproxy {
|
||||
let proxy_host = {
|
||||
let verge_config = Config::verge().await;
|
||||
let verge = verge_config.latest_ref();
|
||||
let verge = verge_config.latest_arc();
|
||||
verge
|
||||
.proxy_host
|
||||
.clone()
|
||||
@@ -424,13 +424,13 @@ impl EventDrivenProxyManager {
|
||||
|
||||
let (verge_mixed_port, proxy_host) = {
|
||||
let verge_config = Config::verge().await;
|
||||
let verge_ref = verge_config.latest_ref();
|
||||
let verge_ref = verge_config.latest_arc();
|
||||
(verge_ref.verge_mixed_port, verge_ref.proxy_host.clone())
|
||||
};
|
||||
|
||||
let default_port = {
|
||||
let clash_config = Config::clash().await;
|
||||
clash_config.latest_ref().get_mixed_port()
|
||||
clash_config.latest_arc().get_mixed_port()
|
||||
};
|
||||
|
||||
let port = verge_mixed_port.unwrap_or(default_port);
|
||||
@@ -450,7 +450,7 @@ impl EventDrivenProxyManager {
|
||||
use crate::constants::bypass;
|
||||
|
||||
let verge_config = Config::verge().await;
|
||||
let verge = verge_config.latest_ref();
|
||||
let verge = verge_config.latest_arc();
|
||||
let use_default = verge.use_default_bypass.unwrap_or(true);
|
||||
let custom = verge.system_proxy_bypass.as_deref().unwrap_or("");
|
||||
|
||||
|
||||
@@ -102,6 +102,7 @@ impl Handle {
|
||||
Self::send_event(FrontendEvent::ProfileUpdateCompleted { uid });
|
||||
}
|
||||
|
||||
// TODO 利用 &str 等缩短 Clone
|
||||
pub fn notice_message<S: Into<String>, M: Into<String>>(status: S, msg: M) {
|
||||
let handle = Self::global();
|
||||
let status_str = status.into();
|
||||
|
||||
@@ -237,7 +237,7 @@ impl Hotkey {
|
||||
|
||||
let is_enable_global_hotkey = Config::verge()
|
||||
.await
|
||||
.latest_ref()
|
||||
.latest_arc()
|
||||
.enable_global_hotkey
|
||||
.unwrap_or(true);
|
||||
|
||||
@@ -274,7 +274,7 @@ singleton_with_logging!(Hotkey, INSTANCE, "Hotkey");
|
||||
impl Hotkey {
|
||||
pub async fn init(&self, skip: bool) -> Result<()> {
|
||||
let verge = Config::verge().await;
|
||||
let enable_global_hotkey = !skip && verge.latest_ref().enable_global_hotkey.unwrap_or(true);
|
||||
let enable_global_hotkey = !skip && verge.latest_arc().enable_global_hotkey.unwrap_or(true);
|
||||
|
||||
logging!(
|
||||
debug,
|
||||
@@ -284,7 +284,7 @@ impl Hotkey {
|
||||
);
|
||||
|
||||
// Extract hotkeys data before async operations
|
||||
let hotkeys = verge.latest_ref().hotkeys.as_ref().cloned();
|
||||
let hotkeys = verge.latest_arc().hotkeys.as_ref().cloned();
|
||||
|
||||
if let Some(hotkeys) = hotkeys {
|
||||
logging!(
|
||||
|
||||
@@ -17,13 +17,15 @@ impl CoreManager {
|
||||
use crate::constants::files::RUNTIME_CONFIG;
|
||||
|
||||
let runtime_path = dirs::app_home_dir()?.join(RUNTIME_CONFIG);
|
||||
let clash_config = Config::clash().await.latest_ref().0.clone();
|
||||
let clash_config = &Config::clash().await.latest_arc().0;
|
||||
|
||||
**Config::runtime().await.draft_mut() = IRuntime {
|
||||
config: Some(clash_config.clone()),
|
||||
exists_keys: vec![],
|
||||
chain_logs: Default::default(),
|
||||
};
|
||||
Config::runtime().await.edit_draft(|d| {
|
||||
*d = IRuntime {
|
||||
config: Some(clash_config.to_owned()),
|
||||
exists_keys: vec![],
|
||||
chain_logs: Default::default(),
|
||||
}
|
||||
});
|
||||
|
||||
help::save_yaml(&runtime_path, &clash_config, Some("# Clash Verge Runtime")).await?;
|
||||
handle::Handle::notice_message(error_key, error_msg);
|
||||
|
||||
@@ -47,10 +47,12 @@ impl CoreManager {
|
||||
return Err(format!("Invalid clash core: {}", clash_core).into());
|
||||
}
|
||||
|
||||
Config::verge().await.draft_mut().clash_core = clash_core.to_owned().into();
|
||||
Config::verge().await.edit_draft(|d| {
|
||||
d.clash_core = Some(clash_core.to_owned());
|
||||
});
|
||||
Config::verge().await.apply();
|
||||
|
||||
let verge_data = Config::verge().await.latest_ref().clone();
|
||||
let verge_data = Config::verge().await.latest_arc();
|
||||
verge_data.save_file().await.map_err(|e| e.to_string())?;
|
||||
|
||||
let run_path = Config::generate_file(ConfigType::Run)
|
||||
@@ -82,7 +84,7 @@ impl CoreManager {
|
||||
|
||||
let needs_service = Config::verge()
|
||||
.await
|
||||
.latest_ref()
|
||||
.latest_arc()
|
||||
.enable_tun_mode
|
||||
.unwrap_or(false);
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ impl CoreManager {
|
||||
|
||||
let config_file = Config::generate_file(crate::config::ConfigType::Run).await?;
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let clash_core = Config::verge().await.latest_ref().get_valid_clash_core();
|
||||
let clash_core = Config::verge().await.latest_arc().get_valid_clash_core();
|
||||
let config_dir = dirs::app_home_dir()?;
|
||||
|
||||
let (mut rx, child) = app_handle
|
||||
|
||||
@@ -353,7 +353,7 @@ pub(super) async fn start_with_existing_service(config_file: &PathBuf) -> Result
|
||||
logging!(info, Type::Service, "尝试使用现有服务启动核心");
|
||||
|
||||
let verge_config = Config::verge().await;
|
||||
let clash_core = verge_config.latest_ref().get_valid_clash_core();
|
||||
let clash_core = verge_config.latest_arc().get_valid_clash_core();
|
||||
drop(verge_config);
|
||||
|
||||
let bin_ext = if cfg!(windows) { ".exe" } else { "" };
|
||||
@@ -532,7 +532,7 @@ impl ServiceManager {
|
||||
return Err(anyhow::anyhow!("服务不可用: {}", reason));
|
||||
}
|
||||
}
|
||||
let _ = tray::Tray::global().update_tray_display().await;
|
||||
let _ = tray::Tray::global().update_menu().await;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,12 +31,12 @@ static DEFAULT_BYPASS: &str = "127.0.0.1,192.168.0.0/16,10.0.0.0/8,172.16.0.0/12
|
||||
async fn get_bypass() -> String {
|
||||
let use_default = Config::verge()
|
||||
.await
|
||||
.latest_ref()
|
||||
.latest_arc()
|
||||
.use_default_bypass
|
||||
.unwrap_or(true);
|
||||
let res = {
|
||||
let verge = Config::verge().await;
|
||||
let verge = verge.latest_ref();
|
||||
let verge = verge.latest_arc();
|
||||
verge.system_proxy_bypass.clone()
|
||||
};
|
||||
let custom_bypass = match res {
|
||||
@@ -124,17 +124,17 @@ impl Sysopt {
|
||||
}
|
||||
|
||||
let port = {
|
||||
let verge_port = Config::verge().await.latest_ref().verge_mixed_port;
|
||||
let verge_port = Config::verge().await.latest_arc().verge_mixed_port;
|
||||
match verge_port {
|
||||
Some(port) => port,
|
||||
None => Config::clash().await.latest_ref().get_mixed_port(),
|
||||
None => Config::clash().await.latest_arc().get_mixed_port(),
|
||||
}
|
||||
};
|
||||
let pac_port = IVerge::get_singleton_port();
|
||||
|
||||
let (sys_enable, pac_enable, proxy_host) = {
|
||||
let verge = Config::verge().await;
|
||||
let verge = verge.latest_ref();
|
||||
let verge = verge.latest_arc();
|
||||
(
|
||||
verge.enable_system_proxy.unwrap_or(false),
|
||||
verge.proxy_auto_config.unwrap_or(false),
|
||||
@@ -266,7 +266,7 @@ impl Sysopt {
|
||||
|
||||
/// update the startup
|
||||
pub async fn update_launch(&self) -> Result<()> {
|
||||
let enable_auto_launch = { Config::verge().await.latest_ref().enable_auto_launch };
|
||||
let enable_auto_launch = { Config::verge().await.latest_arc().enable_auto_launch };
|
||||
let is_enable = enable_auto_launch.unwrap_or(false);
|
||||
logging!(
|
||||
info,
|
||||
|
||||
@@ -100,7 +100,7 @@ impl Timer {
|
||||
|
||||
// Collect profiles that need immediate update
|
||||
let profiles_to_update =
|
||||
if let Some(items) = Config::profiles().await.latest_ref().get_items() {
|
||||
if let Some(items) = Config::profiles().await.latest_arc().get_items() {
|
||||
items
|
||||
.iter()
|
||||
.filter_map(|item| {
|
||||
@@ -161,9 +161,7 @@ impl Timer {
|
||||
.set_frequency_count_down_by_seconds(3, 3)
|
||||
.spawn_async_routine(|| async move {
|
||||
logging!(debug, Type::Timer, "Updating tray menu");
|
||||
crate::core::tray::Tray::global()
|
||||
.update_tray_display()
|
||||
.await
|
||||
crate::core::tray::Tray::global().update_menu().await
|
||||
})
|
||||
.context("failed to create update tray menu timer task")?;
|
||||
delay_timer
|
||||
@@ -273,7 +271,7 @@ impl Timer {
|
||||
async fn gen_map(&self) -> HashMap<String, u64> {
|
||||
let mut new_map = HashMap::new();
|
||||
|
||||
if let Some(items) = Config::profiles().await.latest_ref().get_items() {
|
||||
if let Some(items) = Config::profiles().await.latest_arc().get_items() {
|
||||
for item in items.iter() {
|
||||
if let Some(option) = item.option.as_ref()
|
||||
&& let Some(allow_auto_update) = option.allow_auto_update
|
||||
@@ -427,7 +425,7 @@ impl Timer {
|
||||
// Get the profile updated timestamp - now safe to await
|
||||
let items = {
|
||||
let profiles = Config::profiles().await;
|
||||
let profiles_guard = profiles.latest_ref();
|
||||
let profiles_guard = profiles.latest_arc();
|
||||
match profiles_guard.get_items() {
|
||||
Some(i) => i.clone(),
|
||||
None => {
|
||||
@@ -489,7 +487,7 @@ impl Timer {
|
||||
match tokio::time::timeout(std::time::Duration::from_secs(40), async {
|
||||
Self::emit_update_event(uid, true);
|
||||
|
||||
let is_current = Config::profiles().await.latest_ref().current.as_ref() == Some(uid);
|
||||
let is_current = Config::profiles().await.latest_arc().current.as_ref() == Some(uid);
|
||||
logging!(
|
||||
info,
|
||||
Type::Timer,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use once_cell::sync::OnceCell;
|
||||
use tauri::Emitter;
|
||||
use tauri::tray::TrayIconBuilder;
|
||||
use tauri_plugin_mihomo::models::Proxies;
|
||||
use tokio::fs;
|
||||
@@ -86,7 +85,7 @@ pub struct Tray {
|
||||
|
||||
impl TrayState {
|
||||
pub async fn get_common_tray_icon() -> (bool, Vec<u8>) {
|
||||
let verge = Config::verge().await.latest_ref().clone();
|
||||
let verge = Config::verge().await.latest_arc();
|
||||
let is_common_tray_icon = verge.common_tray_icon.unwrap_or(false);
|
||||
if is_common_tray_icon
|
||||
&& let Ok(Some(common_icon_path)) = find_target_icons("common")
|
||||
@@ -123,7 +122,7 @@ impl TrayState {
|
||||
}
|
||||
|
||||
pub async fn get_sysproxy_tray_icon() -> (bool, Vec<u8>) {
|
||||
let verge = Config::verge().await.latest_ref().clone();
|
||||
let verge = Config::verge().await.latest_arc();
|
||||
let is_sysproxy_tray_icon = verge.sysproxy_tray_icon.unwrap_or(false);
|
||||
if is_sysproxy_tray_icon
|
||||
&& let Ok(Some(sysproxy_icon_path)) = find_target_icons("sysproxy")
|
||||
@@ -160,7 +159,7 @@ impl TrayState {
|
||||
}
|
||||
|
||||
pub async fn get_tun_tray_icon() -> (bool, Vec<u8>) {
|
||||
let verge = Config::verge().await.latest_ref().clone();
|
||||
let verge = Config::verge().await.latest_arc();
|
||||
let is_tun_tray_icon = verge.tun_tray_icon.unwrap_or(false);
|
||||
if is_tun_tray_icon
|
||||
&& let Ok(Some(tun_icon_path)) = find_target_icons("tun")
|
||||
@@ -243,7 +242,7 @@ impl Tray {
|
||||
}
|
||||
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let tray_event = { Config::verge().await.latest_ref().tray_event.clone() };
|
||||
let tray_event = { Config::verge().await.latest_arc().tray_event.clone() };
|
||||
let tray_event = tray_event.unwrap_or_else(|| "main_window".into());
|
||||
let tray = app_handle
|
||||
.tray_by_id("main")
|
||||
@@ -303,7 +302,7 @@ impl Tray {
|
||||
}
|
||||
|
||||
async fn update_menu_internal(&self, app_handle: &AppHandle) -> Result<()> {
|
||||
let verge = Config::verge().await.latest_ref().clone();
|
||||
let verge = Config::verge().await.latest_arc();
|
||||
let system_proxy = verge.enable_system_proxy.as_ref().unwrap_or(&false);
|
||||
let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false);
|
||||
let tun_mode_available = cmd::system::is_admin().unwrap_or_default()
|
||||
@@ -311,18 +310,16 @@ impl Tray {
|
||||
let mode = {
|
||||
Config::clash()
|
||||
.await
|
||||
.latest_ref()
|
||||
.latest_arc()
|
||||
.0
|
||||
.get("mode")
|
||||
.map(|val| val.as_str().unwrap_or("rule"))
|
||||
.unwrap_or("rule")
|
||||
.to_owned()
|
||||
};
|
||||
let profile_uid_and_name = Config::profiles()
|
||||
.await
|
||||
.data_mut()
|
||||
.all_profile_uid_and_name()
|
||||
.unwrap_or_default();
|
||||
let profiles_config = Config::profiles().await;
|
||||
let profiles_arc = profiles_config.latest_arc();
|
||||
let profile_uid_and_name = profiles_arc.all_profile_uid_and_name().unwrap_or_default();
|
||||
let is_lightweight_mode = is_in_lightweight_mode();
|
||||
|
||||
match app_handle.tray_by_id("main") {
|
||||
@@ -375,7 +372,7 @@ impl Tray {
|
||||
}
|
||||
};
|
||||
|
||||
let verge = Config::verge().await.latest_ref().clone();
|
||||
let verge = Config::verge().await.latest_arc();
|
||||
let system_mode = verge.enable_system_proxy.as_ref().unwrap_or(&false);
|
||||
let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false);
|
||||
|
||||
@@ -418,7 +415,7 @@ impl Tray {
|
||||
}
|
||||
};
|
||||
|
||||
let verge = Config::verge().await.latest_ref().clone();
|
||||
let verge = Config::verge().await.latest_arc();
|
||||
let system_mode = verge.enable_system_proxy.as_ref().unwrap_or(&false);
|
||||
let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false);
|
||||
|
||||
@@ -433,24 +430,6 @@ impl Tray {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 更新托盘显示状态的函数
|
||||
pub async fn update_tray_display(&self) -> Result<()> {
|
||||
if handle::Handle::global().is_exiting() {
|
||||
logging!(debug, Type::Tray, "应用正在退出,跳过托盘显示状态更新");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let _tray = app_handle
|
||||
.tray_by_id("main")
|
||||
.ok_or_else(|| anyhow::anyhow!("Failed to get main tray"))?;
|
||||
|
||||
// 更新菜单
|
||||
self.update_menu().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// 更新托盘提示
|
||||
pub async fn update_tooltip(&self) -> Result<()> {
|
||||
if handle::Handle::global().is_exiting() {
|
||||
@@ -460,7 +439,7 @@ impl Tray {
|
||||
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
|
||||
let verge = Config::verge().await.latest_ref().clone();
|
||||
let verge = Config::verge().await.latest_arc();
|
||||
let system_proxy = verge.enable_system_proxy.as_ref().unwrap_or(&false);
|
||||
let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false);
|
||||
|
||||
@@ -474,9 +453,9 @@ impl Tray {
|
||||
let mut current_profile_name = "None".into();
|
||||
{
|
||||
let profiles = Config::profiles().await;
|
||||
let profiles = profiles.latest_ref();
|
||||
let profiles = profiles.latest_arc();
|
||||
if let Some(current_profile_uid) = profiles.get_current()
|
||||
&& let Ok(profile) = profiles.get_item(¤t_profile_uid)
|
||||
&& let Ok(profile) = profiles.get_item(current_profile_uid)
|
||||
{
|
||||
current_profile_name = match &profile.name {
|
||||
Some(profile_name) => profile_name.to_string(),
|
||||
@@ -525,9 +504,7 @@ impl Tray {
|
||||
logging!(debug, Type::Tray, "应用正在退出,跳过托盘局部更新");
|
||||
return Ok(());
|
||||
}
|
||||
// self.update_menu().await?;
|
||||
// 更新轻量模式显示状态
|
||||
self.update_tray_display().await?;
|
||||
self.update_menu().await?;
|
||||
self.update_icon().await?;
|
||||
self.update_tooltip().await?;
|
||||
Ok(())
|
||||
@@ -552,7 +529,7 @@ impl Tray {
|
||||
|
||||
#[cfg(any(target_os = "macos", target_os = "windows"))]
|
||||
let show_menu_on_left_click = {
|
||||
let tray_event = { Config::verge().await.latest_ref().tray_event.clone() };
|
||||
let tray_event = { Config::verge().await.latest_arc().tray_event.clone() };
|
||||
let tray_event: String = tray_event.unwrap_or_else(|| "main_window".into());
|
||||
tray_event.as_str() == "tray_menu"
|
||||
};
|
||||
@@ -583,7 +560,7 @@ impl Tray {
|
||||
}
|
||||
|
||||
AsyncHandler::spawn(|| async move {
|
||||
let tray_event = { Config::verge().await.latest_ref().tray_event.clone() };
|
||||
let tray_event = { Config::verge().await.latest_arc().tray_event.clone() };
|
||||
let tray_event: String = tray_event.unwrap_or_else(|| "main_window".into());
|
||||
logging!(debug, Type::Tray, "tray event: {tray_event:?}");
|
||||
|
||||
@@ -622,22 +599,6 @@ impl Tray {
|
||||
tray.on_menu_event(on_menu_event);
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// 托盘统一的状态更新函数
|
||||
pub async fn update_all_states(&self) -> Result<()> {
|
||||
if handle::Handle::global().is_exiting() {
|
||||
logging!(debug, Type::Tray, "应用正在退出,跳过托盘状态更新");
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// 确保所有状态更新完成
|
||||
self.update_tray_display().await?;
|
||||
// self.update_menu().await?;
|
||||
self.update_icon().await?;
|
||||
self.update_tooltip().await?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
fn create_hotkeys(hotkeys: &Option<Vec<String>>) -> HashMap<String, String> {
|
||||
@@ -666,7 +627,7 @@ fn create_hotkeys(hotkeys: &Option<Vec<String>>) -> HashMap<String, String> {
|
||||
|
||||
async fn create_profile_menu_item(
|
||||
app_handle: &AppHandle,
|
||||
profile_uid_and_name: Vec<(String, String)>,
|
||||
profile_uid_and_name: Vec<(&String, &String)>,
|
||||
) -> Result<Vec<CheckMenuItem<Wry>>> {
|
||||
let futures = profile_uid_and_name
|
||||
.iter()
|
||||
@@ -675,7 +636,7 @@ async fn create_profile_menu_item(
|
||||
async move {
|
||||
let is_current_profile = Config::profiles()
|
||||
.await
|
||||
.latest_ref()
|
||||
.latest_arc()
|
||||
.is_current_profile_index(profile_uid);
|
||||
CheckMenuItem::with_id(
|
||||
&app_handle,
|
||||
@@ -870,7 +831,7 @@ async fn create_tray_menu(
|
||||
system_proxy_enabled: bool,
|
||||
tun_mode_enabled: bool,
|
||||
tun_mode_available: bool,
|
||||
profile_uid_and_name: Vec<(String, String)>,
|
||||
profile_uid_and_name: Vec<(&String, &String)>,
|
||||
is_lightweight_mode: bool,
|
||||
) -> Result<tauri::menu::Menu<Wry>> {
|
||||
let current_proxy_mode = mode.unwrap_or("");
|
||||
@@ -878,10 +839,10 @@ async fn create_tray_menu(
|
||||
// 获取当前配置文件的选中代理组信息
|
||||
let current_profile_selected = {
|
||||
let profiles_config = Config::profiles().await;
|
||||
let profiles_ref = profiles_config.latest_ref();
|
||||
let profiles_ref = profiles_config.latest_arc();
|
||||
profiles_ref
|
||||
.get_current()
|
||||
.and_then(|uid| profiles_ref.get_item(&uid).ok())
|
||||
.and_then(|uid| profiles_ref.get_item(uid).ok())
|
||||
.and_then(|profile| profile.selected.clone())
|
||||
.unwrap_or_default()
|
||||
};
|
||||
@@ -924,7 +885,7 @@ async fn create_tray_menu(
|
||||
.collect::<HashMap<String, usize>>()
|
||||
});
|
||||
|
||||
let verge_settings = Config::verge().await.latest_ref().clone();
|
||||
let verge_settings = Config::verge().await.latest_arc();
|
||||
let show_proxy_groups_inline = verge_settings.tray_inline_proxy_groups.unwrap_or(false);
|
||||
|
||||
let version = env!("CARGO_PKG_VERSION");
|
||||
@@ -1255,66 +1216,20 @@ fn on_menu_event(_: &AppHandle, event: MenuEvent) {
|
||||
}
|
||||
id if id.starts_with("proxy_") => {
|
||||
// proxy_{group_name}_{proxy_name}
|
||||
let parts: Vec<&str> = id.splitn(3, '_').collect();
|
||||
|
||||
if parts.len() == 3 && parts[0] == "proxy" {
|
||||
let group_name = parts[1];
|
||||
let proxy_name = parts[2];
|
||||
|
||||
match handle::Handle::mihomo()
|
||||
.await
|
||||
.select_node_for_group(group_name, proxy_name)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
logging!(
|
||||
info,
|
||||
Type::Tray,
|
||||
"切换代理成功: {} -> {}",
|
||||
group_name,
|
||||
proxy_name
|
||||
);
|
||||
let _ = handle::Handle::app_handle()
|
||||
.emit("verge://refresh-proxy-config", ());
|
||||
}
|
||||
Err(e) => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Tray,
|
||||
"切换代理失败: {} -> {}, 错误: {:?}",
|
||||
group_name,
|
||||
proxy_name,
|
||||
e
|
||||
);
|
||||
|
||||
// Fallback to IPC update
|
||||
if (handle::Handle::mihomo()
|
||||
.await
|
||||
.select_node_for_group(group_name, proxy_name)
|
||||
.await)
|
||||
.is_ok()
|
||||
{
|
||||
logging!(
|
||||
info,
|
||||
Type::Tray,
|
||||
"代理切换回退成功: {} -> {}",
|
||||
group_name,
|
||||
proxy_name
|
||||
);
|
||||
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let _ = app_handle.emit("verge://force-refresh-proxies", ());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
let rest = match id.strip_prefix("proxy_") {
|
||||
Some(r) => r,
|
||||
None => return,
|
||||
};
|
||||
let (group_name, proxy_name) = match rest.split_once('_') {
|
||||
Some((g, p)) => (g, p),
|
||||
None => return,
|
||||
};
|
||||
feat::switch_proxy_node(group_name, proxy_name).await;
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
||||
// Ensure tray state update is awaited and properly handled
|
||||
if let Err(e) = Tray::global().update_all_states().await {
|
||||
logging!(warn, Type::Tray, "Failed to update tray state: {e}");
|
||||
}
|
||||
// We dont expected to refresh tray state here
|
||||
// as the inner handle function (SHOULD) already takes care of it
|
||||
});
|
||||
}
|
||||
|
||||
@@ -266,7 +266,7 @@ impl CoreConfigValidator {
|
||||
|
||||
logging!(info, Type::Validate, "开始验证配置文件: {}", config_path);
|
||||
|
||||
let clash_core = Config::verge().await.latest_ref().get_valid_clash_core();
|
||||
let clash_core = Config::verge().await.latest_arc().get_valid_clash_core();
|
||||
logging!(info, Type::Validate, "使用内核: {}", clash_core);
|
||||
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
|
||||
@@ -44,12 +44,49 @@ struct ProfileItems {
|
||||
profile_name: String,
|
||||
}
|
||||
|
||||
impl Default for ProfileItems {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
config: Default::default(),
|
||||
profile_name: Default::default(),
|
||||
merge_item: ChainItem {
|
||||
uid: "".into(),
|
||||
data: ChainType::Merge(Mapping::new()),
|
||||
},
|
||||
script_item: ChainItem {
|
||||
uid: "".into(),
|
||||
data: ChainType::Script(tmpl::ITEM_SCRIPT.into()),
|
||||
},
|
||||
rules_item: ChainItem {
|
||||
uid: "".into(),
|
||||
data: ChainType::Rules(SeqMap::default()),
|
||||
},
|
||||
proxies_item: ChainItem {
|
||||
uid: "".into(),
|
||||
data: ChainType::Proxies(SeqMap::default()),
|
||||
},
|
||||
groups_item: ChainItem {
|
||||
uid: "".into(),
|
||||
data: ChainType::Groups(SeqMap::default()),
|
||||
},
|
||||
global_merge: ChainItem {
|
||||
uid: "Merge".into(),
|
||||
data: ChainType::Merge(Mapping::new()),
|
||||
},
|
||||
global_script: ChainItem {
|
||||
uid: "Script".into(),
|
||||
data: ChainType::Script(tmpl::ITEM_SCRIPT.into()),
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn get_config_values() -> ConfigValues {
|
||||
let clash_config = { Config::clash().await.latest_ref().0.clone() };
|
||||
let clash_config = { Config::clash().await.latest_arc().0.clone() };
|
||||
|
||||
let (clash_core, enable_tun, enable_builtin, socks_enabled, http_enabled, enable_dns_settings) = {
|
||||
let verge = Config::verge().await;
|
||||
let verge = verge.latest_ref();
|
||||
let verge = verge.latest_arc();
|
||||
(
|
||||
Some(verge.get_valid_clash_core()),
|
||||
verge.enable_tun_mode.unwrap_or(false),
|
||||
@@ -63,14 +100,14 @@ async fn get_config_values() -> ConfigValues {
|
||||
#[cfg(not(target_os = "windows"))]
|
||||
let redir_enabled = {
|
||||
let verge = Config::verge().await;
|
||||
let verge = verge.latest_ref();
|
||||
let verge = verge.latest_arc();
|
||||
verge.verge_redir_enabled.unwrap_or(false)
|
||||
};
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
let tproxy_enabled = {
|
||||
let verge = Config::verge().await;
|
||||
let verge = verge.latest_ref();
|
||||
let verge = verge.latest_arc();
|
||||
verge.verge_tproxy_enabled.unwrap_or(false)
|
||||
};
|
||||
|
||||
@@ -89,33 +126,43 @@ async fn get_config_values() -> ConfigValues {
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::cognitive_complexity)]
|
||||
async fn collect_profile_items() -> ProfileItems {
|
||||
// 从profiles里拿东西 - 先收集需要的数据,然后释放锁
|
||||
let (
|
||||
current,
|
||||
merge_uid,
|
||||
script_uid,
|
||||
rules_uid,
|
||||
proxies_uid,
|
||||
groups_uid,
|
||||
_current_profile_uid,
|
||||
name,
|
||||
) = {
|
||||
let (current, merge_uid, script_uid, rules_uid, proxies_uid, groups_uid, name) = {
|
||||
let current = {
|
||||
let profiles = Config::profiles().await;
|
||||
let profiles_clone = profiles.latest_ref().clone();
|
||||
let profiles_clone = profiles.latest_arc();
|
||||
profiles_clone.current_mapping().await.unwrap_or_default()
|
||||
};
|
||||
|
||||
let profiles = Config::profiles().await;
|
||||
let profiles_ref = profiles.latest_ref();
|
||||
let profiles_ref = profiles.latest_arc();
|
||||
let current_profile_uid = match profiles_ref.get_current() {
|
||||
Some(uid) => uid.clone(),
|
||||
None => return ProfileItems::default(),
|
||||
};
|
||||
|
||||
let merge_uid = profiles_ref.current_merge().unwrap_or_default();
|
||||
let script_uid = profiles_ref.current_script().unwrap_or_default();
|
||||
let rules_uid = profiles_ref.current_rules().unwrap_or_default();
|
||||
let proxies_uid = profiles_ref.current_proxies().unwrap_or_default();
|
||||
let groups_uid = profiles_ref.current_groups().unwrap_or_default();
|
||||
let current_profile_uid = profiles_ref.get_current().unwrap_or_default();
|
||||
let current_item = match profiles_ref.get_item_arc(¤t_profile_uid) {
|
||||
Some(item) => item,
|
||||
None => return ProfileItems::default(),
|
||||
};
|
||||
|
||||
let merge_uid = current_item
|
||||
.current_merge()
|
||||
.unwrap_or_else(|| "Merge".into());
|
||||
let script_uid = current_item
|
||||
.current_script()
|
||||
.unwrap_or_else(|| "Script".into());
|
||||
let rules_uid = current_item
|
||||
.current_rules()
|
||||
.unwrap_or_else(|| "Rules".into());
|
||||
let proxies_uid = current_item
|
||||
.current_proxies()
|
||||
.unwrap_or_else(|| "Proxies".into());
|
||||
let groups_uid = current_item
|
||||
.current_groups()
|
||||
.unwrap_or_else(|| "Groups".into());
|
||||
|
||||
let name = profiles_ref
|
||||
.get_item(¤t_profile_uid)
|
||||
@@ -130,7 +177,6 @@ async fn collect_profile_items() -> ProfileItems {
|
||||
rules_uid,
|
||||
proxies_uid,
|
||||
groups_uid,
|
||||
current_profile_uid,
|
||||
name,
|
||||
)
|
||||
};
|
||||
@@ -139,8 +185,8 @@ async fn collect_profile_items() -> ProfileItems {
|
||||
let merge_item = {
|
||||
let item = {
|
||||
let profiles = Config::profiles().await;
|
||||
let profiles = profiles.latest_ref();
|
||||
profiles.get_item(merge_uid).ok().cloned()
|
||||
let profiles = profiles.latest_arc();
|
||||
profiles.get_item(&merge_uid).ok().cloned()
|
||||
};
|
||||
if let Some(item) = item {
|
||||
<Option<ChainItem>>::from_async(&item).await
|
||||
@@ -156,8 +202,8 @@ async fn collect_profile_items() -> ProfileItems {
|
||||
let script_item = {
|
||||
let item = {
|
||||
let profiles = Config::profiles().await;
|
||||
let profiles = profiles.latest_ref();
|
||||
profiles.get_item(script_uid).ok().cloned()
|
||||
let profiles = profiles.latest_arc();
|
||||
profiles.get_item(&script_uid).ok().cloned()
|
||||
};
|
||||
if let Some(item) = item {
|
||||
<Option<ChainItem>>::from_async(&item).await
|
||||
@@ -173,8 +219,8 @@ async fn collect_profile_items() -> ProfileItems {
|
||||
let rules_item = {
|
||||
let item = {
|
||||
let profiles = Config::profiles().await;
|
||||
let profiles = profiles.latest_ref();
|
||||
profiles.get_item(rules_uid).ok().cloned()
|
||||
let profiles = profiles.latest_arc();
|
||||
profiles.get_item(&rules_uid).ok().cloned()
|
||||
};
|
||||
if let Some(item) = item {
|
||||
<Option<ChainItem>>::from_async(&item).await
|
||||
@@ -190,8 +236,8 @@ async fn collect_profile_items() -> ProfileItems {
|
||||
let proxies_item = {
|
||||
let item = {
|
||||
let profiles = Config::profiles().await;
|
||||
let profiles = profiles.latest_ref();
|
||||
profiles.get_item(proxies_uid).ok().cloned()
|
||||
let profiles = profiles.latest_arc();
|
||||
profiles.get_item(&proxies_uid).ok().cloned()
|
||||
};
|
||||
if let Some(item) = item {
|
||||
<Option<ChainItem>>::from_async(&item).await
|
||||
@@ -207,8 +253,8 @@ async fn collect_profile_items() -> ProfileItems {
|
||||
let groups_item = {
|
||||
let item = {
|
||||
let profiles = Config::profiles().await;
|
||||
let profiles = profiles.latest_ref();
|
||||
profiles.get_item(groups_uid).ok().cloned()
|
||||
let profiles = profiles.latest_arc();
|
||||
profiles.get_item(&groups_uid).ok().cloned()
|
||||
};
|
||||
if let Some(item) = item {
|
||||
<Option<ChainItem>>::from_async(&item).await
|
||||
@@ -224,7 +270,7 @@ async fn collect_profile_items() -> ProfileItems {
|
||||
let global_merge = {
|
||||
let item = {
|
||||
let profiles = Config::profiles().await;
|
||||
let profiles = profiles.latest_ref();
|
||||
let profiles = profiles.latest_arc();
|
||||
profiles.get_item("Merge").ok().cloned()
|
||||
};
|
||||
if let Some(item) = item {
|
||||
@@ -241,7 +287,7 @@ async fn collect_profile_items() -> ProfileItems {
|
||||
let global_script = {
|
||||
let item = {
|
||||
let profiles = Config::profiles().await;
|
||||
let profiles = profiles.latest_ref();
|
||||
let profiles = profiles.latest_arc();
|
||||
profiles.get_item("Script").ok().cloned()
|
||||
};
|
||||
if let Some(item) = item {
|
||||
@@ -394,7 +440,7 @@ async fn merge_default_config(
|
||||
if key.as_str() == Some("external-controller") {
|
||||
let enable_external_controller = Config::verge()
|
||||
.await
|
||||
.latest_ref()
|
||||
.latest_arc()
|
||||
.enable_external_controller
|
||||
.unwrap_or(false);
|
||||
|
||||
|
||||
@@ -78,7 +78,7 @@ pub async fn delete_webdav_backup(filename: String) -> Result<()> {
|
||||
/// Restore WebDAV backup
|
||||
pub async fn restore_webdav_backup(filename: String) -> Result<()> {
|
||||
let verge = Config::verge().await;
|
||||
let verge_data = verge.latest_ref().clone();
|
||||
let verge_data = verge.latest_arc();
|
||||
let webdav_url = verge_data.webdav_url.clone();
|
||||
let webdav_username = verge_data.webdav_username.clone();
|
||||
let webdav_password = verge_data.webdav_password.clone();
|
||||
@@ -243,7 +243,7 @@ pub async fn restore_local_backup(filename: String) -> Result<()> {
|
||||
|
||||
let (webdav_url, webdav_username, webdav_password) = {
|
||||
let verge = Config::verge().await;
|
||||
let verge = verge.latest_ref();
|
||||
let verge = verge.latest_arc();
|
||||
(
|
||||
verge.webdav_url.clone(),
|
||||
verge.webdav_username.clone(),
|
||||
|
||||
@@ -72,10 +72,12 @@ pub async fn change_clash_mode(mode: String) {
|
||||
{
|
||||
Ok(_) => {
|
||||
// 更新订阅
|
||||
Config::clash().await.data_mut().patch_config(mapping);
|
||||
Config::clash()
|
||||
.await
|
||||
.edit_draft(|d| d.patch_config(mapping));
|
||||
|
||||
// 分离数据获取和异步调用
|
||||
let clash_data = Config::clash().await.data_mut().clone();
|
||||
let clash_data = Config::clash().await.data_arc();
|
||||
if clash_data.save_config().await.is_ok() {
|
||||
handle::Handle::refresh_clash();
|
||||
logging_error!(Type::Tray, tray::Tray::global().update_menu().await);
|
||||
@@ -84,7 +86,7 @@ pub async fn change_clash_mode(mode: String) {
|
||||
|
||||
let is_auto_close_connection = Config::verge()
|
||||
.await
|
||||
.data_mut()
|
||||
.data_arc()
|
||||
.auto_close_connection
|
||||
.unwrap_or(false);
|
||||
if is_auto_close_connection {
|
||||
@@ -102,7 +104,7 @@ pub async fn test_delay(url: String) -> anyhow::Result<u32> {
|
||||
|
||||
let tun_mode = Config::verge()
|
||||
.await
|
||||
.latest_ref()
|
||||
.latest_arc()
|
||||
.enable_tun_mode
|
||||
.unwrap_or(false);
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@ use crate::{
|
||||
core::{CoreManager, handle, hotkey, sysopt, tray},
|
||||
logging_error,
|
||||
module::lightweight,
|
||||
utils::logging::Type,
|
||||
utils::{draft::SharedBox, logging::Type},
|
||||
};
|
||||
use anyhow::Result;
|
||||
use serde_yaml_ng::Mapping;
|
||||
@@ -12,8 +12,7 @@ use serde_yaml_ng::Mapping;
|
||||
pub async fn patch_clash(patch: Mapping) -> Result<()> {
|
||||
Config::clash()
|
||||
.await
|
||||
.draft_mut()
|
||||
.patch_config(patch.clone());
|
||||
.edit_draft(|d| d.patch_config(patch.clone()));
|
||||
|
||||
let res = {
|
||||
// 激活订阅
|
||||
@@ -25,7 +24,9 @@ pub async fn patch_clash(patch: Mapping) -> Result<()> {
|
||||
logging_error!(Type::Tray, tray::Tray::global().update_menu().await);
|
||||
logging_error!(Type::Tray, tray::Tray::global().update_icon().await);
|
||||
}
|
||||
Config::runtime().await.draft_mut().patch_config(patch);
|
||||
Config::runtime()
|
||||
.await
|
||||
.edit_draft(|d| d.patch_config(patch));
|
||||
CoreManager::global().update_config().await?;
|
||||
}
|
||||
handle::Handle::refresh_clash();
|
||||
@@ -35,7 +36,7 @@ pub async fn patch_clash(patch: Mapping) -> Result<()> {
|
||||
Ok(()) => {
|
||||
Config::clash().await.apply();
|
||||
// 分离数据获取和异步调用
|
||||
let clash_data = Config::clash().await.data_mut().clone();
|
||||
let clash_data = Config::clash().await.data_arc();
|
||||
clash_data.save_config().await?;
|
||||
Ok(())
|
||||
}
|
||||
@@ -190,7 +191,9 @@ async fn process_terminated_flags(update_flags: i32, patch: &IVerge) -> Result<(
|
||||
handle::Handle::refresh_clash();
|
||||
}
|
||||
if (update_flags & (UpdateFlags::VergeConfig as i32)) != 0 {
|
||||
Config::verge().await.draft_mut().enable_global_hotkey = patch.enable_global_hotkey;
|
||||
Config::verge()
|
||||
.await
|
||||
.edit_draft(|d| d.enable_global_hotkey = patch.enable_global_hotkey);
|
||||
handle::Handle::refresh_verge();
|
||||
}
|
||||
if (update_flags & (UpdateFlags::Launch as i32)) != 0 {
|
||||
@@ -227,7 +230,7 @@ async fn process_terminated_flags(update_flags: i32, patch: &IVerge) -> Result<(
|
||||
}
|
||||
|
||||
pub async fn patch_verge(patch: &IVerge, not_save_file: bool) -> Result<()> {
|
||||
Config::verge().await.draft_mut().patch_config(patch);
|
||||
Config::verge().await.edit_draft(|d| d.patch_config(patch));
|
||||
|
||||
let update_flags = determine_update_flags(patch);
|
||||
let process_flag_result: std::result::Result<(), anyhow::Error> = {
|
||||
@@ -242,8 +245,14 @@ pub async fn patch_verge(patch: &IVerge, not_save_file: bool) -> Result<()> {
|
||||
Config::verge().await.apply();
|
||||
if !not_save_file {
|
||||
// 分离数据获取和异步调用
|
||||
let verge_data = Config::verge().await.data_ref().clone();
|
||||
let verge_data = Config::verge().await.data_arc();
|
||||
verge_data.save_file().await?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn fetch_verge_config() -> Result<SharedBox<IVerge>> {
|
||||
let draft = Config::verge().await;
|
||||
let data = draft.data_arc();
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ use crate::{
|
||||
};
|
||||
use anyhow::{Result, bail};
|
||||
use smartstring::alias::String;
|
||||
use tauri::Emitter;
|
||||
|
||||
/// Toggle proxy profile
|
||||
pub async fn toggle_proxy_profile(profile_index: String) {
|
||||
@@ -23,12 +24,72 @@ pub async fn toggle_proxy_profile(profile_index: String) {
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn switch_proxy_node(group_name: &str, proxy_name: &str) {
|
||||
match handle::Handle::mihomo()
|
||||
.await
|
||||
.select_node_for_group(group_name, proxy_name)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
logging!(
|
||||
info,
|
||||
Type::Tray,
|
||||
"切换代理成功: {} -> {}",
|
||||
group_name,
|
||||
proxy_name
|
||||
);
|
||||
let _ = handle::Handle::app_handle().emit("verge://refresh-proxy-config", ());
|
||||
let _ = tray::Tray::global().update_menu().await;
|
||||
return;
|
||||
}
|
||||
Err(err) => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Tray,
|
||||
"切换代理失败: {} -> {}, 错误: {:?}",
|
||||
group_name,
|
||||
proxy_name,
|
||||
err
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
match handle::Handle::mihomo()
|
||||
.await
|
||||
.select_node_for_group(group_name, proxy_name)
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
logging!(
|
||||
info,
|
||||
Type::Tray,
|
||||
"代理切换回退成功: {} -> {}",
|
||||
group_name,
|
||||
proxy_name
|
||||
);
|
||||
let _ = handle::Handle::app_handle().emit("verge://force-refresh-proxies", ());
|
||||
let _ = tray::Tray::global().update_menu().await;
|
||||
}
|
||||
Err(err) => {
|
||||
logging!(
|
||||
error,
|
||||
Type::Tray,
|
||||
"代理切换最终失败: {} -> {}, 错误: {:?}",
|
||||
group_name,
|
||||
proxy_name,
|
||||
err
|
||||
);
|
||||
let _ = handle::Handle::app_handle().emit("verge://force-refresh-proxies", ());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn should_update_profile(
|
||||
uid: &String,
|
||||
ignore_auto_update: bool,
|
||||
) -> Result<Option<(String, Option<PrfOption>)>> {
|
||||
let profiles = Config::profiles().await;
|
||||
let profiles = profiles.latest_ref();
|
||||
let profiles = profiles.latest_arc();
|
||||
let item = profiles.get_item(uid)?;
|
||||
let is_remote = item.itype.as_ref().is_some_and(|s| s == "remote");
|
||||
|
||||
@@ -89,15 +150,15 @@ async fn perform_profile_update(
|
||||
let mut merged_opt = PrfOption::merge(opt, option);
|
||||
let is_current = {
|
||||
let profiles = Config::profiles().await;
|
||||
profiles.latest_ref().is_current_profile_index(uid)
|
||||
};
|
||||
let profile_name = {
|
||||
let profiles = Config::profiles().await;
|
||||
profiles
|
||||
.latest_ref()
|
||||
.get_name_by_uid(uid)
|
||||
.unwrap_or_default()
|
||||
profiles.latest_arc().is_current_profile_index(uid)
|
||||
};
|
||||
let profiles = Config::profiles().await;
|
||||
let profiles_arc = profiles.latest_arc();
|
||||
let profile_name = profiles_arc
|
||||
.get_name_by_uid(uid)
|
||||
.cloned()
|
||||
.unwrap_or_else(|| String::from("UnKown Profile"));
|
||||
|
||||
let mut last_err;
|
||||
|
||||
match PrfItem::from_url(url, None, None, merged_opt.as_ref()).await {
|
||||
|
||||
@@ -10,8 +10,8 @@ use tauri_plugin_clipboard_manager::ClipboardExt;
|
||||
/// Toggle system proxy on/off
|
||||
pub async fn toggle_system_proxy() {
|
||||
let verge = Config::verge().await;
|
||||
let enable = verge.latest_ref().enable_system_proxy.unwrap_or(false);
|
||||
let auto_close_connection = verge.latest_ref().auto_close_connection.unwrap_or(false);
|
||||
let enable = verge.latest_arc().enable_system_proxy.unwrap_or(false);
|
||||
let auto_close_connection = verge.latest_arc().auto_close_connection.unwrap_or(false);
|
||||
|
||||
// 如果当前系统代理即将关闭,且自动关闭连接设置为true,则关闭所有连接
|
||||
if enable
|
||||
@@ -42,7 +42,7 @@ pub async fn toggle_system_proxy() {
|
||||
|
||||
/// Toggle TUN mode on/off
|
||||
pub async fn toggle_tun_mode(not_save_file: Option<bool>) {
|
||||
let enable = Config::verge().await.data_mut().enable_tun_mode;
|
||||
let enable = Config::verge().await.latest_arc().enable_tun_mode;
|
||||
let enable = enable.unwrap_or(false);
|
||||
|
||||
match super::patch_verge(
|
||||
@@ -66,7 +66,7 @@ pub async fn copy_clash_env() {
|
||||
Ok(ip) => ip.into(),
|
||||
Err(_) => Config::verge()
|
||||
.await
|
||||
.latest_ref()
|
||||
.latest_arc()
|
||||
.proxy_host
|
||||
.clone()
|
||||
.unwrap_or_else(|| "127.0.0.1".into()),
|
||||
@@ -76,7 +76,7 @@ pub async fn copy_clash_env() {
|
||||
let port = {
|
||||
Config::verge()
|
||||
.await
|
||||
.latest_ref()
|
||||
.latest_arc()
|
||||
.verge_mixed_port
|
||||
.unwrap_or(7897)
|
||||
};
|
||||
@@ -84,7 +84,7 @@ pub async fn copy_clash_env() {
|
||||
let socks5_proxy = format!("socks5://{clash_verge_rev_ip}:{port}");
|
||||
|
||||
let cliboard = app_handle.clipboard();
|
||||
let env_type = { Config::verge().await.latest_ref().env_type.clone() };
|
||||
let env_type = { Config::verge().await.latest_arc().env_type.clone() };
|
||||
let env_type = match env_type {
|
||||
Some(env_type) => env_type,
|
||||
None => {
|
||||
|
||||
@@ -47,7 +47,7 @@ pub async fn clean_async() -> bool {
|
||||
let tun_task = async {
|
||||
let tun_enabled = Config::verge()
|
||||
.await
|
||||
.latest_ref()
|
||||
.latest_arc()
|
||||
.enable_tun_mode
|
||||
.unwrap_or(false);
|
||||
|
||||
@@ -100,7 +100,7 @@ pub async fn clean_async() -> bool {
|
||||
// 检查系统代理是否开启
|
||||
let sys_proxy_enabled = Config::verge()
|
||||
.await
|
||||
.latest_ref()
|
||||
.latest_arc()
|
||||
.enable_system_proxy
|
||||
.unwrap_or(false);
|
||||
|
||||
@@ -176,7 +176,7 @@ pub async fn clean_async() -> bool {
|
||||
{
|
||||
let sys_proxy_enabled = Config::verge()
|
||||
.await
|
||||
.latest_ref()
|
||||
.latest_arc()
|
||||
.enable_system_proxy
|
||||
.unwrap_or(false);
|
||||
|
||||
@@ -316,7 +316,7 @@ pub async fn hide() {
|
||||
|
||||
let enable_auto_light_weight_mode = Config::verge()
|
||||
.await
|
||||
.data_mut()
|
||||
.latest_arc()
|
||||
.enable_auto_light_weight_mode
|
||||
.unwrap_or(false);
|
||||
|
||||
|
||||
@@ -319,7 +319,7 @@ pub fn run() {
|
||||
AsyncHandler::spawn(move || async move {
|
||||
let is_enable_global_hotkey = Config::verge()
|
||||
.await
|
||||
.latest_ref()
|
||||
.latest_arc()
|
||||
.enable_global_hotkey
|
||||
.unwrap_or(true);
|
||||
|
||||
@@ -360,7 +360,7 @@ pub fn run() {
|
||||
let _ = hotkey::Hotkey::global().unregister_system_hotkey(SystemHotkey::CmdW);
|
||||
let is_enable_global_hotkey = Config::verge()
|
||||
.await
|
||||
.latest_ref()
|
||||
.latest_arc()
|
||||
.enable_global_hotkey
|
||||
.unwrap_or(true);
|
||||
if !is_enable_global_hotkey {
|
||||
|
||||
@@ -79,7 +79,7 @@ pub fn is_in_lightweight_mode() -> bool {
|
||||
}
|
||||
|
||||
async fn refresh_lightweight_tray_state() {
|
||||
if let Err(err) = Tray::global().update_tray_display().await {
|
||||
if let Err(err) = Tray::global().update_menu().await {
|
||||
logging!(warn, Type::Lightweight, "更新托盘轻量模式状态失败: {err}");
|
||||
}
|
||||
}
|
||||
@@ -87,11 +87,11 @@ async fn refresh_lightweight_tray_state() {
|
||||
pub async fn auto_lightweight_boot() -> Result<()> {
|
||||
let verge_config = Config::verge().await;
|
||||
let enable_auto = verge_config
|
||||
.data_mut()
|
||||
.latest_arc()
|
||||
.enable_auto_light_weight_mode
|
||||
.unwrap_or(false);
|
||||
let is_silent_start = verge_config
|
||||
.latest_ref()
|
||||
.latest_arc()
|
||||
.enable_silent_start
|
||||
.unwrap_or(false);
|
||||
|
||||
@@ -236,7 +236,7 @@ async fn setup_light_weight_timer() -> Result<()> {
|
||||
|
||||
let once_by_minutes = Config::verge()
|
||||
.await
|
||||
.latest_ref()
|
||||
.latest_arc()
|
||||
.auto_light_weight_minutes
|
||||
.unwrap_or(10);
|
||||
|
||||
|
||||
@@ -1,179 +1,368 @@
|
||||
use parking_lot::RwLock;
|
||||
use std::sync::Arc;
|
||||
|
||||
use parking_lot::{
|
||||
MappedRwLockReadGuard, MappedRwLockWriteGuard, RwLock, RwLockReadGuard,
|
||||
RwLockUpgradableReadGuard, RwLockWriteGuard,
|
||||
};
|
||||
pub type SharedBox<T> = Arc<Box<T>>;
|
||||
type DraftInner<T> = (SharedBox<T>, Option<SharedBox<T>>);
|
||||
|
||||
/// Draft 管理:committed 与 optional draft 都以 Arc<Box<T>> 存储,
|
||||
// (committed_snapshot, optional_draft_snapshot)
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Draft<T: Clone + ToOwned> {
|
||||
inner: Arc<RwLock<(T, Option<T>)>>,
|
||||
pub struct Draft<T: Clone> {
|
||||
inner: Arc<RwLock<DraftInner<T>>>,
|
||||
}
|
||||
|
||||
impl<T: Clone + ToOwned> From<T> for Draft<T> {
|
||||
fn from(data: T) -> Self {
|
||||
impl<T: Clone> Draft<T> {
|
||||
pub fn new(data: T) -> Self {
|
||||
Self {
|
||||
inner: Arc::new(RwLock::new((data, None))),
|
||||
inner: Arc::new(RwLock::new((Arc::new(Box::new(data)), None))),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Implements draft management for `Box<T>`, allowing for safe concurrent editing and committing of draft data.
|
||||
/// # Type Parameters
|
||||
/// - `T`: The underlying data type, which must implement `Clone` and `ToOwned`.
|
||||
///
|
||||
/// # Methods
|
||||
/// - `data_mut`: Returns a mutable reference to the committed data.
|
||||
/// - `draft_mut`: Creates or retrieves a mutable reference to the draft data, cloning the committed data if no draft exists.
|
||||
/// - `latest_ref`: Returns an immutable reference to the draft data if it exists, otherwise to the committed data.
|
||||
/// - `apply`: Commits the draft data, replacing the committed data and returning the old committed value if a draft existed.
|
||||
/// - `discard`: Discards the draft data and returns it if it existed.
|
||||
impl<T: Clone + ToOwned> Draft<Box<T>> {
|
||||
/// 正式数据视图
|
||||
pub fn data_ref(&self) -> MappedRwLockReadGuard<'_, Box<T>> {
|
||||
RwLockReadGuard::map(self.inner.read(), |inner| &inner.0)
|
||||
/// 以 Arc<Box<T>> 的形式获取当前“已提交(正式)”数据的快照(零拷贝,仅 clone Arc)
|
||||
pub fn data_arc(&self) -> SharedBox<T> {
|
||||
let guard = self.inner.read();
|
||||
Arc::clone(&guard.0)
|
||||
}
|
||||
|
||||
/// 可写正式数据
|
||||
pub fn data_mut(&self) -> MappedRwLockWriteGuard<'_, Box<T>> {
|
||||
RwLockWriteGuard::map(self.inner.write(), |inner| &mut inner.0)
|
||||
/// 获取当前(草稿若存在则返回草稿,否则返回已提交)的快照
|
||||
/// 这也是零拷贝:只 clone Arc,不 clone T
|
||||
pub fn latest_arc(&self) -> SharedBox<T> {
|
||||
let guard = self.inner.read();
|
||||
guard
|
||||
.1
|
||||
.as_ref()
|
||||
.cloned()
|
||||
.unwrap_or_else(|| Arc::clone(&guard.0))
|
||||
}
|
||||
|
||||
/// 创建或获取草稿并返回可写引用
|
||||
pub fn draft_mut(&self) -> MappedRwLockWriteGuard<'_, Box<T>> {
|
||||
let guard = self.inner.upgradable_read();
|
||||
/// 通过闭包以可变方式编辑草稿(在闭包中我们给出 &mut T)
|
||||
/// - 延迟拷贝:如果只有这一个 Arc 引用,则直接修改,不会克隆 T;
|
||||
/// - 若草稿被其他读者共享,Arc::make_mut 会做一次 T.clone(最小必要拷贝)。
|
||||
pub fn edit_draft<F, R>(&self, f: F) -> R
|
||||
where
|
||||
F: FnOnce(&mut T) -> R,
|
||||
{
|
||||
// 先获得写锁以创建或取出草稿 Arc 的可变引用位置
|
||||
let mut guard = self.inner.write();
|
||||
if guard.1.is_none() {
|
||||
let mut guard = RwLockUpgradableReadGuard::upgrade(guard);
|
||||
guard.1 = Some(guard.0.clone());
|
||||
return RwLockWriteGuard::map(guard, |inner| {
|
||||
inner.1.as_mut().unwrap_or_else(|| {
|
||||
unreachable!("Draft was just created above, this should never fail")
|
||||
})
|
||||
});
|
||||
// 创建草稿 snapshot(Arc clone,cheap)
|
||||
guard.1 = Some(Arc::clone(&guard.0));
|
||||
}
|
||||
// 已存在草稿,升级为写锁映射
|
||||
RwLockWriteGuard::map(RwLockUpgradableReadGuard::upgrade(guard), |inner| {
|
||||
inner
|
||||
.1
|
||||
.as_mut()
|
||||
.unwrap_or_else(|| unreachable!("Draft should exist when guard.1.is_some()"))
|
||||
})
|
||||
// 此时 guaranteed: guard.1 is Some(Arc<Box<T>>)
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let arc_box = guard.1.as_mut().unwrap();
|
||||
// Arc::make_mut: 如果只有一个引用则返回可变引用;否则会克隆底层 Box<T>(要求 T: Clone)
|
||||
let boxed = Arc::make_mut(arc_box); // &mut Box<T>
|
||||
// 对 Box<T> 解引用得到 &mut T
|
||||
|
||||
f(&mut **boxed)
|
||||
}
|
||||
|
||||
/// 零拷贝只读视图:返回草稿(若存在)或正式值
|
||||
pub fn latest_ref(&self) -> MappedRwLockReadGuard<'_, Box<T>> {
|
||||
RwLockReadGuard::map(self.inner.read(), |inner| {
|
||||
inner.1.as_ref().unwrap_or(&inner.0)
|
||||
})
|
||||
}
|
||||
|
||||
/// 提交草稿,返回旧正式数据
|
||||
/// 将草稿提交到已提交位置(替换),并清除草稿
|
||||
pub fn apply(&self) {
|
||||
let guard = self.inner.upgradable_read();
|
||||
if guard.1.is_none() {
|
||||
return;
|
||||
}
|
||||
|
||||
let mut guard = RwLockUpgradableReadGuard::upgrade(guard);
|
||||
if let Some(draft) = guard.1.take() {
|
||||
guard.0 = draft;
|
||||
let mut guard = self.inner.write();
|
||||
if let Some(d) = guard.1.take() {
|
||||
guard.0 = d;
|
||||
}
|
||||
}
|
||||
|
||||
/// 丢弃草稿,返回被丢弃的草稿
|
||||
/// 丢弃草稿(如果存在)
|
||||
pub fn discard(&self) {
|
||||
self.inner.write().1.take();
|
||||
let mut guard = self.inner.write();
|
||||
guard.1 = None;
|
||||
}
|
||||
|
||||
/// 异步修改正式数据,闭包直接获得 Box<T> 所有权
|
||||
pub async fn with_data_modify<F, Fut, R, E>(&self, f: F) -> Result<R, E>
|
||||
/// 异步地以拥有 Box<T> 的方式修改已提交数据:将克隆一次已提交数据到本地,
|
||||
/// 异步闭包返回新的 Box<T>(替换已提交数据)和业务返回值 R。
|
||||
pub async fn with_data_modify<F, Fut, R>(&self, f: F) -> Result<R, anyhow::Error>
|
||||
where
|
||||
T: Send + Sync + 'static,
|
||||
F: FnOnce(Box<T>) -> Fut + Send,
|
||||
Fut: std::future::Future<Output = Result<(Box<T>, R), E>> + Send,
|
||||
E: From<anyhow::Error>,
|
||||
Fut: std::future::Future<Output = Result<(Box<T>, R), anyhow::Error>> + Send,
|
||||
{
|
||||
// 克隆正式数据
|
||||
let local = {
|
||||
// 读取已提交快照(cheap Arc clone, 然后得到 Box<T> 所有权 via clone)
|
||||
// 注意:为了让闭包接收 Box<T> 所有权,我们需要 clone 底层 T(不可避免)
|
||||
let local: Box<T> = {
|
||||
let guard = self.inner.read();
|
||||
guard.0.clone()
|
||||
// 将 Arc<Box<T>> 的 Box<T> clone 出来(会调用 T: Clone)
|
||||
(*guard.0).clone()
|
||||
};
|
||||
|
||||
// 异步闭包执行,返回修改后的 Box<T> 和业务结果 R
|
||||
let (new_local, res) = f(local).await?;
|
||||
|
||||
// 写回正式数据
|
||||
// 将新的 Box<T> 放到已提交位置(包进 Arc)
|
||||
let mut guard = self.inner.write();
|
||||
guard.0 = new_local;
|
||||
guard.0 = Arc::new(new_local);
|
||||
|
||||
Ok(res)
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_draft_box() {
|
||||
use crate::config::IVerge;
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use anyhow::anyhow;
|
||||
use std::future::Future;
|
||||
use std::pin::Pin;
|
||||
use std::task::{Context, Poll, RawWaker, RawWakerVTable, Waker};
|
||||
|
||||
// 1. 创建 Draft<Box<IVerge>>
|
||||
let verge = Box::new(IVerge {
|
||||
enable_auto_launch: Some(true),
|
||||
enable_tun_mode: Some(false),
|
||||
..IVerge::default()
|
||||
});
|
||||
let draft = Draft::from(verge);
|
||||
|
||||
// 2. 读取正式数据(data_mut)
|
||||
{
|
||||
let data = draft.data_mut();
|
||||
assert_eq!(data.enable_auto_launch, Some(true));
|
||||
assert_eq!(data.enable_tun_mode, Some(false));
|
||||
#[derive(Clone, Debug, Default, PartialEq)]
|
||||
struct IVerge {
|
||||
enable_auto_launch: Option<bool>,
|
||||
enable_tun_mode: Option<bool>,
|
||||
}
|
||||
|
||||
// 3. 初次获取草稿(draft_mut 会自动 clone 一份)
|
||||
{
|
||||
let draft_view = draft.draft_mut();
|
||||
assert_eq!(draft_view.enable_auto_launch, Some(true));
|
||||
assert_eq!(draft_view.enable_tun_mode, Some(false));
|
||||
// Minimal single-threaded executor for immediately-ready futures
|
||||
fn block_on_ready<F: Future>(fut: F) -> F::Output {
|
||||
fn no_op_raw_waker() -> RawWaker {
|
||||
fn clone(_: *const ()) -> RawWaker {
|
||||
no_op_raw_waker()
|
||||
}
|
||||
fn wake(_: *const ()) {}
|
||||
fn wake_by_ref(_: *const ()) {}
|
||||
fn drop(_: *const ()) {}
|
||||
static VTABLE: RawWakerVTable = RawWakerVTable::new(clone, wake, wake_by_ref, drop);
|
||||
RawWaker::new(std::ptr::null(), &VTABLE)
|
||||
}
|
||||
|
||||
let waker = unsafe { Waker::from_raw(no_op_raw_waker()) };
|
||||
let mut cx = Context::from_waker(&waker);
|
||||
let mut fut = Box::pin(fut);
|
||||
loop {
|
||||
match Pin::as_mut(&mut fut).poll(&mut cx) {
|
||||
Poll::Ready(v) => return v,
|
||||
Poll::Pending => std::thread::yield_now(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 4. 修改草稿
|
||||
{
|
||||
let mut d = draft.draft_mut();
|
||||
d.enable_auto_launch = Some(false);
|
||||
d.enable_tun_mode = Some(true);
|
||||
#[test]
|
||||
fn test_draft_basic_flow() {
|
||||
let verge = IVerge {
|
||||
enable_auto_launch: Some(true),
|
||||
enable_tun_mode: Some(false),
|
||||
};
|
||||
let draft = Draft::new(verge);
|
||||
|
||||
// 读取正式数据(data_arc)
|
||||
{
|
||||
let data = draft.data_arc();
|
||||
assert_eq!(data.enable_auto_launch, Some(true));
|
||||
assert_eq!(data.enable_tun_mode, Some(false));
|
||||
}
|
||||
|
||||
// 修改草稿(使用 edit_draft)
|
||||
draft.edit_draft(|d| {
|
||||
d.enable_auto_launch = Some(false);
|
||||
d.enable_tun_mode = Some(true);
|
||||
});
|
||||
|
||||
// 正式数据未变
|
||||
{
|
||||
let data = draft.data_arc();
|
||||
assert_eq!(data.enable_auto_launch, Some(true));
|
||||
assert_eq!(data.enable_tun_mode, Some(false));
|
||||
}
|
||||
|
||||
// 草稿已变
|
||||
{
|
||||
let latest = draft.latest_arc();
|
||||
assert_eq!(latest.enable_auto_launch, Some(false));
|
||||
assert_eq!(latest.enable_tun_mode, Some(true));
|
||||
}
|
||||
|
||||
// 提交草稿
|
||||
draft.apply();
|
||||
|
||||
// 正式数据已更新
|
||||
{
|
||||
let data = draft.data_arc();
|
||||
assert_eq!(data.enable_auto_launch, Some(false));
|
||||
assert_eq!(data.enable_tun_mode, Some(true));
|
||||
}
|
||||
|
||||
// 新一轮草稿并修改
|
||||
draft.edit_draft(|d| {
|
||||
d.enable_auto_launch = Some(true);
|
||||
});
|
||||
{
|
||||
let latest = draft.latest_arc();
|
||||
assert_eq!(latest.enable_auto_launch, Some(true));
|
||||
assert_eq!(latest.enable_tun_mode, Some(true));
|
||||
}
|
||||
|
||||
// 丢弃草稿
|
||||
draft.discard();
|
||||
|
||||
// 丢弃后再次创建草稿,会从已提交重新 clone
|
||||
{
|
||||
draft.edit_draft(|d| {
|
||||
// 原 committed 是 enable_auto_launch = Some(false)
|
||||
assert_eq!(d.enable_auto_launch, Some(false));
|
||||
// 再修改一下
|
||||
d.enable_tun_mode = Some(false);
|
||||
});
|
||||
// 草稿中值已修改,但正式数据仍是 apply 后的值
|
||||
let data = draft.data_arc();
|
||||
assert_eq!(data.enable_auto_launch, Some(false));
|
||||
assert_eq!(data.enable_tun_mode, Some(true));
|
||||
}
|
||||
}
|
||||
|
||||
// 正式数据未变
|
||||
assert_eq!(draft.data_mut().enable_auto_launch, Some(true));
|
||||
assert_eq!(draft.data_mut().enable_tun_mode, Some(false));
|
||||
#[test]
|
||||
fn test_arc_pointer_behavior_on_edit_and_apply() {
|
||||
let draft = Draft::new(IVerge {
|
||||
enable_auto_launch: Some(true),
|
||||
enable_tun_mode: Some(false),
|
||||
});
|
||||
|
||||
// 草稿已变
|
||||
{
|
||||
let latest = draft.latest_ref();
|
||||
assert_eq!(latest.enable_auto_launch, Some(false));
|
||||
// 初始 latest == committed
|
||||
let committed = draft.data_arc();
|
||||
let latest = draft.latest_arc();
|
||||
assert!(std::sync::Arc::ptr_eq(&committed, &latest));
|
||||
|
||||
// 第一次 edit:由于与 committed 共享,Arc::make_mut 会克隆
|
||||
draft.edit_draft(|d| d.enable_tun_mode = Some(true));
|
||||
let committed_after_first_edit = draft.data_arc();
|
||||
let draft_after_first_edit = draft.latest_arc();
|
||||
assert!(!std::sync::Arc::ptr_eq(
|
||||
&committed_after_first_edit,
|
||||
&draft_after_first_edit
|
||||
));
|
||||
// 提交会把 committed 指向草稿的 Arc
|
||||
let prev_draft_ptr = std::sync::Arc::as_ptr(&draft_after_first_edit);
|
||||
draft.apply();
|
||||
let committed_after_apply = draft.data_arc();
|
||||
assert_eq!(
|
||||
std::sync::Arc::as_ptr(&committed_after_apply),
|
||||
prev_draft_ptr
|
||||
);
|
||||
|
||||
// 第二次编辑:此时草稿唯一持有(无其它引用),不应再克隆
|
||||
// 获取草稿 Arc 的指针并立即丢弃本地引用,避免增加 strong_count
|
||||
draft.edit_draft(|d| d.enable_auto_launch = Some(false));
|
||||
let latest1 = draft.latest_arc();
|
||||
let latest1_ptr = std::sync::Arc::as_ptr(&latest1);
|
||||
drop(latest1); // 确保只有 Draft 内部持有草稿 Arc
|
||||
|
||||
// 再次编辑(unique,Arc::make_mut 不应克隆)
|
||||
draft.edit_draft(|d| d.enable_tun_mode = Some(false));
|
||||
let latest2 = draft.latest_arc();
|
||||
let latest2_ptr = std::sync::Arc::as_ptr(&latest2);
|
||||
|
||||
assert_eq!(latest1_ptr, latest2_ptr, "Unique edit should not clone Arc");
|
||||
assert_eq!(latest2.enable_auto_launch, Some(false));
|
||||
assert_eq!(latest2.enable_tun_mode, Some(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_discard_restores_latest_to_committed() {
|
||||
let draft = Draft::new(IVerge {
|
||||
enable_auto_launch: Some(false),
|
||||
enable_tun_mode: Some(false),
|
||||
});
|
||||
|
||||
// 创建草稿并修改
|
||||
draft.edit_draft(|d| d.enable_auto_launch = Some(true));
|
||||
let committed = draft.data_arc();
|
||||
let latest = draft.latest_arc();
|
||||
assert!(!std::sync::Arc::ptr_eq(&committed, &latest));
|
||||
|
||||
// 丢弃草稿后 latest 应回到 committed
|
||||
draft.discard();
|
||||
let committed2 = draft.data_arc();
|
||||
let latest2 = draft.latest_arc();
|
||||
assert!(std::sync::Arc::ptr_eq(&committed2, &latest2));
|
||||
assert_eq!(latest2.enable_auto_launch, Some(false));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_edit_draft_returns_closure_result() {
|
||||
let draft = Draft::new(IVerge::default());
|
||||
let ret = draft.edit_draft(|d| {
|
||||
d.enable_tun_mode = Some(true);
|
||||
123usize
|
||||
});
|
||||
assert_eq!(ret, 123);
|
||||
let latest = draft.latest_arc();
|
||||
assert_eq!(latest.enable_tun_mode, Some(true));
|
||||
}
|
||||
|
||||
// 5. 提交草稿
|
||||
draft.apply();
|
||||
#[test]
|
||||
fn test_with_data_modify_ok_and_replaces_committed() {
|
||||
let draft = Draft::new(IVerge {
|
||||
enable_auto_launch: Some(false),
|
||||
enable_tun_mode: Some(false),
|
||||
});
|
||||
|
||||
// 正式数据已更新
|
||||
{
|
||||
let data = draft.data_mut();
|
||||
assert_eq!(data.enable_auto_launch, Some(false));
|
||||
assert_eq!(data.enable_tun_mode, Some(true));
|
||||
// 使用 with_data_modify 异步(立即就绪)地更新 committed
|
||||
let res = block_on_ready(draft.with_data_modify(|mut v| async move {
|
||||
v.enable_auto_launch = Some(true);
|
||||
Ok((Box::new(*v), "done")) // Dereference v to get Box<T>
|
||||
}));
|
||||
assert_eq!(
|
||||
{
|
||||
#[allow(clippy::unwrap_used)]
|
||||
res.unwrap()
|
||||
},
|
||||
"done"
|
||||
);
|
||||
|
||||
let committed = draft.data_arc();
|
||||
assert_eq!(committed.enable_auto_launch, Some(true));
|
||||
assert_eq!(committed.enable_tun_mode, Some(false));
|
||||
}
|
||||
|
||||
// 6. 新建并修改下一轮草稿
|
||||
{
|
||||
let mut d = draft.draft_mut();
|
||||
d.enable_auto_launch = Some(true);
|
||||
#[test]
|
||||
fn test_with_data_modify_error_propagation() {
|
||||
let draft = Draft::new(IVerge::default());
|
||||
|
||||
#[allow(clippy::unwrap_used)]
|
||||
let err = block_on_ready(draft.with_data_modify(|v| async move {
|
||||
drop(v);
|
||||
Err::<(Box<IVerge>, ()), _>(anyhow!("boom"))
|
||||
}))
|
||||
.unwrap_err();
|
||||
|
||||
assert_eq!(format!("{err}"), "boom");
|
||||
}
|
||||
assert_eq!(draft.draft_mut().enable_auto_launch, Some(true));
|
||||
|
||||
// 7. 丢弃草稿
|
||||
draft.discard();
|
||||
#[test]
|
||||
fn test_with_data_modify_does_not_touch_existing_draft() {
|
||||
let draft = Draft::new(IVerge {
|
||||
enable_auto_launch: Some(false),
|
||||
enable_tun_mode: Some(false),
|
||||
});
|
||||
|
||||
// 8. 草稿已被丢弃,新的 draft_mut() 会重新 clone
|
||||
assert_eq!(draft.draft_mut().enable_auto_launch, Some(false));
|
||||
// 创建草稿并修改
|
||||
draft.edit_draft(|d| {
|
||||
d.enable_auto_launch = Some(true);
|
||||
d.enable_tun_mode = Some(true);
|
||||
});
|
||||
let draft_before = draft.latest_arc();
|
||||
let draft_before_ptr = std::sync::Arc::as_ptr(&draft_before);
|
||||
|
||||
// 同时通过 with_data_modify 修改 committed
|
||||
#[allow(clippy::unwrap_used)]
|
||||
block_on_ready(draft.with_data_modify(|mut v| async move {
|
||||
v.enable_auto_launch = Some(false); // 与草稿不同
|
||||
Ok((Box::new(*v), ())) // Dereference v to get Box<T>
|
||||
}))
|
||||
.unwrap();
|
||||
|
||||
// 草稿应保持不变
|
||||
let draft_after = draft.latest_arc();
|
||||
assert_eq!(
|
||||
std::sync::Arc::as_ptr(&draft_after),
|
||||
draft_before_ptr,
|
||||
"Existing draft should not be replaced by with_data_modify"
|
||||
);
|
||||
assert_eq!(draft_after.enable_auto_launch, Some(true));
|
||||
assert_eq!(draft_after.enable_tun_mode, Some(true));
|
||||
|
||||
// 丢弃草稿后 latest == committed,且 committed 为异步修改结果
|
||||
draft.discard();
|
||||
let latest = draft.latest_arc();
|
||||
assert_eq!(latest.enable_auto_launch, Some(false));
|
||||
assert_eq!(latest.enable_tun_mode, Some(false));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ pub fn get_supported_languages() -> Vec<String> {
|
||||
pub async fn current_language() -> String {
|
||||
Config::verge()
|
||||
.await
|
||||
.latest_ref()
|
||||
.latest_arc()
|
||||
.language
|
||||
.as_deref()
|
||||
.map(String::from)
|
||||
|
||||
@@ -31,7 +31,7 @@ pub async fn init_logger() -> Result<()> {
|
||||
// TODO 提供 runtime 级别实时修改
|
||||
let (log_level, log_max_size, log_max_count) = {
|
||||
let verge_guard = Config::verge().await;
|
||||
let verge = verge_guard.latest_ref();
|
||||
let verge = verge_guard.latest_arc();
|
||||
(
|
||||
verge.get_log_level(),
|
||||
verge.app_log_max_size.unwrap_or(128),
|
||||
@@ -81,7 +81,7 @@ pub async fn init_logger() -> Result<()> {
|
||||
pub async fn sidecar_writer() -> Result<FileLogWriter> {
|
||||
let (log_max_size, log_max_count) = {
|
||||
let verge_guard = Config::verge().await;
|
||||
let verge = verge_guard.latest_ref();
|
||||
let verge = verge_guard.latest_arc();
|
||||
(
|
||||
verge.app_log_max_size.unwrap_or(128),
|
||||
verge.app_log_max_count.unwrap_or(8),
|
||||
@@ -109,7 +109,7 @@ pub async fn sidecar_writer() -> Result<FileLogWriter> {
|
||||
pub async fn service_writer_config() -> Result<WriterConfig> {
|
||||
let (log_max_size, log_max_count) = {
|
||||
let verge_guard = Config::verge().await;
|
||||
let verge = verge_guard.latest_ref();
|
||||
let verge = verge_guard.latest_arc();
|
||||
(
|
||||
verge.app_log_max_size.unwrap_or(128),
|
||||
verge.app_log_max_count.unwrap_or(8),
|
||||
@@ -134,7 +134,7 @@ pub async fn delete_log() -> Result<()> {
|
||||
|
||||
let auto_log_clean = {
|
||||
let verge = Config::verge().await;
|
||||
let verge = verge.latest_ref();
|
||||
let verge = verge.latest_arc();
|
||||
verge.auto_log_clean.unwrap_or(0)
|
||||
};
|
||||
|
||||
@@ -509,7 +509,7 @@ pub async fn startup_script() -> Result<()> {
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
let script_path = {
|
||||
let verge = Config::verge().await;
|
||||
let verge = verge.latest_ref();
|
||||
let verge = verge.latest_arc();
|
||||
verge.startup_script.clone().unwrap_or_else(|| "".into())
|
||||
};
|
||||
|
||||
|
||||
@@ -165,10 +165,10 @@ impl NetworkManager {
|
||||
ProxyType::None => None,
|
||||
ProxyType::Localhost => {
|
||||
let port = {
|
||||
let verge_port = Config::verge().await.latest_ref().verge_mixed_port;
|
||||
let verge_port = Config::verge().await.latest_arc().verge_mixed_port;
|
||||
match verge_port {
|
||||
Some(port) => port,
|
||||
None => Config::clash().await.latest_ref().get_mixed_port(),
|
||||
None => Config::clash().await.latest_arc().get_mixed_port(),
|
||||
}
|
||||
};
|
||||
let proxy_scheme = format!("http://127.0.0.1:{port}");
|
||||
|
||||
@@ -179,7 +179,7 @@ pub(super) async fn refresh_tray_menu() {
|
||||
pub(super) async fn init_window() {
|
||||
let is_silent_start = Config::verge()
|
||||
.await
|
||||
.latest_ref()
|
||||
.latest_arc()
|
||||
.enable_silent_start
|
||||
.unwrap_or(false);
|
||||
#[cfg(target_os = "macos")]
|
||||
|
||||
@@ -22,7 +22,7 @@ pub async fn build_new_window() -> Result<WebviewWindow, String> {
|
||||
let app_handle = handle::Handle::app_handle();
|
||||
|
||||
let config = Config::verge().await;
|
||||
let latest = config.latest_ref();
|
||||
let latest = config.latest_arc();
|
||||
let start_page = latest.start_page.as_deref().unwrap_or("/");
|
||||
|
||||
match tauri::WebviewWindowBuilder::new(
|
||||
|
||||
@@ -89,15 +89,15 @@ pub fn embed_server() {
|
||||
let clash_config = Config::clash().await;
|
||||
|
||||
let pac_content = verge_config
|
||||
.latest_ref()
|
||||
.latest_arc()
|
||||
.pac_file_content
|
||||
.clone()
|
||||
.unwrap_or_else(|| DEFAULT_PAC.into());
|
||||
|
||||
let pac_port = verge_config
|
||||
.latest_ref()
|
||||
.latest_arc()
|
||||
.verge_mixed_port
|
||||
.unwrap_or_else(|| clash_config.latest_ref().get_mixed_port());
|
||||
.unwrap_or_else(|| clash_config.latest_arc().get_mixed_port());
|
||||
|
||||
let pac = warp::path!("commands" / "pac").map(move || {
|
||||
let processed_content = pac_content.replace("%mixed-port%", &format!("{pac_port}"));
|
||||
|
||||
@@ -71,8 +71,8 @@ export const ClashPortViewer = forwardRef<ClashPortViewerRef>((_, ref) => {
|
||||
setOpen(false);
|
||||
showNotice.success("settings.clash.port.messages.saved");
|
||||
},
|
||||
onError: () => {
|
||||
showNotice.error("settings.clash.port.messages.saveFailed");
|
||||
onError: (error) => {
|
||||
showNotice.error("settings.clash.port.messages.saveFailed", error);
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
@@ -70,8 +70,8 @@ export const useClashInfo = () => {
|
||||
|
||||
if (patch["redir-port"]) {
|
||||
const port = patch["redir-port"];
|
||||
if (port < 1111) {
|
||||
throw new Error("The port should not < 1111");
|
||||
if (port < 1000) {
|
||||
throw new Error("The port should not < 1000");
|
||||
}
|
||||
if (port > 65536) {
|
||||
throw new Error("The port should not > 65536");
|
||||
@@ -80,8 +80,8 @@ export const useClashInfo = () => {
|
||||
|
||||
if (patch["tproxy-port"]) {
|
||||
const port = patch["tproxy-port"];
|
||||
if (port < 1111) {
|
||||
throw new Error("The port should not < 1111");
|
||||
if (port < 1000) {
|
||||
throw new Error("The port should not < 1000");
|
||||
}
|
||||
if (port > 65536) {
|
||||
throw new Error("The port should not > 65536");
|
||||
@@ -90,8 +90,8 @@ export const useClashInfo = () => {
|
||||
|
||||
if (patch["mixed-port"]) {
|
||||
const port = patch["mixed-port"];
|
||||
if (port < 1111) {
|
||||
throw new Error("The port should not < 1111");
|
||||
if (port < 1000) {
|
||||
throw new Error("The port should not < 1000");
|
||||
}
|
||||
if (port > 65536) {
|
||||
throw new Error("The port should not > 65536");
|
||||
@@ -100,8 +100,8 @@ export const useClashInfo = () => {
|
||||
|
||||
if (patch["socks-port"]) {
|
||||
const port = patch["socks-port"];
|
||||
if (port < 1111) {
|
||||
throw new Error("The port should not < 1111");
|
||||
if (port < 1000) {
|
||||
throw new Error("The port should not < 1000");
|
||||
}
|
||||
if (port > 65536) {
|
||||
throw new Error("The port should not > 65536");
|
||||
@@ -110,8 +110,8 @@ export const useClashInfo = () => {
|
||||
|
||||
if (patch["port"]) {
|
||||
const port = patch["port"];
|
||||
if (port < 1111) {
|
||||
throw new Error("The port should not < 1111");
|
||||
if (port < 1000) {
|
||||
throw new Error("The port should not < 1000");
|
||||
}
|
||||
if (port > 65536) {
|
||||
throw new Error("The port should not > 65536");
|
||||
|
||||
2
src/types/types.d.ts
vendored
2
src/types/types.d.ts
vendored
@@ -801,7 +801,7 @@ interface IVergeConfig {
|
||||
common_tray_icon?: boolean;
|
||||
sysproxy_tray_icon?: boolean;
|
||||
tun_tray_icon?: boolean;
|
||||
enable_tray_speed?: boolean;
|
||||
// enable_tray_speed?: boolean;
|
||||
// enable_tray_icon?: boolean;
|
||||
tray_inline_proxy_groups?: boolean;
|
||||
enable_tun_mode?: boolean;
|
||||
|
||||
Reference in New Issue
Block a user