Compare commits

..
Author SHA1 Message Date
Tunglies d1e3f09f17 perf(tray): batch menu rebuild into a single dispatch
Every menu API call runs on the main thread; invoked from a worker,
each call pays a blocking round-trip there and back (channel, event
loop hop, recv). Rebuilding the tray menu with proxy groups enabled
issued one round-trip per menu item — 200+ for a typical subscription —
and the per-call wait grows whenever the event loop is busy with
webview work.

Gather the async inputs (verge/clash/profiles snapshots, proxies and
runtime group order) on the caller's task, then build the menu and
attach it in a single run_on_main_thread closure where every item
constructs inline. Measured on a standalone tauri bench (5 groups x
40 nodes): a rebuild drops from ~6.2ms to ~1.6ms on an idle event
loop and from ~11.4ms to ~1.6ms with the loop busy (8ms of work per
16ms frame), with ~450 fewer allocations (~155KB) per rebuild.
2026-10-21 15:44:39 +08:00
45 changed files with 954 additions and 843 deletions
+2 -2
View File
@@ -4,8 +4,8 @@
"features": { "features": {
"ghcr.io/devcontainers/features/node:2": { "ghcr.io/devcontainers/features/node:2": {
"version": "24.22.1", "version": "24.22.0",
"pnpmVersion": "12.15.0" "pnpmVersion": "12.14.0"
}, },
"ghcr.io/devcontainers/features/rust:1": { "ghcr.io/devcontainers/features/rust:1": {
"version": "1.99.1", "version": "1.99.1",
-33
View File
@@ -1,33 +0,0 @@
# Issue Guidelines / Issue 提交指引
Use these suggestions to make reports easier to assess. State what you observed
and what you could not verify; unknown details are fine.
以下建议有助于维护者评估问题。请说明实际观察到的现象和无法确认的部分;未知信息可以留空。
- Search existing issues by symptom, conditions, and outcome. Add new evidence
to an existing report when it describes the same problem.
按现象、条件和结果搜索已有 Issue。若描述的是同一问题,请在原 Issue 中补充新证据。
- Keep each issue to one distinct user problem or requested outcome. Split
unrelated findings instead of bundling an audit or checklist.
每个 Issue 聚焦一个独立的问题或需求结果;无关发现请拆开,不要把审计清单打包成一个 Issue。
- For bugs, include the version, operating system, steps, and actual versus
expected behavior when known. A root-cause diagnosis is not required. Share
only relevant logs and redact tokens or personal information.
反馈 Bug 时,尽可能提供版本、操作系统、复现步骤及实际与预期行为。无需先判断根因;只提供相关日志,并遮盖令牌和个人信息。
- For feature requests, describe the user task, current limitation, desired
outcome, and affected platforms. An implementation proposal is optional.
提交功能请求时,请描述用户要完成的任务、当前限制、期望结果和适用平台;实现方案不是必填项。
- Separate observations from guesses. An AI or scanner finding is a hypothesis
until its affected configuration, concrete impact, and supporting evidence
are stated; do not present a list of theoretical risks as confirmed bugs.
区分观察结果和推测。AI 或扫描器的发现应先视为假设;请说明受影响的配置、具体影响和支持证据,不要把理论风险清单写成已确认的 Bug。
- Do not post secrets, personal data, or sensitive exploit details in a public
issue. Ask maintainers how to share sensitive security information safely.
请勿在公开 Issue 中发布密钥、个人信息或敏感利用细节;如涉及安全敏感信息,请先询问维护者如何安全提供。
- AI assistance is welcome; human contributors do not need to disclose it. If
an AI agent or automation creates or co-authors an issue, add a final body line
such as `Assisted by: GPT-5.6 High`. Omit effort when the runtime does not
report it. Disclosure is transparency only, not evidence for or against issue
quality.
欢迎使用 AI 辅助,人工提交者无需披露。如果 Issue 由 AI Agent 或自动化创建或共同撰写,请在正文末尾添加披露行,例如 `Assisted by: GPT-5.6 High`。运行环境未提供 effort 时可省略。披露仅用于提高透明度,不作为 Issue 质量的正面或负面证据。
-4
View File
@@ -10,8 +10,6 @@ body:
value: | value: |
## 在提交问题之前,请确认以下事项: ## 在提交问题之前,请确认以下事项:
提交前可参考 [Issue 提交指引](https://github.com/clash-verge-rev/clash-verge-rev/blob/dev/.github/ISSUE_GUIDELINES.md),说明可观察到的问题和证据;不确定的原因请标为猜测。
1. 请 **确保** 您已经查阅了 [Clash Verge Rev 官方文档](https://clash-verge-rev.github.io/guide/term.html) 以及 [常见问题](https://clash-verge-rev.github.io/faq/windows.html) 1. 请 **确保** 您已经查阅了 [Clash Verge Rev 官方文档](https://clash-verge-rev.github.io/guide/term.html) 以及 [常见问题](https://clash-verge-rev.github.io/faq/windows.html)
2. 请 **确保** [已有的问题](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue) 中没有人提交过相似 issue,否则请在已有的 issue 下进行讨论 2. 请 **确保** [已有的问题](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue) 中没有人提交过相似 issue,否则请在已有的 issue 下进行讨论
3. 请 **务必** 给 issue 填写一个简洁明了的标题,以便他人快速检索 3. 请 **务必** 给 issue 填写一个简洁明了的标题,以便他人快速检索
@@ -21,8 +19,6 @@ body:
## Before submitting the issue, please make sure of the following checklist: ## Before submitting the issue, please make sure of the following checklist:
See the [Issue guidelines](https://github.com/clash-verge-rev/clash-verge-rev/blob/dev/.github/ISSUE_GUIDELINES.md) for how to describe observed behavior and evidence; mark uncertain causes as hypotheses.
1. Please make sure you have read the [Clash Verge Rev official documentation](https://clash-verge-rev.github.io/guide/term.html) and [FAQ](https://clash-verge-rev.github.io/faq/windows.html) 1. Please make sure you have read the [Clash Verge Rev official documentation](https://clash-verge-rev.github.io/guide/term.html) and [FAQ](https://clash-verge-rev.github.io/faq/windows.html)
2. Please make sure there is no similar issue in the [existing issues](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue), otherwise please discuss under the existing issue 2. Please make sure there is no similar issue in the [existing issues](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue), otherwise please discuss under the existing issue
3. Please be sure to fill in a concise and clear title for the issue so that others can quickly search 3. Please be sure to fill in a concise and clear title for the issue so that others can quickly search
@@ -9,16 +9,12 @@ body:
attributes: attributes:
value: | value: |
## 在提交问题之前,请确认以下事项: ## 在提交问题之前,请确认以下事项:
可参考 [Issue 提交指引](https://github.com/clash-verge-rev/clash-verge-rev/blob/dev/.github/ISSUE_GUIDELINES.md),描述用户任务、当前限制和期望结果。
1. 请 **确保** 您已经查阅了 [Clash Verge Rev 官方文档](https://clash-verge-rev.github.io/guide/term.html) 确认软件不存在类似的功能 1. 请 **确保** 您已经查阅了 [Clash Verge Rev 官方文档](https://clash-verge-rev.github.io/guide/term.html) 确认软件不存在类似的功能
2. 请 **确保** [已有的问题](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue) 中没有人提交过相似 issue,否则请在已有的 issue 下进行讨论 2. 请 **确保** [已有的问题](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue) 中没有人提交过相似 issue,否则请在已有的 issue 下进行讨论
3. 请 **务必** 给issue填写一个简洁明了的标题,以便他人快速检索 3. 请 **务必** 给issue填写一个简洁明了的标题,以便他人快速检索
4. 请 **务必** 先下载 [AutoBuild](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/autobuild) 版本测试,确保该功能还未实现 4. 请 **务必** 先下载 [AutoBuild](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/autobuild) 版本测试,确保该功能还未实现
5. 请 **务必** 按照模板规范详细描述问题,否则 issue 将会被关闭 5. 请 **务必** 按照模板规范详细描述问题,否则 issue 将会被关闭
## Before submitting the issue, please make sure of the following checklist: ## Before submitting the issue, please make sure of the following checklist:
See the [Issue guidelines](https://github.com/clash-verge-rev/clash-verge-rev/blob/dev/.github/ISSUE_GUIDELINES.md) for describing the user task, current limitation, and desired outcome.
1. Please make sure you have read the [Clash Verge Rev official documentation](https://clash-verge-rev.github.io/guide/term.html) to confirm that the software does not have similar functions 1. Please make sure you have read the [Clash Verge Rev official documentation](https://clash-verge-rev.github.io/guide/term.html) to confirm that the software does not have similar functions
2. Please make sure there is no similar issue in the [existing issues](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue), otherwise please discuss under the existing issue 2. Please make sure there is no similar issue in the [existing issues](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue), otherwise please discuss under the existing issue
3. Please be sure to fill in a concise and clear title for the issue so that others can quickly search 3. Please be sure to fill in a concise and clear title for the issue so that others can quickly search
+1 -7
View File
@@ -1,12 +1,6 @@
## Problem ## Problem
What breaks or what is missing. Product bug fixes and features require a What breaks or what is missing. Link the issue if one exists (`Fixes #123`).
pre-existing issue; link it here (`Fixes #123` only when this PR fully resolves
it). Maintenance work (such as chore, perf, docs, refactor, test, CI/build,
dependency, style, routine localization, or release changes) does not require
an issue link; link one when it defines the scope. For reverts, link the PR or
commit being reverted. For security-sensitive fixes that cannot be tracked
publicly, follow maintainer-approved private tracking.
## Approach ## Approach
+5 -5
View File
@@ -51,7 +51,7 @@ jobs:
- name: Install Node - name: Install Node
uses: actions/setup-node@v7 uses: actions/setup-node@v7
with: with:
node-version: '24.22.1' node-version: '24.22.0'
- name: Install dependencies - name: Install dependencies
run: pnpm install --frozen-lockfile run: pnpm install --frozen-lockfile
@@ -185,7 +185,7 @@ jobs:
- name: Install Node - name: Install Node
uses: actions/setup-node@v7 uses: actions/setup-node@v7
with: with:
node-version: '24.22.1' node-version: '24.22.0'
cache: 'pnpm' cache: 'pnpm'
- name: Pnpm Cache - name: Pnpm Cache
@@ -282,7 +282,7 @@ jobs:
- name: Install Node - name: Install Node
uses: actions/setup-node@v7 uses: actions/setup-node@v7
with: with:
node-version: '24.22.1' node-version: '24.22.0'
cache: 'pnpm' cache: 'pnpm'
- name: Pnpm Cache - name: Pnpm Cache
@@ -426,7 +426,7 @@ jobs:
- name: Install Node - name: Install Node
uses: actions/setup-node@v7 uses: actions/setup-node@v7
with: with:
node-version: '24.22.1' node-version: '24.22.0'
cache: 'pnpm' cache: 'pnpm'
- name: Pnpm Cache - name: Pnpm Cache
@@ -524,7 +524,7 @@ jobs:
- name: Install Node - name: Install Node
uses: actions/setup-node@v7 uses: actions/setup-node@v7
with: with:
node-version: '24.22.1' node-version: '24.22.0'
- uses: pnpm/action-setup@v6.1.0 - uses: pnpm/action-setup@v6.1.0
name: Install pnpm name: Install pnpm
+1 -1
View File
@@ -43,7 +43,7 @@ jobs:
- name: Install Node - name: Install Node
uses: actions/setup-node@v7 uses: actions/setup-node@v7
with: with:
node-version: '24.22.1' node-version: '24.22.0'
- uses: pnpm/action-setup@v6 - uses: pnpm/action-setup@v6
name: Install pnpm name: Install pnpm
+1 -1
View File
@@ -103,7 +103,7 @@ jobs:
if: github.event.inputs[matrix.input] == 'true' if: github.event.inputs[matrix.input] == 'true'
uses: actions/setup-node@v7 uses: actions/setup-node@v7
with: with:
node-version: '24.22.1' node-version: '24.22.0'
cache: 'pnpm' cache: 'pnpm'
- name: Pnpm Cache - name: Pnpm Cache
+2 -2
View File
@@ -52,7 +52,7 @@ jobs:
- uses: actions/setup-node@v7 - uses: actions/setup-node@v7
if: steps.check_frontend.outputs.frontend == 'true' if: steps.check_frontend.outputs.frontend == 'true'
with: with:
node-version: '24.22.1' node-version: '24.22.0'
cache: 'pnpm' cache: 'pnpm'
- name: Restore pnpm cache - name: Restore pnpm cache
@@ -138,7 +138,7 @@ jobs:
- uses: actions/setup-node@v7 - uses: actions/setup-node@v7
if: steps.check_inputs.outputs.contract == 'true' if: steps.check_inputs.outputs.contract == 'true'
with: with:
node-version: '24.22.1' node-version: '24.22.0'
cache: 'pnpm' cache: 'pnpm'
- run: pnpm install --frozen-lockfile - run: pnpm install --frozen-lockfile
+6 -6
View File
@@ -230,7 +230,7 @@ jobs:
- name: Install Node - name: Install Node
uses: actions/setup-node@v7 uses: actions/setup-node@v7
with: with:
node-version: '24.22.1' node-version: '24.22.0'
- uses: pnpm/action-setup@v6 - uses: pnpm/action-setup@v6
name: Install pnpm name: Install pnpm
@@ -331,7 +331,7 @@ jobs:
- name: Install Node - name: Install Node
uses: actions/setup-node@v7 uses: actions/setup-node@v7
with: with:
node-version: '24.22.1' node-version: '24.22.0'
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v6 uses: pnpm/action-setup@v6
@@ -466,7 +466,7 @@ jobs:
- name: Install Node - name: Install Node
uses: actions/setup-node@v7 uses: actions/setup-node@v7
with: with:
node-version: '24.22.1' node-version: '24.22.0'
- uses: pnpm/action-setup@v6 - uses: pnpm/action-setup@v6
name: Install pnpm name: Install pnpm
@@ -547,7 +547,7 @@ jobs:
- name: Install Node - name: Install Node
uses: actions/setup-node@v7 uses: actions/setup-node@v7
with: with:
node-version: '24.22.1' node-version: '24.22.0'
- uses: pnpm/action-setup@v6 - uses: pnpm/action-setup@v6
name: Install pnpm name: Install pnpm
@@ -573,7 +573,7 @@ jobs:
- name: Install Node - name: Install Node
uses: actions/setup-node@v7 uses: actions/setup-node@v7
with: with:
node-version: '24.22.1' node-version: '24.22.0'
- uses: pnpm/action-setup@v6 - uses: pnpm/action-setup@v6
name: Install pnpm name: Install pnpm
@@ -638,7 +638,7 @@ jobs:
- name: Install Node - name: Install Node
uses: actions/setup-node@v7 uses: actions/setup-node@v7
with: with:
node-version: '24.22.1' node-version: '24.22.0'
- uses: pnpm/action-setup@v6 - uses: pnpm/action-setup@v6
name: Install pnpm name: Install pnpm
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
- name: Install Node - name: Install Node
uses: actions/setup-node@v7 uses: actions/setup-node@v7
with: with:
node-version: '24.22.1' node-version: '24.22.0'
- name: Install pnpm - name: Install pnpm
uses: pnpm/action-setup@v6 uses: pnpm/action-setup@v6
+1 -1
View File
@@ -34,7 +34,7 @@ jobs:
- name: Install Node - name: Install Node
uses: actions/setup-node@v7 uses: actions/setup-node@v7
with: with:
node-version: '24.22.1' node-version: '24.22.0'
- uses: pnpm/action-setup@v6 - uses: pnpm/action-setup@v6
name: Install pnpm name: Install pnpm
+2 -2
View File
@@ -15,7 +15,7 @@ jobs:
- name: Install Node - name: Install Node
uses: actions/setup-node@v7 uses: actions/setup-node@v7
with: with:
node-version: '24.22.1' node-version: '24.22.0'
- uses: pnpm/action-setup@v6 - uses: pnpm/action-setup@v6
name: Install pnpm name: Install pnpm
@@ -39,7 +39,7 @@ jobs:
- name: Install Node - name: Install Node
uses: actions/setup-node@v7 uses: actions/setup-node@v7
with: with:
node-version: '24.22.1' node-version: '24.22.0'
- uses: pnpm/action-setup@v6 - uses: pnpm/action-setup@v6
name: Install pnpm name: Install pnpm
+2 -2
View File
@@ -1,3 +1,3 @@
nodejs 24.22.1 nodejs 24.22.0
pnpm 12.15.0 pnpm 12.14.0
rust 1.99.1 rust 1.99.1
+21 -25
View File
@@ -18,25 +18,22 @@ These rules apply to every change, whether human- or agent-authored. They match
the ownership evidence the AI-slop review evaluates (see the ownership evidence the AI-slop review evaluates (see
[`pr-ai-slop-review.md`](.github/workflows/pr-ai-slop-review.md)). [`pr-ai-slop-review.md`](.github/workflows/pr-ai-slop-review.md)).
1. **Issue first.** Product bug fixes and features require a pre-existing issue; 1. **Issue first.** Non-trivial changes require a pre-existing issue describing
self-scoped maintenance work does not. Follow the PR template for linkage and the problem. If none exists, ask the maintainers to open or approve one
revert references. If a required issue is missing, ask maintainers to open or before implementing.
approve one before implementing. For security-sensitive fixes that cannot be
tracked publicly, follow maintainer-approved private tracking.
2. **Scope discipline.** Every changed file must be justifiable from the linked 2. **Scope discipline.** Every changed file must be justifiable from the linked
issue or the stated PR scope. No drive-by refactors, renames, formatting issue. No drive-by refactors, renames, formatting churn, or dependency bumps
churn, or dependency bumps unrelated to the work. unrelated to the problem being fixed.
3. **Author accountability.** AI assistance is welcome, but the contributor owns 3. **Author accountability.** AI assistance is welcome, but the contributor owns
the result: understand the change, describe the problem and approach in your the result: understand the change, describe the problem and approach in your
own words, and verify the change against the reported behavior before own words, and verify the change against the reported behavior before
submitting. submitting.
4. **Tests are justified, not default.** Do not add tests, test scaffolding, or 4. **Tests are justified, not default.** Do not add tests, test scaffolding, or
speculative defensive code unless the linked issue or stated task demands speculative defensive code unless the linked issue demands them. When a test
them. When a test is genuinely necessary — it reproduces the reported is genuinely necessary — it reproduces the reported regression or guards
regression or guards behavior whose breakage would otherwise go unnoticed — behavior whose breakage would otherwise go unnoticed — keep it minimal and
keep it minimal and state in the PR body why it is needed. Bulk test files state in the PR body why it is needed. Bulk test files and defensive
and defensive programming for hypothetical failure modes are PR bloat, not programming for hypothetical failure modes are PR bloat, not rigor.
rigor.
5. **Comments state constraints, not narration.** Write a comment only for a 5. **Comments state constraints, not narration.** Write a comment only for a
non-obvious constraint the code cannot express; never restate what the code non-obvious constraint the code cannot express; never restate what the code
does. does.
@@ -47,12 +44,13 @@ the ownership evidence the AI-slop review evaluates (see
real evidence instead: reproduction steps, failure output, targeted tests. real evidence instead: reproduction steps, failure output, targeted tests.
8. **Minimal diffs.** Match the surrounding code's style, naming, and comment 8. **Minimal diffs.** Match the surrounding code's style, naming, and comment
density. Do not introduce new dependencies or restructure working code unless density. Do not introduce new dependencies or restructure working code unless
the linked issue or stated task demands it. the issue demands it.
9. **Disclose AI automation.** When an agent creates or co-creates a PR or Issue, 9. **Disclose AI automation.** When an agent produces or co-produces a change,
append a final body line with the model and effort used (e.g. append a footer line to the PR body with the model and effort used (e.g.
`Assisted by: GPT-5.6 High`). Effort may be omitted when the runtime does not `Assisted by: GPT-5.6 High`). The PR template intentionally omits this line —
report it. Human contributors are not asked to disclose AI assistance. the agent adds it itself, humans are not asked to declare anything. Effort may
Disclosure is transparency only and carries no positive or negative weight. be omitted when the runtime does not report it. Disclosure is transparency
only; it does not substitute for any rule above.
10. **Compiled workflows.** The AI-slop review policy in 10. **Compiled workflows.** The AI-slop review policy in
[pr-ai-slop-review.md](.github/workflows/pr-ai-slop-review.md) is compiled: [pr-ai-slop-review.md](.github/workflows/pr-ai-slop-review.md) is compiled:
after editing it, run `gh aw compile` and commit the regenerated after editing it, run `gh aw compile` and commit the regenerated
@@ -61,10 +59,8 @@ the ownership evidence the AI-slop review evaluates (see
[`template/Changelog.md`](template/Changelog.md): one line per [`template/Changelog.md`](template/Changelog.md): one line per
user-visible change, no internals. user-visible change, no internals.
## Issue and PR Workflows ## Pull Request Shape
When drafting, triaging, splitting, or implementing from a community issue, use Describe three things, briefly: the problem (with issue link), why this approach
the conditional checklist in [`.github/ISSUE_GUIDELINES.md`](.github/ISSUE_GUIDELINES.md). solves it, and what changed. See
Do not make GitHub issue changes without explicit authorization. For PRs, follow [`PULL_REQUEST_TEMPLATE.md`](.github/PULL_REQUEST_TEMPLATE.md).
the directly loaded [PR template](.github/PULL_REQUEST_TEMPLATE.md); do not
duplicate its prompts here.
Generated
+160 -73
View File
@@ -1119,7 +1119,7 @@ dependencies = [
"getrandom 0.4.3", "getrandom 0.4.3",
"libc", "libc",
"log", "log",
"minisign-verify", "minisign-verify 0.3.0",
"nanoid", "nanoid",
"network-interface", "network-interface",
"objc2", "objc2",
@@ -4170,6 +4170,12 @@ dependencies = [
"unicase", "unicase",
] ]
[[package]]
name = "minisign-verify"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22f9645cb765ea72b8111f36c522475d2daa0d22c957a9826437e97534bc4e9e"
[[package]] [[package]]
name = "minisign-verify" name = "minisign-verify"
version = "0.3.0" version = "0.3.0"
@@ -5993,13 +5999,12 @@ dependencies = [
[[package]] [[package]]
name = "rust-ini" name = "rust-ini"
version = "0.21.1" version = "0.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e310ef0e1b6eeb79169a1171daf9abcb87a2e17c03bee2c4bb100b55c75409f" checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"ordered-multimap", "ordered-multimap",
"trim-in-place",
] ]
[[package]] [[package]]
@@ -6620,6 +6625,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e362d9935bc50f019969e2f9ecd66786612daae13e8f277be7bfb66e8bed3f7" checksum = "1e362d9935bc50f019969e2f9ecd66786612daae13e8f277be7bfb66e8bed3f7"
dependencies = [ dependencies = [
"libc", "libc",
"sigchld",
"windows-sys 0.60.2", "windows-sys 0.60.2",
] ]
@@ -6639,6 +6645,17 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "sigchld"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47106eded3c154e70176fc83df9737335c94ce22f821c32d17ed1db1f83badb1"
dependencies = [
"libc",
"os_pipe",
"signal-hook 0.3.18",
]
[[package]] [[package]]
name = "signal-hook" name = "signal-hook"
version = "0.1.17" version = "0.1.17"
@@ -6649,6 +6666,16 @@ dependencies = [
"signal-hook-registry", "signal-hook-registry",
] ]
[[package]]
name = "signal-hook"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]] [[package]]
name = "signal-hook-registry" name = "signal-hook-registry"
version = "1.4.8" version = "1.4.8"
@@ -6854,12 +6881,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "static_vcruntime"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff7589a1859b09232522bb9edf076ca7fcf14ded96685d3fa629db73023ffbb7"
[[package]] [[package]]
name = "string_cache" name = "string_cache"
version = "0.9.0" version = "0.9.0"
@@ -6919,9 +6940,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]] [[package]]
name = "swift-rs" name = "swift-rs"
version = "1.0.9" version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cefd87076fd385308ee4aff597256c902f1fbb4d031de1558efc12f8ffefadd9" checksum = "e45c444e496845d3f2a351146bff59aae4975b2280238df1dfaa0c7d1846f38e"
dependencies = [ dependencies = [
"base64 0.21.7", "base64 0.21.7",
"serde", "serde",
@@ -7168,9 +7189,9 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
[[package]] [[package]]
name = "tauri" name = "tauri"
version = "2.12.4" version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f316751027fc2644221d1a448564d519efb07067fda584879d9cda966391d212" checksum = "ed99ee9694a2deb776d91cae48ac7411ddfc89ecae2f9b5041111d8c88f2ace9"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@@ -7216,16 +7237,16 @@ dependencies = [
"tray-icon", "tray-icon",
"url", "url",
"webkit2gtk", "webkit2gtk",
"webview2-com", "webview2-com 0.39.1",
"window-vibrancy", "window-vibrancy",
"windows 0.62.2", "windows 0.62.2",
] ]
[[package]] [[package]]
name = "tauri-build" name = "tauri-build"
version = "2.7.3" version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "641dd5fd297c99ab92717b0428cca5b9c6f960b2ac96ca131b3eebc9d70b8ddc" checksum = "59563ca5b331fd97f27ee9672ec8d36d33bb3c9e2c9f6710a1a5687256052e45"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cargo_toml", "cargo_toml",
@@ -7237,8 +7258,6 @@ dependencies = [
"semver 1.0.28", "semver 1.0.28",
"serde", "serde",
"serde_json", "serde_json",
"static_vcruntime",
"strsim",
"tauri-utils", "tauri-utils",
"tauri-winres", "tauri-winres",
"walkdir", "walkdir",
@@ -7246,11 +7265,11 @@ dependencies = [
[[package]] [[package]]
name = "tauri-codegen" name = "tauri-codegen"
version = "2.7.3" version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6585c5b536c00f039d12ce70a9755172d5423bbcfd3969863baa70314c3e02f" checksum = "e50213e463086ecc283d7e9f51c30cc0fd304ad457425158018be26dbf0d0a80"
dependencies = [ dependencies = [
"base64 0.23.1", "base64 0.22.1",
"brotli", "brotli",
"ico", "ico",
"json-patch", "json-patch",
@@ -7273,9 +7292,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-macros" name = "tauri-macros"
version = "2.7.3" version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e83a0cfa1bae376e8fbbdc0b1692707aed50cd6072ce85cae4789af5d6c19f" checksum = "8129fb43bb6e0d6b73ad3315cd172beaab17cd28899c594b1b32c80edbbfd99c"
dependencies = [ dependencies = [
"heck 0.5.0", "heck 0.5.0",
"proc-macro2", "proc-macro2",
@@ -7331,9 +7350,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-clipboard-manager" name = "tauri-plugin-clipboard-manager"
version = "2.5.0" version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9be5ed8d94c33afe8377e885119069f6508780659271d0a25cfc54d441f2bc81" checksum = "333d3b6beeb1c2c7fc040233d4ea7b6c03a9f8f039592b7400833c43fb5b568a"
dependencies = [ dependencies = [
"arboard", "arboard",
"log", "log",
@@ -7346,9 +7365,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-deep-link" name = "tauri-plugin-deep-link"
version = "2.6.2" version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b3c64e24ebcbc63aaa2d88a9c51e2507084b46620baf39ecb0baa0738a08bb4" checksum = "4ac7a92a46ab5c88f44532ca50906d6e448a948d4ddf8c5376ab125e3260f736"
dependencies = [ dependencies = [
"dunce", "dunce",
"plist", "plist",
@@ -7394,9 +7413,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-dialog" name = "tauri-plugin-dialog"
version = "2.8.3" version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7461aaacf1901915ea7b9f5cd74f8224a351ed96af0a62c07fce934003cb34b" checksum = "daf9a5c92e39bdd84f22f6be9230e39d0c4e140345f71ffe6058dca4f71127cf"
dependencies = [ dependencies = [
"log", "log",
"raw-window-handle", "raw-window-handle",
@@ -7412,9 +7431,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-fs" name = "tauri-plugin-fs"
version = "2.7.1" version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d58eb1d4984e535f47a642ffec09a2da6f5c18a12b5902d12126cb7626d628b" checksum = "466eb32466fe5021b6728b06862f9ff14a9f7e1af890047654e03a96d7c0e1bb"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"dunce", "dunce",
@@ -7436,9 +7455,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-global-shortcut" name = "tauri-plugin-global-shortcut"
version = "2.4.1" version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93ff17919fe09852d269bd37b1d3d2e993b9dbb514afe7acbf3346c1d3627e2d" checksum = "0bd2e1f725891a1613af8f25bf918ba3357654b0fd3f2d794c0ce76cbdd92ed2"
dependencies = [ dependencies = [
"global-hotkey", "global-hotkey",
"log", "log",
@@ -7451,9 +7470,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-http" name = "tauri-plugin-http"
version = "2.8.3" version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c57e3451553f95a1db3bfed6a358a77ce5080b6399a242c6eadac113f97abd2" checksum = "93e9d6b3319e101bc1be27cfa0fb4054d7784a12509c67ead0033b82180e5ed1"
dependencies = [ dependencies = [
"bytes", "bytes",
"cookie_store", "cookie_store",
@@ -7470,7 +7489,7 @@ dependencies = [
"thiserror 2.0.20", "thiserror 2.0.20",
"tokio", "tokio",
"url", "url",
"urlpattern", "urlpattern 0.3.0",
] ]
[[package]] [[package]]
@@ -7500,9 +7519,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-notification" name = "tauri-plugin-notification"
version = "2.7.0" version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b939d7b19c8af1381a72ef8474b3aba2b56c1ddac2c23f0c8e96abbcf86b198b" checksum = "533770889abb60a0407d5a32096182f0e6463f5a3dfaa675be94580aa14070e1"
dependencies = [ dependencies = [
"log", "log",
"notify-rust", "notify-rust",
@@ -7515,14 +7534,13 @@ dependencies = [
"thiserror 2.0.20", "thiserror 2.0.20",
"time", "time",
"url", "url",
"zbus",
] ]
[[package]] [[package]]
name = "tauri-plugin-opener" name = "tauri-plugin-opener"
version = "2.7.2" version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53810be1b4fed22bb6ac3f4cab5a2b80d9391d1af436bda145607532bc0bb219" checksum = "d39404b2572927e0d3ed30a7ca226037f7508d22e79c96ee6ef0d42b6a4b3345"
dependencies = [ dependencies = [
"dunce", "dunce",
"glob", "glob",
@@ -7552,12 +7570,11 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-shell" name = "tauri-plugin-shell"
version = "2.5.0" version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8567d42b2c7264355daa1df415ec77f52aa42e885924c86a969be2e920e05521" checksum = "094bdbc47e5efe36544ba21b3a53ab5c724150236cf55e6c9be46191deb6eab6"
dependencies = [ dependencies = [
"encoding_rs", "encoding_rs",
"libc",
"log", "log",
"open", "open",
"os_pipe", "os_pipe",
@@ -7570,14 +7587,13 @@ dependencies = [
"tauri-plugin", "tauri-plugin",
"thiserror 2.0.20", "thiserror 2.0.20",
"tokio", "tokio",
"windows-sys 0.60.2",
] ]
[[package]] [[package]]
name = "tauri-plugin-updater" name = "tauri-plugin-updater"
version = "2.14.0" version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7259989d4fe11b2bf51f7d671642249b1837c4ae2da1430dc713ae38f98ceeec" checksum = "3cb0b2ea3e85ca287990d3859a29cc5cb18024240fd78f62e027fb7963670f18"
dependencies = [ dependencies = [
"base64 0.22.1", "base64 0.22.1",
"dirs 7.0.0", "dirs 7.0.0",
@@ -7585,9 +7601,8 @@ dependencies = [
"futures-util", "futures-util",
"http", "http",
"infer", "infer",
"libc",
"log", "log",
"minisign-verify", "minisign-verify 0.2.5",
"osakit", "osakit",
"percent-encoding", "percent-encoding",
"reqwest 0.13.5", "reqwest 0.13.5",
@@ -7609,9 +7624,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-wdio-webdriver" name = "tauri-plugin-wdio-webdriver"
version = "1.5.0" version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f76676d510d271d80abcf8b0552987b5b4f0eae5e3f906e9b5660d43c97c62e" checksum = "52c5e97428174a52b7f4357bbc4535fcce52c6a06c8ee7a379502f7cda1b6045"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"axum", "axum",
@@ -7635,9 +7650,9 @@ dependencies = [
"tracing", "tracing",
"uuid", "uuid",
"webkit2gtk", "webkit2gtk",
"webview2-com", "webview2-com 0.38.2",
"windows 0.62.2", "windows 0.61.3",
"windows-core 0.62.2", "windows-core 0.61.2",
] ]
[[package]] [[package]]
@@ -7657,9 +7672,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-runtime" name = "tauri-runtime"
version = "2.12.2" version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c55766d99cea69055904d1735b93c2049302b2e09effd462120deb5581d78e9" checksum = "c2a57a2a9b8c8b7fc00d35b510fe808ded7f9f1b44fc1cdda6aee38632ea5413"
dependencies = [ dependencies = [
"cookie", "cookie",
"dpi", "dpi",
@@ -7676,15 +7691,15 @@ dependencies = [
"thiserror 2.0.20", "thiserror 2.0.20",
"url", "url",
"webkit2gtk", "webkit2gtk",
"webview2-com", "webview2-com 0.39.1",
"windows 0.62.2", "windows 0.62.2",
] ]
[[package]] [[package]]
name = "tauri-runtime-wry" name = "tauri-runtime-wry"
version = "2.12.2" version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d61892587aea2a85245bcaec98c6fe46268874c0a0deae5535fb71ca7b93eae" checksum = "7f9e7e0c9f80a5130773cfabb6df4dc924518fb1b2c923726966804634b57d22"
dependencies = [ dependencies = [
"gtk", "gtk",
"http", "http",
@@ -7702,7 +7717,7 @@ dependencies = [
"tracing", "tracing",
"url", "url",
"webkit2gtk", "webkit2gtk",
"webview2-com", "webview2-com 0.39.1",
"windows 0.62.2", "windows 0.62.2",
"wry", "wry",
] ]
@@ -7740,9 +7755,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-utils" name = "tauri-utils"
version = "2.10.2" version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "362ab21cd4cc713f2a02b1a9b5688198b1541cfb6907c4281e9190e0f0ef4aa3" checksum = "2ff55a614843b9a3f010f211df637c94df2607175b8d0f6e0f4a7c19e18621ee"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"brotli", "brotli",
@@ -7771,7 +7786,7 @@ dependencies = [
"thiserror 2.0.20", "thiserror 2.0.20",
"toml 1.1.6+spec-1.1.0", "toml 1.1.6+spec-1.1.0",
"url", "url",
"urlpattern", "urlpattern 0.6.0",
"uuid", "uuid",
"walkdir", "walkdir",
] ]
@@ -7890,7 +7905,7 @@ dependencies = [
"regex", "regex",
"semver 0.11.0", "semver 0.11.0",
"sha2 0.9.9", "sha2 0.9.9",
"signal-hook", "signal-hook 0.1.17",
"terminfo", "terminfo",
"termios", "termios",
"thiserror 1.0.69", "thiserror 1.0.69",
@@ -8488,12 +8503,6 @@ dependencies = [
"petgraph", "petgraph",
] ]
[[package]]
name = "trim-in-place"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daa60874b45b168f67f3f9ba1ee821ad77144d6fa19cef1cd1874a7450a948da"
[[package]] [[package]]
name = "triomphe" name = "triomphe"
version = "0.1.16" version = "0.1.16"
@@ -8586,6 +8595,47 @@ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.61.2",
] ]
[[package]]
name = "unic-char-property"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"
dependencies = [
"unic-char-range",
]
[[package]]
name = "unic-char-range"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"
[[package]]
name = "unic-common"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
[[package]]
name = "unic-ucd-ident"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987"
dependencies = [
"unic-char-property",
"unic-char-range",
"unic-ucd-version",
]
[[package]]
name = "unic-ucd-version"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"
dependencies = [
"unic-common",
]
[[package]] [[package]]
name = "unicase" name = "unicase"
version = "2.9.0" version = "2.9.0"
@@ -8645,6 +8695,18 @@ dependencies = [
"serde_derive", "serde_derive",
] ]
[[package]]
name = "urlpattern"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d"
dependencies = [
"regex",
"serde",
"unic-ucd-ident",
"url",
]
[[package]] [[package]]
name = "urlpattern" name = "urlpattern"
version = "0.6.0" version = "0.6.0"
@@ -9036,6 +9098,20 @@ dependencies = [
"rustls-pki-types", "rustls-pki-types",
] ]
[[package]]
name = "webview2-com"
version = "0.38.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a"
dependencies = [
"webview2-com-macros",
"webview2-com-sys 0.38.2",
"windows 0.61.3",
"windows-core 0.61.2",
"windows-implement",
"windows-interface",
]
[[package]] [[package]]
name = "webview2-com" name = "webview2-com"
version = "0.39.1" version = "0.39.1"
@@ -9043,7 +9119,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f89fca7a704cee10dcb3654c1dbb8941d1783132f1917358af75bec37a7d7e6" checksum = "3f89fca7a704cee10dcb3654c1dbb8941d1783132f1917358af75bec37a7d7e6"
dependencies = [ dependencies = [
"webview2-com-macros", "webview2-com-macros",
"webview2-com-sys", "webview2-com-sys 0.39.1",
"windows 0.62.2", "windows 0.62.2",
"windows-core 0.62.2", "windows-core 0.62.2",
] ]
@@ -9059,6 +9135,17 @@ dependencies = [
"syn 2.0.119", "syn 2.0.119",
] ]
[[package]]
name = "webview2-com-sys"
version = "0.38.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c"
dependencies = [
"thiserror 2.0.20",
"windows 0.61.3",
"windows-core 0.61.2",
]
[[package]] [[package]]
name = "webview2-com-sys" name = "webview2-com-sys"
version = "0.39.1" version = "0.39.1"
@@ -9798,7 +9885,7 @@ dependencies = [
"url", "url",
"webkit2gtk", "webkit2gtk",
"webkit2gtk-sys", "webkit2gtk-sys",
"webview2-com", "webview2-com 0.39.1",
"windows 0.62.2", "windows 0.62.2",
"windows-core 0.62.2", "windows-core 0.62.2",
"windows-version", "windows-version",
+2 -2
View File
@@ -61,8 +61,8 @@ tracing-estuary = { git = "https://github.com/Tunglies/tracing-estuary.git", bra
tauri-plugin-clash-verge-sysinfo = { path = "crates/tauri-plugin-clash-verge-sysinfo" } tauri-plugin-clash-verge-sysinfo = { path = "crates/tauri-plugin-clash-verge-sysinfo" }
tracing = "0.1.44" tracing = "0.1.44"
tauri = { version = "2.12.4" } tauri = { version = "2.12.1" }
tauri-plugin-clipboard-manager = "2.5.0" tauri-plugin-clipboard-manager = "2.4.1"
parking_lot = { version = "0.12.5", features = ["hardware-lock-elision"] } parking_lot = { version = "0.12.5", features = ["hardware-lock-elision"] }
anyhow = "1.0.104" anyhow = "1.0.104"
chrono = "0.4.45" chrono = "0.4.45"
-2
View File
@@ -17,8 +17,6 @@
<details> <details>
<summary><strong> ✨ 新增功能 </strong></summary> <summary><strong> ✨ 新增功能 </strong></summary>
- 新增虚拟网卡设置的「DNS 模式」选项,支持配置 mihomo 系统 DNS 行为
- 新增「IP 信息」自动刷新开关,关闭后仍可手动刷新
</details> </details>
+13 -13
View File
@@ -54,13 +54,13 @@
"@mui/icons-material": "^9.4.0", "@mui/icons-material": "^9.4.0",
"@mui/material": "^9.4.0", "@mui/material": "^9.4.0",
"@tanstack/react-virtual": "^3.14.13", "@tanstack/react-virtual": "^3.14.13",
"@tauri-apps/api": "2.12.4", "@tauri-apps/api": "2.12.1",
"@tauri-apps/plugin-clipboard-manager": "2.5.0", "@tauri-apps/plugin-clipboard-manager": "2.4.0",
"@tauri-apps/plugin-dialog": "2.8.3", "@tauri-apps/plugin-dialog": "2.8.0",
"@tauri-apps/plugin-fs": "2.7.1", "@tauri-apps/plugin-fs": "2.6.0",
"@tauri-apps/plugin-http": "2.8.3", "@tauri-apps/plugin-http": "2.8.0",
"@tauri-apps/plugin-opener": "2.7.2", "@tauri-apps/plugin-opener": "2.7.0",
"@tauri-apps/plugin-updater": "2.14.0", "@tauri-apps/plugin-updater": "2.13.1",
"ahooks": "^3.10.0", "ahooks": "^3.10.0",
"cidr-block": "^2.3.0", "cidr-block": "^2.3.0",
"compare-versions": "^6.1.1", "compare-versions": "^6.1.1",
@@ -79,7 +79,7 @@
"react-dom": "19.3.1", "react-dom": "19.3.1",
"react-error-boundary": "6.1.6", "react-error-boundary": "6.1.6",
"react-hook-form": "^7.88.0", "react-hook-form": "^7.88.0",
"react-i18next": "17.1.8", "react-i18next": "17.0.19",
"react-markdown": "10.1.0", "react-markdown": "10.1.0",
"react-router": "^8.4.0", "react-router": "^8.4.0",
"rehype-raw": "^7.0.0", "rehype-raw": "^7.0.0",
@@ -95,7 +95,7 @@
"@biomejs/biome": "^2.5.14", "@biomejs/biome": "^2.5.14",
"@eslint-react/eslint-plugin": "^5.20.5", "@eslint-react/eslint-plugin": "^5.20.5",
"@eslint/js": "^10.0.1", "@eslint/js": "^10.0.1",
"@tauri-apps/cli": "2.12.3", "@tauri-apps/cli": "2.12.1",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/node": "^24.13.6", "@types/node": "^24.13.6",
"@types/react": "19.3.0", "@types/react": "19.3.0",
@@ -103,7 +103,7 @@
"@types/validator": "^13.15.10", "@types/validator": "^13.15.10",
"@vitejs/plugin-legacy": "^8.2.3", "@vitejs/plugin-legacy": "^8.2.3",
"@vitejs/plugin-react": "^6.1.1", "@vitejs/plugin-react": "^6.1.1",
"@wdio/tauri-service": "1.5.0", "@wdio/tauri-service": "1.4.0",
"adm-zip": "^0.6.1", "adm-zip": "^0.6.1",
"axios": "^1.20.0", "axios": "^1.20.0",
"cli-color": "^2.0.4", "cli-color": "^2.0.4",
@@ -123,7 +123,7 @@
"https-proxy-agent": "^9.1.0", "https-proxy-agent": "^9.1.0",
"husky": "^9.1.7", "husky": "^9.1.7",
"jiti": "^2.7.0", "jiti": "^2.7.0",
"knip": "6.43.0", "knip": "6.41.0",
"lint-staged": "^17.5.1", "lint-staged": "^17.5.1",
"sass": "^1.104.1", "sass": "^1.104.1",
"tar": "^7.5.22", "tar": "^7.5.22",
@@ -133,7 +133,7 @@
"vite": "^8.3.0", "vite": "^8.3.0",
"vite-plugin-svgr": "^5.2.0", "vite-plugin-svgr": "^5.2.0",
"vitest": "^5.0.1", "vitest": "^5.0.1",
"webdriverio": "10.1.0", "webdriverio": "10.0.2",
"ws": "8.22.0" "ws": "8.22.0"
}, },
"lint-staged": { "lint-staged": {
@@ -146,5 +146,5 @@
] ]
}, },
"type": "module", "type": "module",
"packageManager": "pnpm@12.15.0" "packageManager": "pnpm@12.14.0"
} }
+615 -411
View File
File diff suppressed because it is too large Load Diff
+3 -4
View File
@@ -434,13 +434,12 @@ for (let iteration = 0; iteration < repeat && !interrupted; iteration++) {
devicePixelRatio, devicePixelRatio,
]) ])
run.viewport = { dom: viewport, driver: await browser.getWindowRect() } run.viewport = { dom: viewport, driver: await browser.getWindowRect() }
const driverScale = run.viewport.driver.width / 1000
assert( assert(
run.viewport.driver.width / viewport[2] === 1000 &&
run.viewport.driver.height / viewport[2] === 700 &&
viewport[0] === 1000 && viewport[0] === 1000 &&
viewport[1] > 0 && viewport[1] > 0 &&
viewport[1] <= 700 && viewport[1] <= 700,
(driverScale === 1 || driverScale === viewport[2]) &&
run.viewport.driver.height / driverScale === 700,
`unexpected window dimensions: ${JSON.stringify(run.viewport)}`, `unexpected window dimensions: ${JSON.stringify(run.viewport)}`,
) )
assert( assert(
+11 -11
View File
@@ -29,7 +29,7 @@ perf-harness = ["dep:tauri-plugin-wdio-webdriver"]
identifier = "io.github.clash-verge-rev.clash-verge-rev" identifier = "io.github.clash-verge-rev.clash-verge-rev"
[build-dependencies] [build-dependencies]
tauri-build = { version = "2.7.3", features = [] } tauri-build = { version = "2.7.1", features = [] }
[dependencies] [dependencies]
clash-verge-draft = { workspace = true } clash-verge-draft = { workspace = true }
@@ -72,12 +72,12 @@ sysproxy = { git = "https://github.com/clash-verge-rev/sysproxy-rs.git", branch
"guard", "guard",
] } ] }
network-interface = { version = "2.0.5", features = ["serde"] } network-interface = { version = "2.0.5", features = ["serde"] }
tauri-plugin-shell = "2.5.0" tauri-plugin-shell = "2.4.0"
tauri-plugin-opener = "2.7.2" tauri-plugin-opener = "2.7.0"
tauri-plugin-dialog = "2.8.3" tauri-plugin-dialog = "2.8.1"
tauri-plugin-fs = "2.7.1" tauri-plugin-fs = "2.6.0"
tauri-plugin-process = "2.4.0" tauri-plugin-process = "2.4.0"
tauri-plugin-deep-link = "2.6.2" tauri-plugin-deep-link = "2.6.1"
tauri-plugin-window-state = "2.5.0" tauri-plugin-window-state = "2.5.0"
zip = "9.0.4" zip = "9.0.4"
flate2 = "1.1.10" flate2 = "1.1.10"
@@ -90,10 +90,10 @@ getrandom = "0.4.3"
futures = "0.3.34" futures = "0.3.34"
gethostname = "1.1.0" gethostname = "1.1.0"
scopeguard = "1.2.0" scopeguard = "1.2.0"
tauri-plugin-notification = "2.7.0" tauri-plugin-notification = "2.5.1"
tokio-stream = "0.1.19" tokio-stream = "0.1.19"
backon = { version = "1.6.0", features = ["tokio-sleep"] } backon = { version = "1.6.0", features = ["tokio-sleep"] }
tauri-plugin-http = "2.8.3" tauri-plugin-http = "2.8.0"
tracing = { workspace = true } tracing = { workspace = true }
tauri-plugin-devtools = { version = "2.2.2", optional = true } tauri-plugin-devtools = { version = "2.2.2", optional = true }
tauri-plugin-mihomo = { git = "https://github.com/clash-verge-rev/tauri-plugin-mihomo", rev = "7a03d16f4d39b1bb885b7babe66b9e153e34b615" } tauri-plugin-mihomo = { git = "https://github.com/clash-verge-rev/tauri-plugin-mihomo", rev = "7a03d16f4d39b1bb885b7babe66b9e153e34b615" }
@@ -120,7 +120,7 @@ libc = "0.2.191"
clash-verge-signal = { workspace = true } clash-verge-signal = { workspace = true }
[target.'cfg(target_os = "macos")'.dependencies] [target.'cfg(target_os = "macos")'.dependencies]
tauri-plugin-wdio-webdriver = { version = "=1.5.0", optional = true } tauri-plugin-wdio-webdriver = { version = "=1.4.0", optional = true }
objc2 = "0.6.5" objc2 = "0.6.5"
objc2-foundation = { version = "0.3.2", features = [ objc2-foundation = { version = "0.3.2", features = [
"NSString", "NSString",
@@ -153,8 +153,8 @@ windows-sys.workspace = true
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
tauri-plugin-autostart = "2.7.0" tauri-plugin-autostart = "2.7.0"
tauri-plugin-global-shortcut = "2.4.1" tauri-plugin-global-shortcut = "2.4.0"
tauri-plugin-updater = "2.14.0" tauri-plugin-updater = "2.13.1"
[dev-dependencies] [dev-dependencies]
criterion = { workspace = true } criterion = { workspace = true }
+4 -9
View File
@@ -1,9 +1,10 @@
fn main() -> Result<(), Box<dyn std::error::Error>> { fn main() -> Result<(), Box<dyn std::error::Error>> {
if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows") if std::env::var("CARGO_CFG_TARGET_OS").as_deref() == Ok("windows")
&& std::env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("msvc") && std::env::var("CARGO_CFG_TARGET_ENV").as_deref() == Ok("msvc")
&& std::env::var_os("CARGO_FEATURE_CLIPPY").is_some()
{ {
// Tauri's resource is linked to the app binary, but not to Rust unit-test harnesses or examples. // Tauri's resource is linked to the app binary, but not to Rust unit-test harnesses.
// Give non-app targets their own manifest so tests and examples can resolve TaskDialogIndirect. // Give mock-context builds their own manifest so tests can resolve TaskDialogIndirect.
let out_dir = std::env::var_os("OUT_DIR").ok_or_else(|| std::io::Error::other("OUT_DIR is not set"))?; let out_dir = std::env::var_os("OUT_DIR").ok_or_else(|| std::io::Error::other("OUT_DIR is not set"))?;
let manifest_path = std::path::PathBuf::from(out_dir).join("windows-test-manifest.xml"); let manifest_path = std::path::PathBuf::from(out_dir).join("windows-test-manifest.xml");
std::fs::write( std::fs::write(
@@ -24,14 +25,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
</assembly> </assembly>
"#, "#,
)?; )?;
let manifest_display = manifest_path.display();
if std::env::var_os("CARGO_FEATURE_CLIPPY").is_some() {
println!("cargo:rustc-link-arg=/MANIFEST:EMBED"); println!("cargo:rustc-link-arg=/MANIFEST:EMBED");
println!("cargo:rustc-link-arg=/MANIFESTINPUT:{manifest_display}"); println!("cargo:rustc-link-arg=/MANIFESTINPUT:{}", manifest_path.display());
} else {
println!("cargo:rustc-link-arg-examples=/MANIFEST:EMBED");
println!("cargo:rustc-link-arg-examples=/MANIFESTINPUT:{manifest_display}");
}
} }
#[cfg(feature = "clippy")] #[cfg(feature = "clippy")]
-1
View File
@@ -75,7 +75,6 @@ pub mod tun {
"auto-redirect", "auto-redirect",
"auto-detect-interface", "auto-detect-interface",
"dns-hijack", "dns-hijack",
"dns-mode",
"strict-route", "strict-route",
"mtu", "mtu",
]; ];
+86 -77
View File
@@ -1,4 +1,4 @@
use crate::config::{IProfilePreview, IVerge}; use crate::config::{IProfilePreview, IProfiles, IVerge};
use crate::core::lightweight; use crate::core::lightweight;
use crate::core::tray::menu_def::TrayAction; use crate::core::tray::menu_def::TrayAction;
use crate::process::AsyncHandler; use crate::process::AsyncHandler;
@@ -41,7 +41,10 @@ const TRAY_CLICK_DEBOUNCE_MS: u64 = 300;
pub const TRAY_ID: &str = "clash-verge-rev-tray"; pub const TRAY_ID: &str = "clash-verge-rev-tray";
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
struct TrayMenuOptions { struct TrayMenuOptions<'a> {
mode: Option<&'a str>,
system_proxy_enabled: bool,
tun_mode_enabled: bool,
is_lightweight_mode: bool, is_lightweight_mode: bool,
include_proxy_groups: bool, include_proxy_groups: bool,
} }
@@ -201,9 +204,8 @@ impl Tray {
}; };
let verge = Config::verge().await.latest_arc(); let verge = Config::verge().await.latest_arc();
let system_proxy = verge.enable_system_proxy.as_ref().unwrap_or(&false); let system_proxy = verge.enable_system_proxy.unwrap_or(false);
let tun_mode = verge.enable_tun_mode.as_ref().unwrap_or(&false); let tun_mode = verge.enable_tun_mode.unwrap_or(false);
let tun_mode_available = crate::core::runstate::RUN_STATE.state().tun_capable();
let mode = { let mode = {
Config::clash() Config::clash()
.await .await
@@ -214,29 +216,73 @@ impl Tray {
.unwrap_or("rule") .unwrap_or("rule")
.to_owned() .to_owned()
}; };
let profiles_config = Config::profiles().await;
let profiles_arc = profiles_config.latest_arc();
let profiles_preview = profiles_arc.profiles_preview().unwrap_or_default();
let is_lightweight_mode = is_in_lightweight_mode(); let is_lightweight_mode = is_in_lightweight_mode();
logging_error!( let fetch_proxy_groups =
Type::Tray, include_proxy_groups && verge.tray_proxy_groups_display_mode.as_deref() != Some("disable");
tray.set_menu(Some(
create_tray_menu( // TODO: should update tray menu again when it was timeout error
app_handle, let (proxy_nodes_data, runtime_proxy_groups_order) = if fetch_proxy_groups {
Some(mode.as_str()), let proxy_nodes_data =
*system_proxy, tokio::time::timeout(Duration::from_millis(1000), handle::Handle::mihomo().get_proxies())
*tun_mode, .await
tun_mode_available, .map_or(None, |res| res.ok());
profiles_preview,
let runtime = Config::runtime().await.latest_arc();
let runtime_proxy_groups_order = runtime.config.as_ref().map(|config| {
config
.get("proxy-groups")
.and_then(|groups| groups.as_sequence())
.map(|groups| {
groups
.iter()
.filter_map(|group| group.get("name"))
.filter_map(|name| name.as_str())
.enumerate()
.map(|(index, name)| (name.into(), index))
.collect::<HashMap<String, usize>>()
})
.unwrap_or_default()
});
(proxy_nodes_data, runtime_proxy_groups_order)
} else {
(None, None)
};
let verge = if fetch_proxy_groups {
Config::verge().await.latest_arc()
} else {
verge
};
let profiles = Config::profiles().await.latest_arc();
let (tx, rx) = tokio::sync::oneshot::channel();
let dispatch_handle = app_handle.clone();
app_handle
.run_on_main_thread(move || {
let _ = tx.send((|| -> Result<()> {
let menu = build_tray_menu(
&dispatch_handle,
TrayMenuOptions { TrayMenuOptions {
mode: Some(mode.as_str()),
system_proxy_enabled: system_proxy,
tun_mode_enabled: tun_mode,
is_lightweight_mode, is_lightweight_mode,
include_proxy_groups, include_proxy_groups,
}, },
) &verge,
.await?, &profiles,
)) runtime_proxy_groups_order,
); proxy_nodes_data,
)?;
logging_error!(Type::Tray, tray.set_menu(Some(menu)));
Ok(())
})());
})
.map_err(|e| anyhow::anyhow!("Failed to dispatch tray menu update: {e}"))?;
rx.await
.map_err(|_| anyhow::anyhow!("Tray menu update returned no result"))??;
logging!(debug, Type::Tray, "托盘菜单更新成功"); logging!(debug, Type::Tray, "托盘菜单更新成功");
Ok(()) Ok(())
@@ -422,11 +468,12 @@ fn create_hotkeys(hotkeys: &Option<Vec<String>>) -> HashMap<&str, &str> {
let mut parts = item.split(','); let mut parts = item.split(',');
match (parts.next(), parts.next()) { match (parts.next(), parts.next()) {
(Some(func), Some(key)) => { (Some(func), Some(key)) => {
// 托盘菜单的 `accelerator` 在 Linux/Windows 下不支持小键盘按键,Windows 下也不支持 Win 键(前端上报为 `CMD`) // 托盘菜单中的 `accelerator` 属性,在 Linux/Windows 中都不支持小键盘按键的解析
let uppercase_key = key.to_uppercase(); if key.to_uppercase().contains("NUMPAD") {
let unsupported = None
uppercase_key.contains("NUMPAD") || (cfg!(windows) && uppercase_key.contains("CMD")); } else {
if unsupported { None } else { Some((func, key)) } Some((func, key))
}
} }
_ => None, _ => None,
} }
@@ -584,53 +631,16 @@ fn create_proxy_menu_item(
Ok((proxies_submenu, inline_proxy_items)) Ok((proxies_submenu, inline_proxy_items))
} }
async fn create_tray_menu( fn build_tray_menu(
app_handle: &AppHandle, app_handle: &AppHandle,
mode: Option<&str>, options: TrayMenuOptions<'_>,
system_proxy_enabled: bool, verge_settings: &IVerge,
tun_mode_enabled: bool, profiles_config: &IProfiles,
tun_mode_available: bool, proxy_group_order_map: Option<HashMap<String, usize>>,
profiles_preview: Vec<IProfilePreview<'_>>, proxy_nodes_data: Option<Proxies>,
options: TrayMenuOptions,
) -> Result<tauri::menu::Menu<Wry>> { ) -> Result<tauri::menu::Menu<Wry>> {
let current_proxy_mode = mode.unwrap_or(""); let current_proxy_mode = options.mode.unwrap_or("");
let tun_mode_available = crate::core::runstate::RUN_STATE.state().tun_capable();
let mut verge_settings = Config::verge().await.latest_arc();
let fetch_proxy_groups =
options.include_proxy_groups && verge_settings.tray_proxy_groups_display_mode.as_deref() != Some("disable");
// TODO: should update tray menu again when it was timeout error
let (proxy_nodes_data, runtime_proxy_groups_order) = if fetch_proxy_groups {
let proxy_nodes_data =
tokio::time::timeout(Duration::from_millis(1000), handle::Handle::mihomo().get_proxies())
.await
.map_or(None, |res| res.ok());
let runtime = Config::runtime().await.latest_arc();
let runtime_proxy_groups_order = runtime.config.as_ref().map(|config| {
config
.get("proxy-groups")
.and_then(|groups| groups.as_sequence())
.map(|groups| {
groups
.iter()
.filter_map(|group| group.get("name"))
.filter_map(|name| name.as_str())
.enumerate()
.map(|(index, name)| (name.into(), index))
.collect::<HashMap<String, usize>>()
})
.unwrap_or_default()
});
(proxy_nodes_data, runtime_proxy_groups_order)
} else {
(None, None)
};
if fetch_proxy_groups {
verge_settings = Config::verge().await.latest_arc();
}
let tray_proxy_groups_display_mode = verge_settings let tray_proxy_groups_display_mode = verge_settings
.tray_proxy_groups_display_mode .tray_proxy_groups_display_mode
@@ -638,15 +648,14 @@ async fn create_tray_menu(
.unwrap_or("default"); .unwrap_or("default");
let include_proxy_groups = options.include_proxy_groups && tray_proxy_groups_display_mode != "disable"; let include_proxy_groups = options.include_proxy_groups && tray_proxy_groups_display_mode != "disable";
let proxy_group_order_map = runtime_proxy_groups_order;
let show_outbound_modes_inline = verge_settings.tray_inline_outbound_modes.unwrap_or(false); let show_outbound_modes_inline = verge_settings.tray_inline_outbound_modes.unwrap_or(false);
let version = env!("CARGO_PKG_VERSION"); let version = env!("CARGO_PKG_VERSION");
let hotkeys = create_hotkeys(&verge_settings.hotkeys); let hotkeys = create_hotkeys(&verge_settings.hotkeys);
let profile_menu_items: Vec<CheckMenuItem<Wry>> = create_profile_menu_item(app_handle, profiles_preview)?; let profile_menu_items: Vec<CheckMenuItem<Wry>> =
create_profile_menu_item(app_handle, profiles_config.profiles_preview().unwrap_or_default())?;
let texts = MenuTexts::new(); let texts = MenuTexts::new();
let profile_menu_items_refs: Vec<&dyn IsMenuItem<Wry>> = profile_menu_items let profile_menu_items_refs: Vec<&dyn IsMenuItem<Wry>> = profile_menu_items
@@ -737,7 +746,7 @@ async fn create_tray_menu(
MenuIds::SYSTEM_PROXY, MenuIds::SYSTEM_PROXY,
&texts.system_proxy, &texts.system_proxy,
true, true,
system_proxy_enabled, options.system_proxy_enabled,
hotkeys.get("toggle_system_proxy").copied(), hotkeys.get("toggle_system_proxy").copied(),
)?; )?;
@@ -746,7 +755,7 @@ async fn create_tray_menu(
MenuIds::TUN_MODE, MenuIds::TUN_MODE,
&texts.tun_mode, &texts.tun_mode,
tun_mode_available, tun_mode_available,
tun_mode_enabled, options.tun_mode_enabled,
hotkeys.get("toggle_tun_mode").copied(), hotkeys.get("toggle_tun_mode").copied(),
)?; )?;
+5 -33
View File
@@ -4,19 +4,10 @@ import {
VisibilityOffOutlined, VisibilityOffOutlined,
VisibilityOutlined, VisibilityOutlined,
} from '@mui/icons-material' } from '@mui/icons-material'
import { import { Box, Button, IconButton, Skeleton, Typography } from '@mui/material'
Box,
Button,
FormControlLabel,
IconButton,
Skeleton,
Switch,
Typography,
} from '@mui/material'
import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow' import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'
import { useEffect } from 'foxact/use-abortable-effect' import { useEffect } from 'foxact/use-abortable-effect'
import { useIntersection } from 'foxact/use-intersection' import { useIntersection } from 'foxact/use-intersection'
import { useLocalStorage } from 'foxact/use-local-storage'
import type { XOR } from 'foxts/ts-xor' import type { XOR } from 'foxts/ts-xor'
import { import {
forwardRef, forwardRef,
@@ -108,13 +99,9 @@ const IPInfoCardContainer = forwardRef<HTMLElement, React.PropsWithChildren>(
export const IpInfoCard = () => { export const IpInfoCard = () => {
const { t } = useTranslation() const { t } = useTranslation()
const [showIp, setShowIp] = useState(false) const [showIp, setShowIp] = useState(false)
const [autoRefresh, setAutoRefresh] = useLocalStorage(
'ip-info-auto-refresh',
true,
)
const appWindow = useMemo(() => getCurrentWebviewWindow(), []) const appWindow = useMemo(() => getCurrentWebviewWindow(), [])
// Once intersected, the card stays eligible for refresh until unmount. // Once intersected, refreshes stay enabled until unmount.
const [containerRef, hasIntersected, _resetIntersected] = useIntersection({ const [containerRef, hasIntersected, _resetIntersected] = useIntersection({
rootMargin: '0px', rootMargin: '0px',
}) })
@@ -164,8 +151,6 @@ export const IpInfoCard = () => {
}) })
useEffect(() => { useEffect(() => {
if (!autoRefresh) return
let timer: number | null = null let timer: number | null = null
// Prefer DOM intersection state; Tauri visibility is unreliable on some platforms. // Prefer DOM intersection state; Tauri visibility is unreliable on some platforms.
@@ -205,7 +190,7 @@ export const IpInfoCard = () => {
if (timer != null) clearInterval(timer) if (timer != null) clearInterval(timer)
document.removeEventListener('visibilitychange', onVisibilityChange) document.removeEventListener('visibilitychange', onVisibilityChange)
} }
}, [autoRefresh, hasIntersected]) }, [hasIntersected])
const toggleShowIp = useCallback(() => { const toggleShowIp = useCallback(() => {
setShowIp((prev) => !prev) setShowIp((prev) => !prev)
@@ -379,25 +364,12 @@ export const IpInfoCard = () => {
fontSize: '0.7rem', fontSize: '0.7rem',
}} }}
> >
<FormControlLabel
sx={{ m: 0 }}
control={
<Switch
size="small"
checked={autoRefresh}
onChange={(_, checked) => setAutoRefresh(checked)}
/>
}
label={
<Typography variant="caption"> <Typography variant="caption">
{t('home.components.ipInfo.labels.autoRefresh')} {t('home.components.ipInfo.labels.autoRefresh')}
{autoRefresh && {countdown.type === 'countdown'
(countdown.type === 'countdown'
? `: ${countdown.remainingSeconds}s` ? `: ${countdown.remainingSeconds}s`
: '...')} : '...'}
</Typography> </Typography>
}
/>
<Typography <Typography
variant="caption" variant="caption"
sx={{ sx={{
@@ -1,43 +0,0 @@
import { Button, ButtonGroup } from '@mui/material'
interface Props {
value?: string
onChange?: (value: string) => void
}
export const DnsModeSwitch = (props: Props) => {
const { value, onChange } = props
return (
<ButtonGroup size="small" sx={{ my: '4px' }}>
<Button
variant={!value ? 'contained' : 'outlined'}
onClick={() => onChange?.('')}
sx={{ textTransform: 'capitalize' }}
>
Auto
</Button>
<Button
variant={value === 'disabled' ? 'contained' : 'outlined'}
onClick={() => onChange?.('disabled')}
sx={{ textTransform: 'capitalize' }}
>
Disabled
</Button>
<Button
variant={value === 'native' ? 'contained' : 'outlined'}
onClick={() => onChange?.('native')}
sx={{ textTransform: 'capitalize' }}
>
Native
</Button>
<Button
variant={value === 'hijack' ? 'contained' : 'outlined'}
onClick={() => onChange?.('hijack')}
sx={{ textTransform: 'capitalize' }}
>
Hijack
</Button>
</ButtonGroup>
)
}
@@ -26,7 +26,6 @@ import { showNotice } from '@/services/notice-service'
import getSystem from '@/utils/get-system' import getSystem from '@/utils/get-system'
import { areValidIpCidrs } from '@/utils/network' import { areValidIpCidrs } from '@/utils/network'
import { DnsModeSwitch } from './dns-mode-switch'
import { StackModeSwitch } from './stack-mode-switch' import { StackModeSwitch } from './stack-mode-switch'
const OS = getSystem() const OS = getSystem()
@@ -53,7 +52,6 @@ export function TunViewer({ ref }: { ref?: Ref<DialogRef> }) {
dnsHijack: ['any:53'], dnsHijack: ['any:53'],
strictRoute: false, strictRoute: false,
mtu: 1500, mtu: 1500,
dnsMode: '',
}) })
const routeExcludeAddressItems = splitRouteExcludeAddress( const routeExcludeAddressItems = splitRouteExcludeAddress(
@@ -86,7 +84,6 @@ export function TunViewer({ ref }: { ref?: Ref<DialogRef> }) {
dnsHijack: clash?.tun['dns-hijack'] ?? ['any:53'], dnsHijack: clash?.tun['dns-hijack'] ?? ['any:53'],
strictRoute: clash?.tun['strict-route'] ?? false, strictRoute: clash?.tun['strict-route'] ?? false,
mtu: clash?.tun.mtu ?? 1500, mtu: clash?.tun.mtu ?? 1500,
dnsMode: clash?.tun['dns-mode'] ?? '',
}) })
}, },
close: () => setOpen(false), close: () => setOpen(false),
@@ -120,7 +117,6 @@ export function TunViewer({ ref }: { ref?: Ref<DialogRef> }) {
: {}), : {}),
'auto-detect-interface': values.autoDetectInterface, 'auto-detect-interface': values.autoDetectInterface,
'dns-hijack': values.dnsHijack[0] === '' ? [] : values.dnsHijack, 'dns-hijack': values.dnsHijack[0] === '' ? [] : values.dnsHijack,
'dns-mode': values.dnsMode,
'strict-route': values.strictRoute, 'strict-route': values.strictRoute,
mtu: values.mtu ?? 1500, mtu: values.mtu ?? 1500,
} }
@@ -167,7 +163,6 @@ export function TunViewer({ ref }: { ref?: Ref<DialogRef> }) {
'auto-detect-interface': true, 'auto-detect-interface': true,
'dns-hijack': ['any:53'], 'dns-hijack': ['any:53'],
'route-exclude-address': [], 'route-exclude-address': [],
'dns-mode': '',
'strict-route': false, 'strict-route': false,
mtu: 1500, mtu: 1500,
} }
@@ -181,7 +176,6 @@ export function TunViewer({ ref }: { ref?: Ref<DialogRef> }) {
dnsHijack: ['any:53'], dnsHijack: ['any:53'],
strictRoute: false, strictRoute: false,
mtu: 1500, mtu: 1500,
dnsMode: '',
}) })
await patchClash({ tun }) await patchClash({ tun })
await mutateClash( await mutateClash(
@@ -218,25 +212,6 @@ export function TunViewer({ ref }: { ref?: Ref<DialogRef> }) {
/> />
</ListItem> </ListItem>
<ListItem sx={{ padding: '5px 2px' }}>
<ListItemText
primary={t('settings.modals.tun.fields.dnsMode')}
sx={{ maxWidth: 'fit-content' }}
/>
<TooltipIcon
title={t('settings.modals.tun.tooltips.dnsMode')}
sx={{ opacity: 0.7 }}
/>
<Box sx={{ marginLeft: 'auto' }}>
<DnsModeSwitch
value={values.dnsMode}
onChange={(value) => {
setValues((v) => ({ ...v, dnsMode: value }))
}}
/>
</Box>
</ListItem>
<ListItem sx={{ padding: '5px 2px' }}> <ListItem sx={{ padding: '5px 2px' }}>
<ListItemText primary={t('settings.modals.tun.fields.device')} /> <ListItemText primary={t('settings.modals.tun.fields.device')} />
<TextField <TextField
-2
View File
@@ -495,13 +495,11 @@
"strictRoute": "توجيه صارم", "strictRoute": "توجيه صارم",
"autoDetectInterface": "الكشف التلقائي عن الواجهة", "autoDetectInterface": "الكشف التلقائي عن الواجهة",
"dnsHijack": "اختطاف DNS", "dnsHijack": "اختطاف DNS",
"dnsMode": "وضع DNS",
"mtu": "وحدة الإرسال القصوى", "mtu": "وحدة الإرسال القصوى",
"autoRedirect": "Auto Redirect" "autoRedirect": "Auto Redirect"
}, },
"tooltips": { "tooltips": {
"dnsHijack": "Please use , to separate multiple DNS servers", "dnsHijack": "Please use , to separate multiple DNS servers",
"dnsMode": "يحافظ Auto على السلوك القديم؛ يتطلب hijack تفعيل توجيه صارم على Windows أو Auto Redirect على Linux",
"autoRedirect": "Automatically configures nftables/iptables TCP redirects" "autoRedirect": "Automatically configures nftables/iptables TCP redirects"
}, },
"messages": { "messages": {
-2
View File
@@ -495,13 +495,11 @@
"strictRoute": "Strenges Routing", "strictRoute": "Strenges Routing",
"autoDetectInterface": "Netzwerkschnittstelle automatisch auswählen", "autoDetectInterface": "Netzwerkschnittstelle automatisch auswählen",
"dnsHijack": "DNS-Hijacking", "dnsHijack": "DNS-Hijacking",
"dnsMode": "DNS-Modus",
"mtu": "Maximale Übertragungseinheit", "mtu": "Maximale Übertragungseinheit",
"autoRedirect": "Automatische Umleitung" "autoRedirect": "Automatische Umleitung"
}, },
"tooltips": { "tooltips": {
"dnsHijack": "Mehrere DNS-Server durch Kommas trennen", "dnsHijack": "Mehrere DNS-Server durch Kommas trennen",
"dnsMode": "Auto behält das alte Verhalten bei; hijack erfordert Strenges Routing unter Windows oder Automatische Umleitung unter Linux",
"autoRedirect": "Richtet TCP-Umleitungen per nftables/iptables automatisch ein" "autoRedirect": "Richtet TCP-Umleitungen per nftables/iptables automatisch ein"
}, },
"messages": { "messages": {
-2
View File
@@ -495,13 +495,11 @@
"strictRoute": "Strict Route", "strictRoute": "Strict Route",
"autoDetectInterface": "Auto Detect Interface", "autoDetectInterface": "Auto Detect Interface",
"dnsHijack": "DNS Hijack", "dnsHijack": "DNS Hijack",
"dnsMode": "DNS Mode",
"mtu": "Max Transmission Unit", "mtu": "Max Transmission Unit",
"autoRedirect": "Auto Redirect" "autoRedirect": "Auto Redirect"
}, },
"tooltips": { "tooltips": {
"dnsHijack": "Please use , to separate multiple DNS servers", "dnsHijack": "Please use , to separate multiple DNS servers",
"dnsMode": "Auto keeps the legacy behavior; hijack requires Strict Route on Windows or Auto Redirect on Linux",
"autoRedirect": "Automatically configures nftables/iptables TCP redirects" "autoRedirect": "Automatically configures nftables/iptables TCP redirects"
}, },
"messages": { "messages": {
-2
View File
@@ -495,13 +495,11 @@
"strictRoute": "Enrutamiento estricto", "strictRoute": "Enrutamiento estricto",
"autoDetectInterface": "Detectar automáticamente la interfaz de salida del tráfico", "autoDetectInterface": "Detectar automáticamente la interfaz de salida del tráfico",
"dnsHijack": "Secuestro de DNS", "dnsHijack": "Secuestro de DNS",
"dnsMode": "Modo DNS",
"mtu": "Unidad máxima de transmisión", "mtu": "Unidad máxima de transmisión",
"autoRedirect": "Auto Redirect" "autoRedirect": "Auto Redirect"
}, },
"tooltips": { "tooltips": {
"dnsHijack": "Please use , to separate multiple DNS servers", "dnsHijack": "Please use , to separate multiple DNS servers",
"dnsMode": "Auto mantiene el comportamiento anterior; hijack requiere Enrutamiento estricto en Windows o Auto Redirect en Linux",
"autoRedirect": "Automatically configures nftables/iptables TCP redirects" "autoRedirect": "Automatically configures nftables/iptables TCP redirects"
}, },
"messages": { "messages": {
-2
View File
@@ -495,13 +495,11 @@
"strictRoute": "مسیر دقیق", "strictRoute": "مسیر دقیق",
"autoDetectInterface": "تشخیص خودکار رابط", "autoDetectInterface": "تشخیص خودکار رابط",
"dnsHijack": "ربایش DNS", "dnsHijack": "ربایش DNS",
"dnsMode": "حالت DNS",
"mtu": "واحد حداکثر انتقال", "mtu": "واحد حداکثر انتقال",
"autoRedirect": "Auto Redirect" "autoRedirect": "Auto Redirect"
}, },
"tooltips": { "tooltips": {
"dnsHijack": "لطفا برای جدا کردن چندین سرور DNS از , استفاده کنید.", "dnsHijack": "لطفا برای جدا کردن چندین سرور DNS از , استفاده کنید.",
"dnsMode": "Auto رفتار قدیمی را حفظ می‌کند؛ hijack در ویندوز نیاز به مسیر دقیق یا در لینوکس نیاز به Auto Redirect دارد",
"autoRedirect": "پیکربندی خودکار ریدایرکت‌های TCP در nftables/iptables" "autoRedirect": "پیکربندی خودکار ریدایرکت‌های TCP در nftables/iptables"
}, },
"messages": { "messages": {
-2
View File
@@ -495,13 +495,11 @@
"strictRoute": "Rute Ketat", "strictRoute": "Rute Ketat",
"autoDetectInterface": "Deteksi Antarmuka Otomatis", "autoDetectInterface": "Deteksi Antarmuka Otomatis",
"dnsHijack": "Pembajakan DNS", "dnsHijack": "Pembajakan DNS",
"dnsMode": "Mode DNS",
"mtu": "Unit Transmisi Maksimum", "mtu": "Unit Transmisi Maksimum",
"autoRedirect": "Auto Redirect" "autoRedirect": "Auto Redirect"
}, },
"tooltips": { "tooltips": {
"dnsHijack": "Please use , to separate multiple DNS servers", "dnsHijack": "Please use , to separate multiple DNS servers",
"dnsMode": "Auto mempertahankan perilaku lama; hijack memerlukan Rute Ketat di Windows atau Auto Redirect di Linux",
"autoRedirect": "Automatically configures nftables/iptables TCP redirects" "autoRedirect": "Automatically configures nftables/iptables TCP redirects"
}, },
"messages": { "messages": {
-2
View File
@@ -495,13 +495,11 @@
"strictRoute": "厳格なルート", "strictRoute": "厳格なルート",
"autoDetectInterface": "トラフィックの出口インターフェースを自動選択", "autoDetectInterface": "トラフィックの出口インターフェースを自動選択",
"dnsHijack": "DNSハイジャック", "dnsHijack": "DNSハイジャック",
"dnsMode": "DNSモード",
"mtu": "最大転送単位", "mtu": "最大転送単位",
"autoRedirect": "Auto Redirect" "autoRedirect": "Auto Redirect"
}, },
"tooltips": { "tooltips": {
"dnsHijack": "Please use , to separate multiple DNS servers", "dnsHijack": "Please use , to separate multiple DNS servers",
"dnsMode": "Auto は従来の動作を維持します。hijack には Windows で厳格なルートまたは Linux で Auto Redirect が必要です",
"autoRedirect": "Automatically configures nftables/iptables TCP redirects" "autoRedirect": "Automatically configures nftables/iptables TCP redirects"
}, },
"messages": { "messages": {
-2
View File
@@ -495,13 +495,11 @@
"strictRoute": "엄격 라우팅", "strictRoute": "엄격 라우팅",
"autoDetectInterface": "인터페이스 자동 감지", "autoDetectInterface": "인터페이스 자동 감지",
"dnsHijack": "DNS 하이재킹", "dnsHijack": "DNS 하이재킹",
"dnsMode": "DNS 모드",
"mtu": "MTU", "mtu": "MTU",
"autoRedirect": "Auto Redirect" "autoRedirect": "Auto Redirect"
}, },
"tooltips": { "tooltips": {
"dnsHijack": "여러 DNS 서버는 쉼표(,)로 구분하세요", "dnsHijack": "여러 DNS 서버는 쉼표(,)로 구분하세요",
"dnsMode": "Auto는 기존 동작을 유지합니다. hijack은 Windows에서 엄격 라우팅 또는 Linux에서 Auto Redirect가 필요합니다",
"autoRedirect": "Automatically configures nftables/iptables TCP redirects" "autoRedirect": "Automatically configures nftables/iptables TCP redirects"
}, },
"messages": { "messages": {
-2
View File
@@ -495,13 +495,11 @@
"strictRoute": "Rota Estrita", "strictRoute": "Rota Estrita",
"autoDetectInterface": "Detectar Interface Automaticamente", "autoDetectInterface": "Detectar Interface Automaticamente",
"dnsHijack": "Sequestro de DNS", "dnsHijack": "Sequestro de DNS",
"dnsMode": "Modo DNS",
"mtu": "Unidade Máxima de Transmissão", "mtu": "Unidade Máxima de Transmissão",
"autoRedirect": "Redirecionamento Automático" "autoRedirect": "Redirecionamento Automático"
}, },
"tooltips": { "tooltips": {
"dnsHijack": "Use , para separar vários servidores DNS", "dnsHijack": "Use , para separar vários servidores DNS",
"dnsMode": "Auto mantém o comportamento antigo; hijack requer Rota Estrita no Windows ou Redirecionamento Automático no Linux",
"autoRedirect": "Configura automaticamente os redirecionamentos TCP do nftables/iptables" "autoRedirect": "Configura automaticamente os redirecionamentos TCP do nftables/iptables"
}, },
"messages": { "messages": {
-2
View File
@@ -495,13 +495,11 @@
"strictRoute": "Строгая маршрутизация", "strictRoute": "Строгая маршрутизация",
"autoDetectInterface": "Автоопределение интерфейса", "autoDetectInterface": "Автоопределение интерфейса",
"dnsHijack": "Перехват DNS", "dnsHijack": "Перехват DNS",
"dnsMode": "Режим DNS",
"mtu": "MTU", "mtu": "MTU",
"autoRedirect": "Автоматическое перенаправление" "autoRedirect": "Автоматическое перенаправление"
}, },
"tooltips": { "tooltips": {
"dnsHijack": "Используйте запятую для разделения нескольких DNS-серверов", "dnsHijack": "Используйте запятую для разделения нескольких DNS-серверов",
"dnsMode": "Auto сохраняет прежнее поведение; для hijack нужна Строгая маршрутизация в Windows или Автоматическое перенаправление в Linux",
"autoRedirect": "Автоматически настраивает перенаправление TCP через nftables/iptables" "autoRedirect": "Автоматически настраивает перенаправление TCP через nftables/iptables"
}, },
"messages": { "messages": {
-2
View File
@@ -495,13 +495,11 @@
"strictRoute": "Katı Yönlendirme", "strictRoute": "Katı Yönlendirme",
"autoDetectInterface": "Arayüzü Otomatik Algıla", "autoDetectInterface": "Arayüzü Otomatik Algıla",
"dnsHijack": "DNS Ele Geçirme", "dnsHijack": "DNS Ele Geçirme",
"dnsMode": "DNS Modu",
"mtu": "Maksimum İletim Birimi", "mtu": "Maksimum İletim Birimi",
"autoRedirect": "Otomatik Yönlendirme" "autoRedirect": "Otomatik Yönlendirme"
}, },
"tooltips": { "tooltips": {
"dnsHijack": "Birden fazla DNS sunucusunu ayırmak için lütfen , kullanın", "dnsHijack": "Birden fazla DNS sunucusunu ayırmak için lütfen , kullanın",
"dnsMode": "Auto eski davranışı korur; hijack için Windows'ta Katı Yönlendirme veya Linux'ta Otomatik Yönlendirme gerekir",
"autoRedirect": "nftables/iptables TCP yönlendirmelerini otomatik olarak yapılandırır" "autoRedirect": "nftables/iptables TCP yönlendirmelerini otomatik olarak yapılandırır"
}, },
"messages": { "messages": {
-2
View File
@@ -495,13 +495,11 @@
"strictRoute": "Катгый маршрутлау", "strictRoute": "Катгый маршрутлау",
"autoDetectInterface": "Интерфейсны автоматик ачыклау", "autoDetectInterface": "Интерфейсны автоматик ачыклау",
"dnsHijack": "DNS'ны үзгәртеп тоту (hijack)", "dnsHijack": "DNS'ны үзгәртеп тоту (hijack)",
"dnsMode": "DNS режиме",
"mtu": "MTU (макс. тапшыру берәмлеге)", "mtu": "MTU (макс. тапшыру берәмлеге)",
"autoRedirect": "Auto Redirect" "autoRedirect": "Auto Redirect"
}, },
"tooltips": { "tooltips": {
"dnsHijack": "Please use , to separate multiple DNS servers", "dnsHijack": "Please use , to separate multiple DNS servers",
"dnsMode": "Auto иске тәртипне саклый; hijack өчен Windows'ta Катгый маршрутлау яки Linux'ta Auto Redirect кирәк",
"autoRedirect": "Automatically configures nftables/iptables TCP redirects" "autoRedirect": "Automatically configures nftables/iptables TCP redirects"
}, },
"messages": { "messages": {
-2
View File
@@ -495,13 +495,11 @@
"strictRoute": "严格路由", "strictRoute": "严格路由",
"autoDetectInterface": "自动选择流量出口接口", "autoDetectInterface": "自动选择流量出口接口",
"dnsHijack": "DNS 劫持", "dnsHijack": "DNS 劫持",
"dnsMode": "DNS 模式",
"mtu": "最大传输单元", "mtu": "最大传输单元",
"autoRedirect": "自动重定向" "autoRedirect": "自动重定向"
}, },
"tooltips": { "tooltips": {
"dnsHijack": "多个 DNS 服务器请使用 , 分隔", "dnsHijack": "多个 DNS 服务器请使用 , 分隔",
"dnsMode": "Auto 保持旧行为;hijack 需 Windows 开启严格路由或 Linux 开启自动重定向",
"autoRedirect": "自动配置 nftables/iptables 的 TCP 重定向" "autoRedirect": "自动配置 nftables/iptables 的 TCP 重定向"
}, },
"messages": { "messages": {
-2
View File
@@ -495,13 +495,11 @@
"strictRoute": "嚴格路由", "strictRoute": "嚴格路由",
"autoDetectInterface": "自動偵測流量輸出介面", "autoDetectInterface": "自動偵測流量輸出介面",
"dnsHijack": "DNS 綁架", "dnsHijack": "DNS 綁架",
"dnsMode": "DNS 模式",
"mtu": "最大傳輸單位", "mtu": "最大傳輸單位",
"autoRedirect": "自動重導" "autoRedirect": "自動重導"
}, },
"tooltips": { "tooltips": {
"dnsHijack": "請用半形逗號來區隔多個 DNS 伺服器", "dnsHijack": "請用半形逗號來區隔多個 DNS 伺服器",
"dnsMode": "Auto 保持舊行為;hijack 需 Windows 開啟嚴格路由或 Linux 開啟自動重導",
"autoRedirect": "自動配置 nftables/iptables 的 TCP 重導" "autoRedirect": "自動配置 nftables/iptables 的 TCP 重導"
}, },
"messages": { "messages": {
-2
View File
@@ -704,11 +704,9 @@ export const translationKeys = [
'settings.modals.tun.fields.strictRoute', 'settings.modals.tun.fields.strictRoute',
'settings.modals.tun.fields.autoDetectInterface', 'settings.modals.tun.fields.autoDetectInterface',
'settings.modals.tun.fields.dnsHijack', 'settings.modals.tun.fields.dnsHijack',
'settings.modals.tun.fields.dnsMode',
'settings.modals.tun.fields.mtu', 'settings.modals.tun.fields.mtu',
'settings.modals.tun.fields.autoRedirect', 'settings.modals.tun.fields.autoRedirect',
'settings.modals.tun.tooltips.dnsHijack', 'settings.modals.tun.tooltips.dnsHijack',
'settings.modals.tun.tooltips.dnsMode',
'settings.modals.tun.tooltips.autoRedirect', 'settings.modals.tun.tooltips.autoRedirect',
'settings.modals.tun.messages.applied', 'settings.modals.tun.messages.applied',
'settings.modals.tun.messages.invalidRouteExcludeAddress', 'settings.modals.tun.messages.invalidRouteExcludeAddress',
-2
View File
@@ -1190,7 +1190,6 @@ export interface TranslationResources {
autoRoute: string autoRoute: string
device: string device: string
dnsHijack: string dnsHijack: string
dnsMode: string
mtu: string mtu: string
routeExcludeAddress: string routeExcludeAddress: string
stack: string stack: string
@@ -1205,7 +1204,6 @@ export interface TranslationResources {
tooltips: { tooltips: {
autoRedirect: string autoRedirect: string
dnsHijack: string dnsHijack: string
dnsMode: string
} }
} }
update: { update: {
-1
View File
@@ -40,7 +40,6 @@ interface IConfigData {
'auto-redirect'?: boolean 'auto-redirect'?: boolean
'auto-detect-interface': boolean 'auto-detect-interface': boolean
'dns-hijack': string[] 'dns-hijack': string[]
'dns-mode'?: string
'route-exclude-address'?: string[] 'route-exclude-address'?: string[]
'strict-route': boolean 'strict-route': boolean
mtu: number mtu: number