Compare commits

..
Author SHA1 Message Date
Tunglies bf35539ad8 perf(tray): mutate menu state in place, rebuild only on structure change
Rebuilding the tray menu for every state change re-creates every item
even when only checkmarks, delay texts or the selected node moved. Keep
handles into the attached menu and, inside the same main-thread
dispatch, mutate items in place. A signature over topology inputs
(display mode, inline flags, mode, language, hotkeys, profile list)
gates a full rebuild, and a proxy-only signature change swaps just the
group submenus under the persisted Proxies submenu.

State-only updates (mode/system proxy/TUN/lightweight switches,
profile selection, node selection, delay texts) no longer reconstruct
the menu; structural changes still rebuild exactly as before.
2026-10-21 16:46:44 +08:00
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
23 changed files with 826 additions and 510 deletions
+1 -1
View File
@@ -4,7 +4,7 @@
"features": {
"ghcr.io/devcontainers/features/node:2": {
"version": "24.22.1",
"version": "24.22.0",
"pnpmVersion": "12.14.0"
},
"ghcr.io/devcontainers/features/rust: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: |
## 在提交问题之前,请确认以下事项:
提交前可参考 [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)
2. 请 **确保** [已有的问题](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue) 中没有人提交过相似 issue,否则请在已有的 issue 下进行讨论
3. 请 **务必** 给 issue 填写一个简洁明了的标题,以便他人快速检索
@@ -21,8 +19,6 @@ body:
## 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)
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
@@ -9,16 +9,12 @@ body:
attributes:
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) 确认软件不存在类似的功能
2. 请 **确保** [已有的问题](https://github.com/clash-verge-rev/clash-verge-rev/issues?q=is%3Aissue) 中没有人提交过相似 issue,否则请在已有的 issue 下进行讨论
3. 请 **务必** 给issue填写一个简洁明了的标题,以便他人快速检索
4. 请 **务必** 先下载 [AutoBuild](https://github.com/clash-verge-rev/clash-verge-rev/releases/tag/autobuild) 版本测试,确保该功能还未实现
5. 请 **务必** 按照模板规范详细描述问题,否则 issue 将会被关闭
## 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
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
+1 -7
View File
@@ -1,12 +1,6 @@
## Problem
What breaks or what is missing. Product bug fixes and features require a
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.
What breaks or what is missing. Link the issue if one exists (`Fixes #123`).
## Approach
+5 -5
View File
@@ -51,7 +51,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v7
with:
node-version: '24.22.1'
node-version: '24.22.0'
- name: Install dependencies
run: pnpm install --frozen-lockfile
@@ -185,7 +185,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v7
with:
node-version: '24.22.1'
node-version: '24.22.0'
cache: 'pnpm'
- name: Pnpm Cache
@@ -282,7 +282,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v7
with:
node-version: '24.22.1'
node-version: '24.22.0'
cache: 'pnpm'
- name: Pnpm Cache
@@ -426,7 +426,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v7
with:
node-version: '24.22.1'
node-version: '24.22.0'
cache: 'pnpm'
- name: Pnpm Cache
@@ -524,7 +524,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v7
with:
node-version: '24.22.1'
node-version: '24.22.0'
- uses: pnpm/action-setup@v6.1.0
name: Install pnpm
+1 -1
View File
@@ -43,7 +43,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v7
with:
node-version: '24.22.1'
node-version: '24.22.0'
- uses: pnpm/action-setup@v6
name: Install pnpm
+1 -1
View File
@@ -103,7 +103,7 @@ jobs:
if: github.event.inputs[matrix.input] == 'true'
uses: actions/setup-node@v7
with:
node-version: '24.22.1'
node-version: '24.22.0'
cache: 'pnpm'
- name: Pnpm Cache
+2 -2
View File
@@ -52,7 +52,7 @@ jobs:
- uses: actions/setup-node@v7
if: steps.check_frontend.outputs.frontend == 'true'
with:
node-version: '24.22.1'
node-version: '24.22.0'
cache: 'pnpm'
- name: Restore pnpm cache
@@ -138,7 +138,7 @@ jobs:
- uses: actions/setup-node@v7
if: steps.check_inputs.outputs.contract == 'true'
with:
node-version: '24.22.1'
node-version: '24.22.0'
cache: 'pnpm'
- run: pnpm install --frozen-lockfile
+6 -6
View File
@@ -230,7 +230,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v7
with:
node-version: '24.22.1'
node-version: '24.22.0'
- uses: pnpm/action-setup@v6
name: Install pnpm
@@ -331,7 +331,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v7
with:
node-version: '24.22.1'
node-version: '24.22.0'
- name: Install pnpm
uses: pnpm/action-setup@v6
@@ -466,7 +466,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v7
with:
node-version: '24.22.1'
node-version: '24.22.0'
- uses: pnpm/action-setup@v6
name: Install pnpm
@@ -547,7 +547,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v7
with:
node-version: '24.22.1'
node-version: '24.22.0'
- uses: pnpm/action-setup@v6
name: Install pnpm
@@ -573,7 +573,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v7
with:
node-version: '24.22.1'
node-version: '24.22.0'
- uses: pnpm/action-setup@v6
name: Install pnpm
@@ -638,7 +638,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v7
with:
node-version: '24.22.1'
node-version: '24.22.0'
- uses: pnpm/action-setup@v6
name: Install pnpm
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v7
with:
node-version: '24.22.1'
node-version: '24.22.0'
- name: Install pnpm
uses: pnpm/action-setup@v6
+1 -1
View File
@@ -34,7 +34,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v7
with:
node-version: '24.22.1'
node-version: '24.22.0'
- uses: pnpm/action-setup@v6
name: Install pnpm
+2 -2
View File
@@ -15,7 +15,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v7
with:
node-version: '24.22.1'
node-version: '24.22.0'
- uses: pnpm/action-setup@v6
name: Install pnpm
@@ -39,7 +39,7 @@ jobs:
- name: Install Node
uses: actions/setup-node@v7
with:
node-version: '24.22.1'
node-version: '24.22.0'
- uses: pnpm/action-setup@v6
name: Install pnpm
+1 -1
View File
@@ -1,3 +1,3 @@
nodejs 24.22.1
nodejs 24.22.0
pnpm 12.14.0
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
[`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;
self-scoped maintenance work does not. Follow the PR template for linkage and
revert references. If a required issue is missing, ask maintainers to open or
approve one before implementing. For security-sensitive fixes that cannot be
tracked publicly, follow maintainer-approved private tracking.
1. **Issue first.** Non-trivial changes require a pre-existing issue describing
the problem. If none exists, ask the maintainers to open or approve one
before implementing.
2. **Scope discipline.** Every changed file must be justifiable from the linked
issue or the stated PR scope. No drive-by refactors, renames, formatting
churn, or dependency bumps unrelated to the work.
issue. No drive-by refactors, renames, formatting churn, or dependency bumps
unrelated to the problem being fixed.
3. **Author accountability.** AI assistance is welcome, but the contributor owns
the result: understand the change, describe the problem and approach in your
own words, and verify the change against the reported behavior before
submitting.
4. **Tests are justified, not default.** Do not add tests, test scaffolding, or
speculative defensive code unless the linked issue or stated task demands
them. When a test is genuinely necessary — it reproduces the reported
regression or guards behavior whose breakage would otherwise go unnoticed —
keep it minimal and state in the PR body why it is needed. Bulk test files
and defensive programming for hypothetical failure modes are PR bloat, not
rigor.
speculative defensive code unless the linked issue demands them. When a test
is genuinely necessary — it reproduces the reported regression or guards
behavior whose breakage would otherwise go unnoticed — keep it minimal and
state in the PR body why it is needed. Bulk test files and defensive
programming for hypothetical failure modes are PR bloat, not rigor.
5. **Comments state constraints, not narration.** Write a comment only for a
non-obvious constraint the code cannot express; never restate what the code
does.
@@ -47,12 +44,13 @@ the ownership evidence the AI-slop review evaluates (see
real evidence instead: reproduction steps, failure output, targeted tests.
8. **Minimal diffs.** Match the surrounding code's style, naming, and comment
density. Do not introduce new dependencies or restructure working code unless
the linked issue or stated task demands it.
9. **Disclose AI automation.** When an agent creates or co-creates a PR or Issue,
append a final body line with the model and effort used (e.g.
`Assisted by: GPT-5.6 High`). Effort may be omitted when the runtime does not
report it. Human contributors are not asked to disclose AI assistance.
Disclosure is transparency only and carries no positive or negative weight.
the issue demands it.
9. **Disclose AI automation.** When an agent produces or co-produces a change,
append a footer line to the PR body with the model and effort used (e.g.
`Assisted by: GPT-5.6 High`). The PR template intentionally omits this line —
the agent adds it itself, humans are not asked to declare anything. Effort may
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
[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
@@ -61,10 +59,8 @@ the ownership evidence the AI-slop review evaluates (see
[`template/Changelog.md`](template/Changelog.md): one line per
user-visible change, no internals.
## Issue and PR Workflows
## Pull Request Shape
When drafting, triaging, splitting, or implementing from a community issue, use
the conditional checklist in [`.github/ISSUE_GUIDELINES.md`](.github/ISSUE_GUIDELINES.md).
Do not make GitHub issue changes without explicit authorization. For PRs, follow
the directly loaded [PR template](.github/PULL_REQUEST_TEMPLATE.md); do not
duplicate its prompts here.
Describe three things, briefly: the problem (with issue link), why this approach
solves it, and what changed. See
[`PULL_REQUEST_TEMPLATE.md`](.github/PULL_REQUEST_TEMPLATE.md).
Generated
+160 -73
View File
@@ -1119,7 +1119,7 @@ dependencies = [
"getrandom 0.4.3",
"libc",
"log",
"minisign-verify",
"minisign-verify 0.3.0",
"nanoid",
"network-interface",
"objc2",
@@ -4170,6 +4170,12 @@ dependencies = [
"unicase",
]
[[package]]
name = "minisign-verify"
version = "0.2.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "22f9645cb765ea72b8111f36c522475d2daa0d22c957a9826437e97534bc4e9e"
[[package]]
name = "minisign-verify"
version = "0.3.0"
@@ -5993,13 +5999,12 @@ dependencies = [
[[package]]
name = "rust-ini"
version = "0.21.1"
version = "0.21.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4e310ef0e1b6eeb79169a1171daf9abcb87a2e17c03bee2c4bb100b55c75409f"
checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7"
dependencies = [
"cfg-if",
"ordered-multimap",
"trim-in-place",
]
[[package]]
@@ -6620,6 +6625,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e362d9935bc50f019969e2f9ecd66786612daae13e8f277be7bfb66e8bed3f7"
dependencies = [
"libc",
"sigchld",
"windows-sys 0.60.2",
]
@@ -6639,6 +6645,17 @@ version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba"
[[package]]
name = "sigchld"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "47106eded3c154e70176fc83df9737335c94ce22f821c32d17ed1db1f83badb1"
dependencies = [
"libc",
"os_pipe",
"signal-hook 0.3.18",
]
[[package]]
name = "signal-hook"
version = "0.1.17"
@@ -6649,6 +6666,16 @@ dependencies = [
"signal-hook-registry",
]
[[package]]
name = "signal-hook"
version = "0.3.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.8"
@@ -6854,12 +6881,6 @@ version = "1.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
[[package]]
name = "static_vcruntime"
version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ff7589a1859b09232522bb9edf076ca7fcf14ded96685d3fa629db73023ffbb7"
[[package]]
name = "string_cache"
version = "0.9.0"
@@ -6919,9 +6940,9 @@ checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292"
[[package]]
name = "swift-rs"
version = "1.0.9"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cefd87076fd385308ee4aff597256c902f1fbb4d031de1558efc12f8ffefadd9"
checksum = "e45c444e496845d3f2a351146bff59aae4975b2280238df1dfaa0c7d1846f38e"
dependencies = [
"base64 0.21.7",
"serde",
@@ -7168,9 +7189,9 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
[[package]]
name = "tauri"
version = "2.12.4"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f316751027fc2644221d1a448564d519efb07067fda584879d9cda966391d212"
checksum = "ed99ee9694a2deb776d91cae48ac7411ddfc89ecae2f9b5041111d8c88f2ace9"
dependencies = [
"anyhow",
"bytes",
@@ -7216,16 +7237,16 @@ dependencies = [
"tray-icon",
"url",
"webkit2gtk",
"webview2-com",
"webview2-com 0.39.1",
"window-vibrancy",
"windows 0.62.2",
]
[[package]]
name = "tauri-build"
version = "2.7.3"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "641dd5fd297c99ab92717b0428cca5b9c6f960b2ac96ca131b3eebc9d70b8ddc"
checksum = "59563ca5b331fd97f27ee9672ec8d36d33bb3c9e2c9f6710a1a5687256052e45"
dependencies = [
"anyhow",
"cargo_toml",
@@ -7237,8 +7258,6 @@ dependencies = [
"semver 1.0.28",
"serde",
"serde_json",
"static_vcruntime",
"strsim",
"tauri-utils",
"tauri-winres",
"walkdir",
@@ -7246,11 +7265,11 @@ dependencies = [
[[package]]
name = "tauri-codegen"
version = "2.7.3"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a6585c5b536c00f039d12ce70a9755172d5423bbcfd3969863baa70314c3e02f"
checksum = "e50213e463086ecc283d7e9f51c30cc0fd304ad457425158018be26dbf0d0a80"
dependencies = [
"base64 0.23.1",
"base64 0.22.1",
"brotli",
"ico",
"json-patch",
@@ -7273,9 +7292,9 @@ dependencies = [
[[package]]
name = "tauri-macros"
version = "2.7.3"
version = "2.7.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e2e83a0cfa1bae376e8fbbdc0b1692707aed50cd6072ce85cae4789af5d6c19f"
checksum = "8129fb43bb6e0d6b73ad3315cd172beaab17cd28899c594b1b32c80edbbfd99c"
dependencies = [
"heck 0.5.0",
"proc-macro2",
@@ -7331,9 +7350,9 @@ dependencies = [
[[package]]
name = "tauri-plugin-clipboard-manager"
version = "2.5.0"
version = "2.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9be5ed8d94c33afe8377e885119069f6508780659271d0a25cfc54d441f2bc81"
checksum = "333d3b6beeb1c2c7fc040233d4ea7b6c03a9f8f039592b7400833c43fb5b568a"
dependencies = [
"arboard",
"log",
@@ -7346,9 +7365,9 @@ dependencies = [
[[package]]
name = "tauri-plugin-deep-link"
version = "2.6.2"
version = "2.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1b3c64e24ebcbc63aaa2d88a9c51e2507084b46620baf39ecb0baa0738a08bb4"
checksum = "4ac7a92a46ab5c88f44532ca50906d6e448a948d4ddf8c5376ab125e3260f736"
dependencies = [
"dunce",
"plist",
@@ -7394,9 +7413,9 @@ dependencies = [
[[package]]
name = "tauri-plugin-dialog"
version = "2.8.3"
version = "2.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b7461aaacf1901915ea7b9f5cd74f8224a351ed96af0a62c07fce934003cb34b"
checksum = "daf9a5c92e39bdd84f22f6be9230e39d0c4e140345f71ffe6058dca4f71127cf"
dependencies = [
"log",
"raw-window-handle",
@@ -7412,9 +7431,9 @@ dependencies = [
[[package]]
name = "tauri-plugin-fs"
version = "2.7.1"
version = "2.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d58eb1d4984e535f47a642ffec09a2da6f5c18a12b5902d12126cb7626d628b"
checksum = "466eb32466fe5021b6728b06862f9ff14a9f7e1af890047654e03a96d7c0e1bb"
dependencies = [
"anyhow",
"dunce",
@@ -7436,9 +7455,9 @@ dependencies = [
[[package]]
name = "tauri-plugin-global-shortcut"
version = "2.4.1"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93ff17919fe09852d269bd37b1d3d2e993b9dbb514afe7acbf3346c1d3627e2d"
checksum = "0bd2e1f725891a1613af8f25bf918ba3357654b0fd3f2d794c0ce76cbdd92ed2"
dependencies = [
"global-hotkey",
"log",
@@ -7451,9 +7470,9 @@ dependencies = [
[[package]]
name = "tauri-plugin-http"
version = "2.8.3"
version = "2.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2c57e3451553f95a1db3bfed6a358a77ce5080b6399a242c6eadac113f97abd2"
checksum = "93e9d6b3319e101bc1be27cfa0fb4054d7784a12509c67ead0033b82180e5ed1"
dependencies = [
"bytes",
"cookie_store",
@@ -7470,7 +7489,7 @@ dependencies = [
"thiserror 2.0.20",
"tokio",
"url",
"urlpattern",
"urlpattern 0.3.0",
]
[[package]]
@@ -7500,9 +7519,9 @@ dependencies = [
[[package]]
name = "tauri-plugin-notification"
version = "2.7.0"
version = "2.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b939d7b19c8af1381a72ef8474b3aba2b56c1ddac2c23f0c8e96abbcf86b198b"
checksum = "533770889abb60a0407d5a32096182f0e6463f5a3dfaa675be94580aa14070e1"
dependencies = [
"log",
"notify-rust",
@@ -7515,14 +7534,13 @@ dependencies = [
"thiserror 2.0.20",
"time",
"url",
"zbus",
]
[[package]]
name = "tauri-plugin-opener"
version = "2.7.2"
version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "53810be1b4fed22bb6ac3f4cab5a2b80d9391d1af436bda145607532bc0bb219"
checksum = "d39404b2572927e0d3ed30a7ca226037f7508d22e79c96ee6ef0d42b6a4b3345"
dependencies = [
"dunce",
"glob",
@@ -7552,12 +7570,11 @@ dependencies = [
[[package]]
name = "tauri-plugin-shell"
version = "2.5.0"
version = "2.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8567d42b2c7264355daa1df415ec77f52aa42e885924c86a969be2e920e05521"
checksum = "094bdbc47e5efe36544ba21b3a53ab5c724150236cf55e6c9be46191deb6eab6"
dependencies = [
"encoding_rs",
"libc",
"log",
"open",
"os_pipe",
@@ -7570,14 +7587,13 @@ dependencies = [
"tauri-plugin",
"thiserror 2.0.20",
"tokio",
"windows-sys 0.60.2",
]
[[package]]
name = "tauri-plugin-updater"
version = "2.14.0"
version = "2.13.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7259989d4fe11b2bf51f7d671642249b1837c4ae2da1430dc713ae38f98ceeec"
checksum = "3cb0b2ea3e85ca287990d3859a29cc5cb18024240fd78f62e027fb7963670f18"
dependencies = [
"base64 0.22.1",
"dirs 7.0.0",
@@ -7585,9 +7601,8 @@ dependencies = [
"futures-util",
"http",
"infer",
"libc",
"log",
"minisign-verify",
"minisign-verify 0.2.5",
"osakit",
"percent-encoding",
"reqwest 0.13.5",
@@ -7609,9 +7624,9 @@ dependencies = [
[[package]]
name = "tauri-plugin-wdio-webdriver"
version = "1.5.0"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f76676d510d271d80abcf8b0552987b5b4f0eae5e3f906e9b5660d43c97c62e"
checksum = "52c5e97428174a52b7f4357bbc4535fcce52c6a06c8ee7a379502f7cda1b6045"
dependencies = [
"async-trait",
"axum",
@@ -7635,9 +7650,9 @@ dependencies = [
"tracing",
"uuid",
"webkit2gtk",
"webview2-com",
"windows 0.62.2",
"windows-core 0.62.2",
"webview2-com 0.38.2",
"windows 0.61.3",
"windows-core 0.61.2",
]
[[package]]
@@ -7657,9 +7672,9 @@ dependencies = [
[[package]]
name = "tauri-runtime"
version = "2.12.2"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7c55766d99cea69055904d1735b93c2049302b2e09effd462120deb5581d78e9"
checksum = "c2a57a2a9b8c8b7fc00d35b510fe808ded7f9f1b44fc1cdda6aee38632ea5413"
dependencies = [
"cookie",
"dpi",
@@ -7676,15 +7691,15 @@ dependencies = [
"thiserror 2.0.20",
"url",
"webkit2gtk",
"webview2-com",
"webview2-com 0.39.1",
"windows 0.62.2",
]
[[package]]
name = "tauri-runtime-wry"
version = "2.12.2"
version = "2.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d61892587aea2a85245bcaec98c6fe46268874c0a0deae5535fb71ca7b93eae"
checksum = "7f9e7e0c9f80a5130773cfabb6df4dc924518fb1b2c923726966804634b57d22"
dependencies = [
"gtk",
"http",
@@ -7702,7 +7717,7 @@ dependencies = [
"tracing",
"url",
"webkit2gtk",
"webview2-com",
"webview2-com 0.39.1",
"windows 0.62.2",
"wry",
]
@@ -7740,9 +7755,9 @@ dependencies = [
[[package]]
name = "tauri-utils"
version = "2.10.2"
version = "2.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "362ab21cd4cc713f2a02b1a9b5688198b1541cfb6907c4281e9190e0f0ef4aa3"
checksum = "2ff55a614843b9a3f010f211df637c94df2607175b8d0f6e0f4a7c19e18621ee"
dependencies = [
"anyhow",
"brotli",
@@ -7771,7 +7786,7 @@ dependencies = [
"thiserror 2.0.20",
"toml 1.1.6+spec-1.1.0",
"url",
"urlpattern",
"urlpattern 0.6.0",
"uuid",
"walkdir",
]
@@ -7890,7 +7905,7 @@ dependencies = [
"regex",
"semver 0.11.0",
"sha2 0.9.9",
"signal-hook",
"signal-hook 0.1.17",
"terminfo",
"termios",
"thiserror 1.0.69",
@@ -8488,12 +8503,6 @@ dependencies = [
"petgraph",
]
[[package]]
name = "trim-in-place"
version = "0.1.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "daa60874b45b168f67f3f9ba1ee821ad77144d6fa19cef1cd1874a7450a948da"
[[package]]
name = "triomphe"
version = "0.1.16"
@@ -8586,6 +8595,47 @@ dependencies = [
"windows-sys 0.61.2",
]
[[package]]
name = "unic-char-property"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a8c57a407d9b6fa02b4795eb81c5b6652060a15a7903ea981f3d723e6c0be221"
dependencies = [
"unic-char-range",
]
[[package]]
name = "unic-char-range"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0398022d5f700414f6b899e10b8348231abf9173fa93144cbc1a43b9793c1fbc"
[[package]]
name = "unic-common"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "80d7ff825a6a654ee85a63e80f92f054f904f21e7d12da4e22f9834a4aaa35bc"
[[package]]
name = "unic-ucd-ident"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e230a37c0381caa9219d67cf063aa3a375ffed5bf541a452db16e744bdab6987"
dependencies = [
"unic-char-property",
"unic-char-range",
"unic-ucd-version",
]
[[package]]
name = "unic-ucd-version"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96bd2f2237fe450fcd0a1d2f5f4e91711124f7857ba2e964247776ebeeb7b0c4"
dependencies = [
"unic-common",
]
[[package]]
name = "unicase"
version = "2.9.0"
@@ -8645,6 +8695,18 @@ dependencies = [
"serde_derive",
]
[[package]]
name = "urlpattern"
version = "0.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "70acd30e3aa1450bc2eece896ce2ad0d178e9c079493819301573dae3c37ba6d"
dependencies = [
"regex",
"serde",
"unic-ucd-ident",
"url",
]
[[package]]
name = "urlpattern"
version = "0.6.0"
@@ -9036,6 +9098,20 @@ dependencies = [
"rustls-pki-types",
]
[[package]]
name = "webview2-com"
version = "0.38.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a"
dependencies = [
"webview2-com-macros",
"webview2-com-sys 0.38.2",
"windows 0.61.3",
"windows-core 0.61.2",
"windows-implement",
"windows-interface",
]
[[package]]
name = "webview2-com"
version = "0.39.1"
@@ -9043,7 +9119,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f89fca7a704cee10dcb3654c1dbb8941d1783132f1917358af75bec37a7d7e6"
dependencies = [
"webview2-com-macros",
"webview2-com-sys",
"webview2-com-sys 0.39.1",
"windows 0.62.2",
"windows-core 0.62.2",
]
@@ -9059,6 +9135,17 @@ dependencies = [
"syn 2.0.119",
]
[[package]]
name = "webview2-com-sys"
version = "0.38.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c"
dependencies = [
"thiserror 2.0.20",
"windows 0.61.3",
"windows-core 0.61.2",
]
[[package]]
name = "webview2-com-sys"
version = "0.39.1"
@@ -9798,7 +9885,7 @@ dependencies = [
"url",
"webkit2gtk",
"webkit2gtk-sys",
"webview2-com",
"webview2-com 0.39.1",
"windows 0.62.2",
"windows-core 0.62.2",
"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" }
tracing = "0.1.44"
tauri = { version = "2.12.4" }
tauri-plugin-clipboard-manager = "2.5.0"
tauri = { version = "2.12.1" }
tauri-plugin-clipboard-manager = "2.4.1"
parking_lot = { version = "0.12.5", features = ["hardware-lock-elision"] }
anyhow = "1.0.104"
chrono = "0.4.45"
+9 -9
View File
@@ -54,13 +54,13 @@
"@mui/icons-material": "^9.4.0",
"@mui/material": "^9.4.0",
"@tanstack/react-virtual": "^3.14.13",
"@tauri-apps/api": "2.12.4",
"@tauri-apps/plugin-clipboard-manager": "2.5.0",
"@tauri-apps/plugin-dialog": "2.8.3",
"@tauri-apps/plugin-fs": "2.7.1",
"@tauri-apps/plugin-http": "2.8.3",
"@tauri-apps/plugin-opener": "2.7.2",
"@tauri-apps/plugin-updater": "2.14.0",
"@tauri-apps/api": "2.12.1",
"@tauri-apps/plugin-clipboard-manager": "2.4.0",
"@tauri-apps/plugin-dialog": "2.8.0",
"@tauri-apps/plugin-fs": "2.6.0",
"@tauri-apps/plugin-http": "2.8.0",
"@tauri-apps/plugin-opener": "2.7.0",
"@tauri-apps/plugin-updater": "2.13.1",
"ahooks": "^3.10.0",
"cidr-block": "^2.3.0",
"compare-versions": "^6.1.1",
@@ -95,7 +95,7 @@
"@biomejs/biome": "^2.5.14",
"@eslint-react/eslint-plugin": "^5.20.5",
"@eslint/js": "^10.0.1",
"@tauri-apps/cli": "2.12.3",
"@tauri-apps/cli": "2.12.1",
"@types/lodash-es": "^4.17.12",
"@types/node": "^24.13.6",
"@types/react": "19.3.0",
@@ -103,7 +103,7 @@
"@types/validator": "^13.15.10",
"@vitejs/plugin-legacy": "^8.2.3",
"@vitejs/plugin-react": "^6.1.1",
"@wdio/tauri-service": "1.5.0",
"@wdio/tauri-service": "1.4.0",
"adm-zip": "^0.6.1",
"axios": "^1.20.0",
"cli-color": "^2.0.4",
+145 -145
View File
@@ -200,26 +200,26 @@ importers:
specifier: ^3.14.13
version: 3.14.13(react-dom@19.3.1(react@19.3.1))(react@19.3.1)
'@tauri-apps/api':
specifier: 2.12.4
version: 2.12.4
specifier: 2.12.1
version: 2.12.1
'@tauri-apps/plugin-clipboard-manager':
specifier: 2.5.0
version: 2.5.0
specifier: 2.4.0
version: 2.4.0
'@tauri-apps/plugin-dialog':
specifier: 2.8.3
version: 2.8.3
specifier: 2.8.0
version: 2.8.0
'@tauri-apps/plugin-fs':
specifier: 2.7.1
version: 2.7.1
specifier: 2.6.0
version: 2.6.0
'@tauri-apps/plugin-http':
specifier: 2.8.3
version: 2.8.3
specifier: 2.8.0
version: 2.8.0
'@tauri-apps/plugin-opener':
specifier: 2.7.2
version: 2.7.2
specifier: 2.7.0
version: 2.7.0
'@tauri-apps/plugin-updater':
specifier: 2.14.0
version: 2.14.0
specifier: 2.13.1
version: 2.13.1
ahooks:
specifier: ^3.10.0
version: 3.10.0(react-dom@19.3.1(react@19.3.1))(react@19.3.1)
@@ -318,8 +318,8 @@ importers:
specifier: ^10.0.1
version: 10.0.1(eslint@10.11.0(jiti@2.7.0)(supports-color@7.2.0))
'@tauri-apps/cli':
specifier: 2.12.3
version: 2.12.3
specifier: 2.12.1
version: 2.12.1
'@types/lodash-es':
specifier: ^4.17.12
version: 4.17.12
@@ -342,8 +342,8 @@ importers:
specifier: ^6.1.1
version: 6.1.1(vite@8.3.0(@types/node@24.13.6)(esbuild@0.28.2)(jiti@2.7.0)(sass@1.104.1)(terser@5.51.2)(yaml@2.9.1))
'@wdio/tauri-service':
specifier: 1.5.0
version: 1.5.0(expect-webdriverio@6.1.0)(supports-color@7.2.0)(webdriverio@10.0.2(supports-color@7.2.0)(yauzl@2.10.0))
specifier: 1.4.0
version: 1.4.0(expect-webdriverio@5.7.0)(supports-color@7.2.0)(webdriverio@10.0.2(supports-color@7.2.0)(yauzl@2.10.0))
adm-zip:
specifier: ^0.6.1
version: 0.6.1
@@ -2129,102 +2129,102 @@ packages:
'@tanstack/virtual-core@3.17.11':
resolution: {integrity: sha512-+ILjvtHup6Y2hzQ6YzwMgX1Q+oQpxEGOXCEsCNaPoIP0VxMbizIBTmYTDtkerkIQS8/CbP1BRuyt8V/8BCsy1g==}
'@tauri-apps/api@2.12.4':
resolution: {integrity: sha512-pVmfis+4zT8H0Qhp3xPZx9Ju7kpzvA6rr/PSJPyuwfvucTQ8sVt61NYU9KMkK703UFlYadFmjKA4HhybOtN0gg==}
'@tauri-apps/api@2.12.1':
resolution: {integrity: sha512-DeyFHa3wynpyoqTDikDEDGTJIq4LQ5USfolQGRmGIWT6JMADyxZBTDa5cAdT3tDg73rUXufPaCwN7aXBos4OnQ==}
'@tauri-apps/cli-darwin-arm64@2.12.3':
resolution: {integrity: sha512-xmVNoJOOKzKLj7GLHXmXLimRby9M42aBIJa0sB80N7o24eKV5JiVmToKTRLVH9yhL05GZ/B6rSSOmbzNFJwBAA==}
'@tauri-apps/cli-darwin-arm64@2.12.1':
resolution: {integrity: sha512-8bMnbpJ2jKO/ufrOPL65EMHC3ptw7fzeraoQkwibyfGLqIl+UCWsC0152kmNbW6r9NvVQduXqAZF5mH1QgTWhw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [darwin]
'@tauri-apps/cli-darwin-x64@2.12.3':
resolution: {integrity: sha512-URd+0usfmUQQyb5kNPiKt2OT8gp7oGU7QR+qOppB6e35mpVBFFVNhWc9oWb3p0u71fDJ09DpF4T6m9xYxoIB8g==}
'@tauri-apps/cli-darwin-x64@2.12.1':
resolution: {integrity: sha512-G+2qte8GNqNlEZ9ftd21rbu/APVkl3r/nzNDI7LVxEoqJWVNe9GbEgQUMH1r4PHT20Z2UxR3KjCkltIb0e/Wjg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [darwin]
'@tauri-apps/cli-linux-arm-gnueabihf@2.12.3':
resolution: {integrity: sha512-0kwUVx2LbmOoTRlwF1oDww7th4/NSluv4ZBQ6KRZsFKg4g0TlTcIiJ0VehqkQYUeC9xIT1Mk6XtF52mI155VSQ==}
'@tauri-apps/cli-linux-arm-gnueabihf@2.12.1':
resolution: {integrity: sha512-5b3n4DaTW+A1Zntnh4Yn44VyLrlPEpB8kMOBRfWkzNnzolBk+AYhKIfKjCLmoC8Mob3ETodxAEOMkE2YPKEWzQ==}
engines: {node: '>= 10'}
cpu: [arm]
os: [linux]
'@tauri-apps/cli-linux-arm64-gnu@2.12.3':
resolution: {integrity: sha512-gKSGDx6fx+HBLQmG6guChFdisbrCSdx9Yli+j5teOa64gm1CeM+C6NLru5Pj3ZkkEep05BmCFjxvX+cjqAycZQ==}
'@tauri-apps/cli-linux-arm64-gnu@2.12.1':
resolution: {integrity: sha512-APArHhDu+8pXSCkCQmvaz1nD3d8WTmlCoSS/oG475TJ6AATBvyQOAmf5qELgsQ4s2+8MOsop0Zd699RKELF1Cw==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [glibc]
'@tauri-apps/cli-linux-arm64-musl@2.12.3':
resolution: {integrity: sha512-Yr59qMy/gcvAfBuFdCGlAmGiLsYWMFDbsP4bAI6iNBzcJTGrYWZGeiQhGDo29MQwfF/NUBqyM2qljXu5tZ6wjQ==}
'@tauri-apps/cli-linux-arm64-musl@2.12.1':
resolution: {integrity: sha512-JaCxcLoV0oGtllVkLr4UAHWG/wTfkEunnUqnsEsGYOp0VRxnUwHg63wFBD0jU2ul5eFfSLIDS4RF2gfzsSo5Jg==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [linux]
libc: [musl]
'@tauri-apps/cli-linux-riscv64-gnu@2.12.3':
resolution: {integrity: sha512-jKr0/REBMnLKC0OwPQZkyge0mKDaNZ0b/AbfGwVyCgFfwS730EVLfVDo3nt7MUu6jJaytRq08+xSj5qfy+U1+Q==}
'@tauri-apps/cli-linux-riscv64-gnu@2.12.1':
resolution: {integrity: sha512-K5+VXM7+SGVDwZleA2+lMgvbTVuA5e1tkiVEawu1Yxj26ZyEfO8h8xTC1rBdRX2xgJDSwCk6vPUBCS6i+Z5slQ==}
engines: {node: '>= 10'}
cpu: [riscv64]
os: [linux]
libc: [glibc]
'@tauri-apps/cli-linux-x64-gnu@2.12.3':
resolution: {integrity: sha512-gTkEqJAGCMROGanxHnzaJzQ+/rXl39HWVg0/hDODS1wDWBGqoceqGC6Qm1+mQbyT11xnsB8mKu/gyVID/YpFIw==}
'@tauri-apps/cli-linux-x64-gnu@2.12.1':
resolution: {integrity: sha512-Z1QGPr49HJZMktu+Spu6Q1b/f9ANadXvqZZZt2645ua1x1ev6prOgp85eiGv2jRzohFEMLo6aJyaLjLUX5KIvA==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [glibc]
'@tauri-apps/cli-linux-x64-musl@2.12.3':
resolution: {integrity: sha512-T2dTxL1svRym71fZtaW4pZYXbthbfFsEJr1NeOUg/p7laWXZbUt/8DMSZf8recu9f7+1F/Lv00HtzsEu6vDO9w==}
'@tauri-apps/cli-linux-x64-musl@2.12.1':
resolution: {integrity: sha512-nHuEhRPOMSJ/8RRhtMRxB9B9lVr7xxpe4DapBz8x1GIau3vdzjHHJ1hAfmw+PyayPLZ6XoaVIHPgI17rX6ii8w==}
engines: {node: '>= 10'}
cpu: [x64]
os: [linux]
libc: [musl]
'@tauri-apps/cli-win32-arm64-msvc@2.12.3':
resolution: {integrity: sha512-eoiQwpEZWbpsVHcM9BAzqqe4TCHszvr3rsM7pUQ4ofLK0lF1i1d/jX0Qo5DYMqcrUtwQuDdDuOCbcT0Izf1VwQ==}
'@tauri-apps/cli-win32-arm64-msvc@2.12.1':
resolution: {integrity: sha512-I0oPk2uGh86R8dAs2X5OxSP+/GqSc8jh1Px3l8EqrocSNivsRHDTh2k3PVOqdv163dlV6PIVvmhZAbH/lPfZrA==}
engines: {node: '>= 10'}
cpu: [arm64]
os: [win32]
'@tauri-apps/cli-win32-ia32-msvc@2.12.3':
resolution: {integrity: sha512-kE+KUg6Dbbkgvcw0m99wJVIToFQzJqpMBAXah74WqxaLzssNwSTUccrHp6+QVzMhjQP330tvekFmnQF+0NbE6Q==}
'@tauri-apps/cli-win32-ia32-msvc@2.12.1':
resolution: {integrity: sha512-baNZRBfepAJ5qNFRMwzgBk94xQmkcmTZrNDjZjnzrq2vAaTC3NAhallb/zo3hyoCGQlMS13sdovktrg46NZz6Q==}
engines: {node: '>= 10'}
cpu: [ia32]
os: [win32]
'@tauri-apps/cli-win32-x64-msvc@2.12.3':
resolution: {integrity: sha512-L7wndWAgX2mLW7alswpL3d1R0P9FN2WYBgHaQkAYEBxHHGkY4vFX9CjcRQ1LAxDAMIJxrY7/aDziFJeChS84NQ==}
'@tauri-apps/cli-win32-x64-msvc@2.12.1':
resolution: {integrity: sha512-dIFaKslrzcbesb+bBh+E1R9km241pHyj32w6D9FPtvQeYpRUFbiCLEa/2S+S0UnIytfxepp4P+X8IerGqnXeYg==}
engines: {node: '>= 10'}
cpu: [x64]
os: [win32]
'@tauri-apps/cli@2.12.3':
resolution: {integrity: sha512-f7/LsY74SVb1ChmOPw1ajOpxii0GSzDAgmWHRci8JUYuuai04rXsSbFobpmB0sP2DdlvMD/B5ZotEn8GOGMRUg==}
'@tauri-apps/cli@2.12.1':
resolution: {integrity: sha512-kEDEiGzG+yAc5FeLxtXpES/VN+F2C8H0r4gDVtfRLKxT9np9101d9REG/Kgo2lr0hKi0IpDsODiHnU3+naOmLg==}
engines: {node: '>= 10'}
hasBin: true
'@tauri-apps/plugin-clipboard-manager@2.5.0':
resolution: {integrity: sha512-dibUy1nKURYDXRMvjNk4deAbTZ5jPQ8wCE4nolt5xniryWgXQ4sK2GkFXoXlHbRdPzfZptZj95BRLfalA7m/2Q==}
'@tauri-apps/plugin-clipboard-manager@2.4.0':
resolution: {integrity: sha512-yAuTffXc/sm+4oYnrwxbUkaJXQMTesPJ9dim0ADcA31rpboyhAN2TGQqs9VCcPIpb60LFOeePkvhpP2I3xhejg==}
'@tauri-apps/plugin-dialog@2.8.3':
resolution: {integrity: sha512-Hd/yBfk2R5v2MFrz/0VArSXeh8xKLLXGunqSiTtcHKn1zNZwaKujuA0VSwUvNfwC4hDoF1PveS+gBDNe3nHspw==}
'@tauri-apps/plugin-dialog@2.8.0':
resolution: {integrity: sha512-HSSD5C8UFE9ijD1wue84F7jjaIReFmj8AAPtS+xyjYRltJafIc4BiWHY7SJ6vbbew6u+YljEJHfyNfy5VwywXg==}
'@tauri-apps/plugin-fs@2.7.1':
resolution: {integrity: sha512-4TBVjpi/q8PuNzjpkAc2iBGXMTLjOrAyfTiiOL8ORaJCZJRG3tjzmQ4sqs2CMG75FvLyRkOkERfP7AyY4iBNwQ==}
'@tauri-apps/plugin-fs@2.6.0':
resolution: {integrity: sha512-MhBAZ4rryd+h9fijd/GWDfArqiAeGI0ZNduV4wuRyUVI6wDZ+oDBY7atqtoXKDwlTUVSvpvmWJAY0ehQd/v6Uw==}
'@tauri-apps/plugin-http@2.8.3':
resolution: {integrity: sha512-MrgCnnxSnzxkMX+Tk1XXB8rSI9hfhUHQmtbn0EpflEDcSx04K1BDfrv51tcK27SCuU/B1j8sNHL0UZur/1iqKA==}
'@tauri-apps/plugin-http@2.8.0':
resolution: {integrity: sha512-cPvZvfUSaBkqGxef/dgbh/iEl6T3jQfSJyypi9OdcWr1VcuTt4ZRlk9DLAHEHF2q2NML+0UOkjyXRPNo6Cky6g==}
'@tauri-apps/plugin-opener@2.7.2':
resolution: {integrity: sha512-LyybP1VSzTMQiaFYcsbAse47KpivlyXdLZMQzGLEnkgQacGESnpjAfFoQ6h5sqWgAdZDH9szYjPzHMpv0Sz+TQ==}
'@tauri-apps/plugin-opener@2.7.0':
resolution: {integrity: sha512-mBorYfVKh9Lt7s4ZFtKbMUTxBw+Y/sVmSlKCtkkoBgMTC/DIbt/kaPm67L/2djAUqDI1wbixy5OHHVmipjuGMw==}
'@tauri-apps/plugin-updater@2.14.0':
resolution: {integrity: sha512-gdylJ9tger4hkyOn24QZd6MNOKVo2Eh53iw5th2Qj1vKTR/+u4PIwJWuiis5hohYvobn6iOvlnALfrqIPSzh7Q==}
'@tauri-apps/plugin-updater@2.13.1':
resolution: {integrity: sha512-+STDzJ0sdQLImjc63zkv6Nncimqeo6jyCWoTQtbSwgGLNj1qbP9I6LC4p5R5ocBk3gNKAZs6i/AJz0jxysytiw==}
'@tybys/wasm-util@0.10.4':
resolution: {integrity: sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==}
@@ -2570,12 +2570,12 @@ packages:
resolution: {integrity: sha512-n6elHE4ItKAtr4lwAaWIh/sAFuEFhyesLM27i5l9IWBJEK6VECdt0Pigj/4vUD2tliOoBtsWxphSW4oiNFXotg==}
engines: {node: '>=22.19.0'}
'@wdio/globals@9.31.3':
resolution: {integrity: sha512-ZzmRKUlwUBahNoNxHbAzst+9dNInEjLQc7Z3c86OhDRxAf3Ivw8k2rc56UCbyYiZnkd1dOpuA91zFiTrUNNORw==}
'@wdio/globals@9.29.1':
resolution: {integrity: sha512-F96BKppx4HGD64v+s57TM4K4zaxqUCg2RXHk6sjB2xrSa7P+d2VYV28ID2ddF7iSodVfPlpa1i2/jy8jMGrU8w==}
engines: {node: '>=18.20.0'}
peerDependencies:
expect-webdriverio: ^6.0.9
webdriverio: ^9.28.0
expect-webdriverio: ^5.6.5
webdriverio: ^9.0.0
'@wdio/logger@10.0.0':
resolution: {integrity: sha512-kUIw0ZzMs1mvXOaLEcjrM1nqNXY1reaJKReKNcjDSnfhnfUIb0Q5pAsxiMeil2uR0WsSUPzn0r3D9v//7vTOkA==}
@@ -2585,21 +2585,21 @@ packages:
resolution: {integrity: sha512-0ZAEIo6PNyMIJPlOGkIgyOJUjcd0pC8/QHlVAAe1c91/IcjZ1X+k0yidXHaboJdN7dq1XPUacmhRdtua0U5EZg==}
engines: {node: '>=18.20.0'}
'@wdio/native-core@1.3.0':
resolution: {integrity: sha512-wUwc0QTdvhD0LwH4YcRaN9JWLX/yac8sovddUR5/4sqHMNJr3yaN11bpIfYoD/bEk+1nTLjWuDCFpe5l5wUgLQ==}
engines: {node: '>=18.20.0'}
'@wdio/native-core@1.2.0':
resolution: {integrity: sha512-Z/Qa2xmV/2IMSCsCKyVk79tda8n47FOOOS9XEOWGpHUiFr1z4gGOFxt13Fd9RQu9Kk1IA9fJB1VmJZtrtkXvEw==}
engines: {node: ^18.12.0 || ^20.9.0 || >=22.11.0}
'@wdio/native-spy@1.4.0':
resolution: {integrity: sha512-unsC2Vgu5T9atD478Zj/p2N+rVSK4i/NibIaaEw4rZF/T5u5bmAHChFntEiBEfyMzd62VwbhKhzwwOSKjzrfJA==}
engines: {node: '>=18.20.0'}
'@wdio/native-spy@1.3.0':
resolution: {integrity: sha512-Kw1/HsOM8CNnlPHozlv0FP+nFnms5efICq5WqpaNdwa0urv/C9K/jhM69OqAbocBfRXkDVuWecNPmCV9S/cXGw==}
engines: {node: ^18.12.0 || ^20.9.0 || >=22.11.0}
'@wdio/native-types@2.6.0':
resolution: {integrity: sha512-MVMVpZTH+UY6nUtLClCzY4BQSIs/nCCHo81i4ClLR+b2NehyBvwccttXDAGPgf0icyHUhd+PgIPXaLgRcm4M9A==}
engines: {node: '>=18.20.0'}
'@wdio/native-types@2.5.0':
resolution: {integrity: sha512-NjaIfcsEKpU9gm0goywEG0A7J4pyEsgORKNrQ1xboSy10BeTk5QpgHWP1bNvqUnZrKoKJRonpSn81ZyYKghO1g==}
engines: {node: ^18.12.0 || ^20.9.0 || >=22.11.0}
'@wdio/native-utils@2.8.0':
resolution: {integrity: sha512-TmIkPAd19FkJ747AdFVEtawbakVV3HHCh8zk3Kl/FLc6B2bbea7hn90jtLxqBZgCVqUuJIBcMNFg/nV/hgBFOw==}
engines: {node: '>=18.20.0'}
'@wdio/native-utils@2.7.0':
resolution: {integrity: sha512-qCzc+5o4YVESApQB5//NxomZ+F+M3Pc02hUNnJ1/w9naMb8mfgaHXBLWVwejEfO36z2MnwGatBNWZAm38G1frA==}
engines: {node: ^18.12.0 || ^20.9.0 || >=22.11.0}
peerDependencies:
tsx: ^4.22.4
peerDependenciesMeta:
@@ -2614,17 +2614,17 @@ packages:
resolution: {integrity: sha512-wAC9ksdWWv2E3Sj86eNKxB3IUt0gEyw9rgDGAWjk/GWgi6K3e+/n3erGNweI9JMJyC/+RNe0qHgVPQpRJMRVpg==}
engines: {node: '>=22.19.0'}
'@wdio/reporter@9.31.2':
resolution: {integrity: sha512-3cKE6vCOsro/Ep9dGTIbo3FBn3WNvJ++BIn32lFd9m19pFdjuwlVgLtPXHb9jWNCJs740h5AIUcVkcscqUYhAA==}
'@wdio/reporter@9.30.1':
resolution: {integrity: sha512-0whWaLfJtOAy3PzbGUxGrCAGitgaUCMe8qUgAuP2l7ess70AXqvp1wJibw74a/Z2giegJlJiAc4PxS0abs9/tw==}
engines: {node: '>=18.20.0'}
'@wdio/spec-reporter@9.31.2':
resolution: {integrity: sha512-lNLLPUk4y1Yp5pWwBFF+tjZIy7bOv0TPTk4HDWV4LnnHnkc24abI0nN+kb2y5/JV8ZgwLCCVKUFRdRUxfKsAaQ==}
'@wdio/spec-reporter@9.30.1':
resolution: {integrity: sha512-En74iLuj8xjNanSPA05rTCii0qHZQe6Zpwi74vTGO6LtfGxvcNr1Z1zUj8xWwnoclnAne3DtmCAyXTezLX1pEA==}
engines: {node: '>=18.20.0'}
'@wdio/tauri-service@1.5.0':
resolution: {integrity: sha512-S3fs8T+jgNOFi4x/X2z69unS/1K3ZB28+owBouhHtVnrMKeBWAKP46IHnp8c8AZXppn6fiPNC4M1jfRZbLKeKw==}
engines: {node: '>=18.20.0'}
'@wdio/tauri-service@1.4.0':
resolution: {integrity: sha512-EUTS8tfMOXK1yPNEaRd6yiSdPoX19zeV+gtp2m7UuPTWFZGayPuMUoFyttP6Lx3OFvJC3lnbxjr1vcCNAOA7tg==}
engines: {node: ^18.12.0 || ^20.9.0 || >=22.11.0}
peerDependencies:
webdriverio: ^9.0.0
@@ -2632,8 +2632,8 @@ packages:
resolution: {integrity: sha512-HGP9lAYXCsLAXzugfT4khCh7tYByWOMIxZHCG+tUvgmpwNzi6lfIsxyIlWownXK1wYJUpk6tkDBX/iXwN+q0lg==}
engines: {node: '>=22.19.0'}
'@wdio/types@9.31.2':
resolution: {integrity: sha512-lQKaiQDCJJ6VC1K/cKzaAxCpwB9pL1U0VbmX2uGksMxO4EDbNuLvIxWryuUDJ7OaSYTjxILTKYFErdbT/xEikg==}
'@wdio/types@9.30.1':
resolution: {integrity: sha512-umKQDbSmu3/ucUsIA5UUTK5gCWnClyVpWz3fC39044rCXQ3zCd1a3GoJibbAITKiwnPFf2yU3LvTa+C78sh9Lg==}
engines: {node: '>=18.20.0'}
'@wdio/utils@10.0.1':
@@ -3418,13 +3418,13 @@ packages:
resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==}
engines: {node: '>=12.0.0'}
expect-webdriverio@6.1.0:
resolution: {integrity: sha512-RXgaXoH1U8IiAHPelkMhZSnWUT0HfIhkjKK8nBkIpnXRO+zJ8dRafoyZhVy5mu/LtoyIr9juRWRwN284OSNM6Q==}
expect-webdriverio@5.7.0:
resolution: {integrity: sha512-jLOTrJoPBC3Wtd83ryHMbRcEajMGtAnn6OWtSnoJoDLt16nHvxVdjcI4Bc0n+1KAOr8DvQtlJ55f0M48kJtEpw==}
engines: {node: '>=20'}
peerDependencies:
'@wdio/globals': ^9.0.0
'@wdio/logger': ^9.0.0
webdriverio: ^9.28.0
webdriverio: ^9.0.0
expect@30.5.2:
resolution: {integrity: sha512-0LNuMvs8/5mRYhnCR4k+lGV7fqPMs6Bnksda4S3zsCUmdxBpAn4zU4NNzXdq1pMKe/H4W5t/zVIDSJ/H3e0vDA==}
@@ -6910,78 +6910,78 @@ snapshots:
'@tanstack/virtual-core@3.17.11': {}
'@tauri-apps/api@2.12.4': {}
'@tauri-apps/api@2.12.1': {}
'@tauri-apps/cli-darwin-arm64@2.12.3':
'@tauri-apps/cli-darwin-arm64@2.12.1':
optional: true
'@tauri-apps/cli-darwin-x64@2.12.3':
'@tauri-apps/cli-darwin-x64@2.12.1':
optional: true
'@tauri-apps/cli-linux-arm-gnueabihf@2.12.3':
'@tauri-apps/cli-linux-arm-gnueabihf@2.12.1':
optional: true
'@tauri-apps/cli-linux-arm64-gnu@2.12.3':
'@tauri-apps/cli-linux-arm64-gnu@2.12.1':
optional: true
'@tauri-apps/cli-linux-arm64-musl@2.12.3':
'@tauri-apps/cli-linux-arm64-musl@2.12.1':
optional: true
'@tauri-apps/cli-linux-riscv64-gnu@2.12.3':
'@tauri-apps/cli-linux-riscv64-gnu@2.12.1':
optional: true
'@tauri-apps/cli-linux-x64-gnu@2.12.3':
'@tauri-apps/cli-linux-x64-gnu@2.12.1':
optional: true
'@tauri-apps/cli-linux-x64-musl@2.12.3':
'@tauri-apps/cli-linux-x64-musl@2.12.1':
optional: true
'@tauri-apps/cli-win32-arm64-msvc@2.12.3':
'@tauri-apps/cli-win32-arm64-msvc@2.12.1':
optional: true
'@tauri-apps/cli-win32-ia32-msvc@2.12.3':
'@tauri-apps/cli-win32-ia32-msvc@2.12.1':
optional: true
'@tauri-apps/cli-win32-x64-msvc@2.12.3':
'@tauri-apps/cli-win32-x64-msvc@2.12.1':
optional: true
'@tauri-apps/cli@2.12.3':
'@tauri-apps/cli@2.12.1':
optionalDependencies:
'@tauri-apps/cli-darwin-arm64': 2.12.3
'@tauri-apps/cli-darwin-x64': 2.12.3
'@tauri-apps/cli-linux-arm-gnueabihf': 2.12.3
'@tauri-apps/cli-linux-arm64-gnu': 2.12.3
'@tauri-apps/cli-linux-arm64-musl': 2.12.3
'@tauri-apps/cli-linux-riscv64-gnu': 2.12.3
'@tauri-apps/cli-linux-x64-gnu': 2.12.3
'@tauri-apps/cli-linux-x64-musl': 2.12.3
'@tauri-apps/cli-win32-arm64-msvc': 2.12.3
'@tauri-apps/cli-win32-ia32-msvc': 2.12.3
'@tauri-apps/cli-win32-x64-msvc': 2.12.3
'@tauri-apps/cli-darwin-arm64': 2.12.1
'@tauri-apps/cli-darwin-x64': 2.12.1
'@tauri-apps/cli-linux-arm-gnueabihf': 2.12.1
'@tauri-apps/cli-linux-arm64-gnu': 2.12.1
'@tauri-apps/cli-linux-arm64-musl': 2.12.1
'@tauri-apps/cli-linux-riscv64-gnu': 2.12.1
'@tauri-apps/cli-linux-x64-gnu': 2.12.1
'@tauri-apps/cli-linux-x64-musl': 2.12.1
'@tauri-apps/cli-win32-arm64-msvc': 2.12.1
'@tauri-apps/cli-win32-ia32-msvc': 2.12.1
'@tauri-apps/cli-win32-x64-msvc': 2.12.1
'@tauri-apps/plugin-clipboard-manager@2.5.0':
'@tauri-apps/plugin-clipboard-manager@2.4.0':
dependencies:
'@tauri-apps/api': 2.12.4
'@tauri-apps/api': 2.12.1
'@tauri-apps/plugin-dialog@2.8.3':
'@tauri-apps/plugin-dialog@2.8.0':
dependencies:
'@tauri-apps/api': 2.12.4
'@tauri-apps/api': 2.12.1
'@tauri-apps/plugin-fs@2.7.1':
'@tauri-apps/plugin-fs@2.6.0':
dependencies:
'@tauri-apps/api': 2.12.4
'@tauri-apps/api': 2.12.1
'@tauri-apps/plugin-http@2.8.3':
'@tauri-apps/plugin-http@2.8.0':
dependencies:
'@tauri-apps/api': 2.12.4
'@tauri-apps/api': 2.12.1
'@tauri-apps/plugin-opener@2.7.2':
'@tauri-apps/plugin-opener@2.7.0':
dependencies:
'@tauri-apps/api': 2.12.4
'@tauri-apps/api': 2.12.1
'@tauri-apps/plugin-updater@2.14.0':
'@tauri-apps/plugin-updater@2.13.1':
dependencies:
'@tauri-apps/api': 2.12.4
'@tauri-apps/api': 2.12.1
'@tybys/wasm-util@0.10.4':
dependencies:
@@ -7323,9 +7323,9 @@ snapshots:
- supports-color
- yauzl
'@wdio/globals@9.31.3(expect-webdriverio@6.1.0)(webdriverio@10.0.2(supports-color@7.2.0)(yauzl@2.10.0))':
'@wdio/globals@9.29.1(expect-webdriverio@5.7.0)(webdriverio@10.0.2(supports-color@7.2.0)(yauzl@2.10.0))':
dependencies:
expect-webdriverio: 6.1.0(@wdio/globals@9.31.3)(@wdio/logger@9.29.1)(webdriverio@10.0.2(supports-color@7.2.0)(yauzl@2.10.0))
expect-webdriverio: 5.7.0(@wdio/globals@9.29.1)(@wdio/logger@9.29.1)(webdriverio@10.0.2(supports-color@7.2.0)(yauzl@2.10.0))
webdriverio: 10.0.2(supports-color@7.2.0)(yauzl@2.10.0)
'@wdio/logger@10.0.0':
@@ -7344,11 +7344,11 @@ snapshots:
safe-regex2: 5.1.1
strip-ansi: 7.2.0
'@wdio/native-core@1.3.0(supports-color@7.2.0)':
'@wdio/native-core@1.2.0(supports-color@7.2.0)':
dependencies:
'@wdio/logger': 9.29.1
'@wdio/native-types': 2.6.0
'@wdio/native-utils': 2.8.0(supports-color@7.2.0)
'@wdio/native-types': 2.5.0
'@wdio/native-utils': 2.7.0(supports-color@7.2.0)
debug: 4.4.3(supports-color@7.2.0)
get-port: 7.2.0
tslib: 2.8.1
@@ -7356,11 +7356,11 @@ snapshots:
- supports-color
- tsx
'@wdio/native-spy@1.4.0': {}
'@wdio/native-spy@1.3.0': {}
'@wdio/native-types@2.6.0': {}
'@wdio/native-types@2.5.0': {}
'@wdio/native-utils@2.8.0(supports-color@7.2.0)':
'@wdio/native-utils@2.7.0(supports-color@7.2.0)':
dependencies:
'@wdio/logger': 9.29.1
debug: 4.4.3(supports-color@7.2.0)
@@ -7378,32 +7378,32 @@ snapshots:
dependencies:
'@types/node': 22.20.5
'@wdio/reporter@9.31.2':
'@wdio/reporter@9.30.1':
dependencies:
'@types/node': 20.19.43
'@wdio/logger': 9.29.1
'@wdio/types': 9.31.2
'@wdio/types': 9.30.1
diff: 8.0.4
object-inspect: 1.13.4
'@wdio/spec-reporter@9.31.2':
'@wdio/spec-reporter@9.30.1':
dependencies:
'@wdio/reporter': 9.31.2
'@wdio/types': 9.31.2
'@wdio/reporter': 9.30.1
'@wdio/types': 9.30.1
chalk: 5.6.2
easy-table: 1.2.0
pretty-ms: 9.3.1
'@wdio/tauri-service@1.5.0(expect-webdriverio@6.1.0)(supports-color@7.2.0)(webdriverio@10.0.2(supports-color@7.2.0)(yauzl@2.10.0))':
'@wdio/tauri-service@1.4.0(expect-webdriverio@5.7.0)(supports-color@7.2.0)(webdriverio@10.0.2(supports-color@7.2.0)(yauzl@2.10.0))':
dependencies:
'@wdio/globals': 9.31.3(expect-webdriverio@6.1.0)(webdriverio@10.0.2(supports-color@7.2.0)(yauzl@2.10.0))
'@wdio/globals': 9.29.1(expect-webdriverio@5.7.0)(webdriverio@10.0.2(supports-color@7.2.0)(yauzl@2.10.0))
'@wdio/logger': 9.29.1
'@wdio/native-core': 1.3.0(supports-color@7.2.0)
'@wdio/native-spy': 1.4.0
'@wdio/native-types': 2.6.0
'@wdio/native-utils': 2.8.0(supports-color@7.2.0)
'@wdio/spec-reporter': 9.31.2
'@wdio/types': 9.31.2
'@wdio/native-core': 1.2.0(supports-color@7.2.0)
'@wdio/native-spy': 1.3.0
'@wdio/native-types': 2.5.0
'@wdio/native-utils': 2.7.0(supports-color@7.2.0)
'@wdio/spec-reporter': 9.30.1
'@wdio/types': 9.30.1
debug: 4.4.3(supports-color@7.2.0)
get-port: 7.2.0
tslib: 2.8.1
@@ -7417,7 +7417,7 @@ snapshots:
dependencies:
'@types/node': 22.20.5
'@wdio/types@9.31.2':
'@wdio/types@9.30.1':
dependencies:
'@types/node': 20.19.43
@@ -8331,10 +8331,10 @@ snapshots:
expect-type@1.4.0: {}
expect-webdriverio@6.1.0(@wdio/globals@9.31.3)(@wdio/logger@9.29.1)(webdriverio@10.0.2(supports-color@7.2.0)(yauzl@2.10.0)):
expect-webdriverio@5.7.0(@wdio/globals@9.29.1)(@wdio/logger@9.29.1)(webdriverio@10.0.2(supports-color@7.2.0)(yauzl@2.10.0)):
dependencies:
'@vitest/snapshot': 4.1.11
'@wdio/globals': 9.31.3(expect-webdriverio@6.1.0)(webdriverio@10.0.2(supports-color@7.2.0)(yauzl@2.10.0))
'@wdio/globals': 9.29.1(expect-webdriverio@5.7.0)(webdriverio@10.0.2(supports-color@7.2.0)(yauzl@2.10.0))
'@wdio/logger': 9.29.1
deep-eql: 5.0.2
expect: 30.5.2
@@ -10005,7 +10005,7 @@ snapshots:
tauri-plugin-mihomo-api@https://codeload.github.com/clash-verge-rev/tauri-plugin-mihomo/tar.gz/7a03d16f4d39b1bb885b7babe66b9e153e34b615:
dependencies:
'@tauri-apps/api': 2.12.4
'@tauri-apps/api': 2.12.1
teex@1.0.1:
dependencies:
+11 -11
View File
@@ -29,7 +29,7 @@ perf-harness = ["dep:tauri-plugin-wdio-webdriver"]
identifier = "io.github.clash-verge-rev.clash-verge-rev"
[build-dependencies]
tauri-build = { version = "2.7.3", features = [] }
tauri-build = { version = "2.7.1", features = [] }
[dependencies]
clash-verge-draft = { workspace = true }
@@ -72,12 +72,12 @@ sysproxy = { git = "https://github.com/clash-verge-rev/sysproxy-rs.git", branch
"guard",
] }
network-interface = { version = "2.0.5", features = ["serde"] }
tauri-plugin-shell = "2.5.0"
tauri-plugin-opener = "2.7.2"
tauri-plugin-dialog = "2.8.3"
tauri-plugin-fs = "2.7.1"
tauri-plugin-shell = "2.4.0"
tauri-plugin-opener = "2.7.0"
tauri-plugin-dialog = "2.8.1"
tauri-plugin-fs = "2.6.0"
tauri-plugin-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"
zip = "9.0.4"
flate2 = "1.1.10"
@@ -90,10 +90,10 @@ getrandom = "0.4.3"
futures = "0.3.34"
gethostname = "1.1.0"
scopeguard = "1.2.0"
tauri-plugin-notification = "2.7.0"
tauri-plugin-notification = "2.5.1"
tokio-stream = "0.1.19"
backon = { version = "1.6.0", features = ["tokio-sleep"] }
tauri-plugin-http = "2.8.3"
tauri-plugin-http = "2.8.0"
tracing = { workspace = true }
tauri-plugin-devtools = { version = "2.2.2", optional = true }
tauri-plugin-mihomo = { git = "https://github.com/clash-verge-rev/tauri-plugin-mihomo", rev = "7a03d16f4d39b1bb885b7babe66b9e153e34b615" }
@@ -120,7 +120,7 @@ libc = "0.2.191"
clash-verge-signal = { workspace = true }
[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-foundation = { version = "0.3.2", features = [
"NSString",
@@ -153,8 +153,8 @@ windows-sys.workspace = true
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
tauri-plugin-autostart = "2.7.0"
tauri-plugin-global-shortcut = "2.4.1"
tauri-plugin-updater = "2.14.0"
tauri-plugin-global-shortcut = "2.4.0"
tauri-plugin-updater = "2.13.1"
[dev-dependencies]
criterion = { workspace = true }
+5 -10
View File
@@ -1,9 +1,10 @@
fn main() -> Result<(), Box<dyn std::error::Error>> {
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_os("CARGO_FEATURE_CLIPPY").is_some()
{
// Tauri's resource is linked to the app binary, but not to Rust unit-test harnesses or examples.
// Give non-app targets their own manifest so tests and examples can resolve TaskDialogIndirect.
// Tauri's resource is linked to the app binary, but not to Rust unit-test harnesses.
// 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 manifest_path = std::path::PathBuf::from(out_dir).join("windows-test-manifest.xml");
std::fs::write(
@@ -24,14 +25,8 @@ fn main() -> Result<(), Box<dyn std::error::Error>> {
</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=/MANIFESTINPUT:{manifest_display}");
} else {
println!("cargo:rustc-link-arg-examples=/MANIFEST:EMBED");
println!("cargo:rustc-link-arg-examples=/MANIFESTINPUT:{manifest_display}");
}
println!("cargo:rustc-link-arg=/MANIFEST:EMBED");
println!("cargo:rustc-link-arg=/MANIFESTINPUT:{}", manifest_path.display());
}
#[cfg(feature = "clippy")]
+237
View File
@@ -0,0 +1,237 @@
//! Live handles into the attached tray menu and the reconcile decision:
//! state-only changes mutate items in place, a proxy-only structure change
//! swaps the group submenus, anything else rebuilds.
use crate::Type;
use crate::config::{IProfiles, IVerge};
use clash_verge_logging::logging_error;
use parking_lot::Mutex;
use smartstring::alias::String;
use std::collections::HashMap;
use tauri::menu::{CheckMenuItem, IsMenuItem, Submenu};
use tauri::tray::TrayIcon;
use tauri::{AppHandle, Wry};
use tauri_plugin_mihomo::models::Proxies;
use super::{TrayMenuOptions, build_tray_menu, create_subcreate_proxy_menu_item};
pub(super) struct ProxyNodeItem {
pub(super) name: String,
pub(super) item: CheckMenuItem<Wry>,
pub(super) text: String,
pub(super) checked: bool,
}
pub(super) struct ProxyGroupItems {
pub(super) name: String,
pub(super) nodes: Vec<ProxyNodeItem>,
}
pub(super) struct TrayMenuState {
pub(super) mode_items: [CheckMenuItem<Wry>; 3],
pub(super) system_proxy_item: CheckMenuItem<Wry>,
pub(super) tun_item: CheckMenuItem<Wry>,
pub(super) lightweight_item: CheckMenuItem<Wry>,
pub(super) profile_items: Vec<(String, CheckMenuItem<Wry>)>,
pub(super) proxies_submenu: Option<Submenu<Wry>>,
pub(super) proxy_groups: Vec<ProxyGroupItems>,
pub(super) signature: String,
pub(super) proxy_signature: Option<String>,
}
pub(super) fn proxy_delay_text(proxy_nodes_data: &Proxies, proxy_str: &str) -> String {
proxy_nodes_data
.proxies
.get(proxy_str)
.and_then(|h| h.history.last())
.map(|h| match h.delay {
0 => "-ms".into(),
delay if delay >= 10000 => "-ms".into(),
_ => format!("{}ms", h.delay).into(),
})
.unwrap_or_else(|| "-ms".into())
}
/// Topology inputs that a rebuild cannot reconcile by mutating items.
pub(super) fn menu_signature(verge: &IVerge, profiles: &IProfiles, mode: &str) -> String {
let hotkeys = verge.hotkeys.as_ref().map(|h| h.join(",")).unwrap_or_default();
let profiles = profiles
.profiles_preview()
.unwrap_or_default()
.into_iter()
.map(|profile| format!("{}:{}", profile.uid, profile.name))
.collect::<Vec<_>>()
.join(";");
format!(
"{}|{}|{mode}|{}|{hotkeys}|{profiles}",
verge.tray_proxy_groups_display_mode.as_deref().unwrap_or("default"),
verge.tray_inline_outbound_modes.unwrap_or(false),
verge.language.as_deref().unwrap_or(""),
)
.into()
}
/// Group/node identity plus ordering, canonicalized so it is stable across fetches.
pub(super) fn proxy_structure_signature(
proxy_mode: &str,
order_map: Option<&HashMap<String, usize>>,
proxy_nodes_data: &Proxies,
) -> String {
let mut parts: Vec<std::string::String> = proxy_nodes_data
.proxies
.iter()
.filter(|(name, data)| {
let global = name.as_str() == "GLOBAL";
(match proxy_mode {
"global" => global,
_ => !global,
}) && !data.hidden.unwrap_or_default()
})
.filter_map(|(name, data)| {
data.all.as_ref().map(|all| {
let order = order_map
.and_then(|map| map.get(name.as_str()))
.map_or_else(|| "-".to_owned(), |index| index.to_string());
format!(
"{}@{order}:{}",
name,
all.iter().map(|node| node.as_str()).collect::<Vec<_>>().join(",")
)
})
})
.collect();
parts.sort();
parts.join(";").into()
}
pub(super) struct ReconcileContext<'a> {
pub(super) app_handle: &'a AppHandle<Wry>,
pub(super) tray: &'a TrayIcon<Wry>,
pub(super) options: TrayMenuOptions<'a>,
pub(super) verge: &'a IVerge,
pub(super) profiles: &'a IProfiles,
pub(super) fetched_proxies: Option<&'a Proxies>,
pub(super) order_map: Option<&'a HashMap<String, usize>>,
pub(super) menu_signature: &'a str,
pub(super) proxy_signature: Option<&'a str>,
pub(super) fetched: bool,
}
pub(super) fn reconcile(state_store: &Mutex<Option<TrayMenuState>>, ctx: ReconcileContext<'_>) -> anyhow::Result<()> {
let tun_mode_available = crate::core::runstate::RUN_STATE.state().tun_capable();
{
let mut attached = state_store.lock();
if let Some(state) = attached.as_mut().filter(|state| state.signature == ctx.menu_signature) {
let in_place = !ctx.fetched || state.proxy_signature.as_deref() == ctx.proxy_signature;
let swapped = !in_place
&& ctx.fetched_proxies.is_some_and(|data| {
state.swap_proxy_section(
ctx.app_handle,
ctx.options.mode.unwrap_or(""),
ctx.order_map,
data,
ctx.proxy_signature,
)
});
if in_place || swapped {
state.apply_static(&ctx.options, ctx.profiles, tun_mode_available);
if in_place && let Some(data) = ctx.fetched_proxies {
state.apply_proxy(data);
}
return Ok(());
}
}
}
let (menu, mut new_state) = build_tray_menu(
ctx.app_handle,
ctx.options,
ctx.verge,
ctx.profiles,
ctx.order_map,
ctx.fetched_proxies,
)?;
new_state.signature = ctx.menu_signature.into();
new_state.proxy_signature = if ctx.fetched {
ctx.proxy_signature.map(Into::into)
} else {
None
};
logging_error!(Type::Tray, ctx.tray.set_menu(Some(menu)));
*state_store.lock() = Some(new_state);
Ok(())
}
impl TrayMenuState {
fn apply_static(&self, options: &TrayMenuOptions<'_>, profiles: &IProfiles, tun_mode_available: bool) {
let mode = options.mode.unwrap_or("");
for (item, name) in self.mode_items.iter().zip(["rule", "global", "direct"]) {
logging_error!(Type::Tray, item.set_checked(mode == name));
}
logging_error!(
Type::Tray,
self.system_proxy_item.set_checked(options.system_proxy_enabled)
);
logging_error!(Type::Tray, self.tun_item.set_enabled(tun_mode_available));
logging_error!(Type::Tray, self.tun_item.set_checked(options.tun_mode_enabled));
logging_error!(
Type::Tray,
self.lightweight_item.set_checked(options.is_lightweight_mode)
);
let current = profiles.current.as_deref();
for (uid, item) in &self.profile_items {
logging_error!(Type::Tray, item.set_checked(current == Some(uid.as_str())));
}
}
fn apply_proxy(&mut self, proxy_nodes_data: &Proxies) {
for group in self.proxy_groups.iter_mut() {
let Some(group_data) = proxy_nodes_data.proxies.get(group.name.as_str()) else {
continue;
};
let now = group_data.now.as_deref().unwrap_or_default();
for node in group.nodes.iter_mut() {
let text: String = format!(
"{} | {}",
node.name,
proxy_delay_text(proxy_nodes_data, node.name.as_str())
)
.into();
if node.text != text {
logging_error!(Type::Tray, node.item.set_text(&text));
node.text = text;
}
let checked = node.name == now;
if node.checked != checked {
logging_error!(Type::Tray, node.item.set_checked(checked));
node.checked = checked;
}
}
}
}
fn swap_proxy_section(
&mut self,
app_handle: &AppHandle<Wry>,
proxy_mode: &str,
order_map: Option<&HashMap<String, usize>>,
proxy_nodes_data: &Proxies,
proxy_signature: Option<&str>,
) -> bool {
let Some(proxies_submenu) = self.proxies_submenu.clone() else {
return false;
};
let (group_menus, group_state) =
create_subcreate_proxy_menu_item(app_handle, proxy_mode, order_map, proxy_nodes_data);
if group_menus.is_empty() {
return false;
}
while proxies_submenu.remove_at(0).is_ok_and(|item| item.is_some()) {}
let group_menus_refs: Vec<&dyn IsMenuItem<Wry>> =
group_menus.iter().map(|menu| menu as &dyn IsMenuItem<Wry>).collect();
logging_error!(Type::Tray, proxies_submenu.append_items(&group_menus_refs));
self.proxy_groups = group_state;
self.proxy_signature = proxy_signature.map(Into::into);
true
}
}
+214 -166
View File
@@ -1,4 +1,4 @@
use crate::config::{IProfilePreview, IVerge};
use crate::config::{IProfilePreview, IProfiles, IVerge};
use crate::core::lightweight;
use crate::core::tray::menu_def::TrayAction;
use crate::process::AsyncHandler;
@@ -13,6 +13,7 @@ use crate::{
};
use clash_verge_limiter::{Limiter, SystemClock, SystemLimiter};
use clash_verge_logging::logging_error;
use parking_lot::Mutex;
use tauri::tray::{MouseButton, MouseButtonState, TrayIcon, TrayIconBuilder, TrayIconEvent};
use tauri_plugin_mihomo::models::Proxies;
use tokio::fs;
@@ -29,9 +30,14 @@ use tauri::{
};
mod menu_def;
mod menu_state;
#[cfg(target_os = "macos")]
mod speed_task;
use menu_def::{MenuIds, MenuTexts};
use menu_state::{
ProxyGroupItems, ProxyNodeItem, ReconcileContext, TrayMenuState, menu_signature, proxy_delay_text,
proxy_structure_signature, reconcile,
};
// TODO: 是否需要将可变菜单抽离存储起来,后续直接更新对应菜单实例,无需重新创建菜单(待考虑)
@@ -41,7 +47,10 @@ const TRAY_CLICK_DEBOUNCE_MS: u64 = 300;
pub const TRAY_ID: &str = "clash-verge-rev-tray";
#[derive(Clone, Copy)]
struct TrayMenuOptions {
struct TrayMenuOptions<'a> {
mode: Option<&'a str>,
system_proxy_enabled: bool,
tun_mode_enabled: bool,
is_lightweight_mode: bool,
include_proxy_groups: bool,
}
@@ -59,6 +68,7 @@ pub struct Tray {
limiter: SystemLimiter,
#[cfg(target_os = "macos")]
speed_controller: speed_task::TraySpeedController,
menu_state: Mutex<Option<TrayMenuState>>,
}
impl TrayState {
@@ -132,6 +142,7 @@ impl Default for Tray {
limiter: Limiter::new(Duration::from_millis(TRAY_CLICK_DEBOUNCE_MS), SystemClock),
#[cfg(target_os = "macos")]
speed_controller: speed_task::TraySpeedController::new(),
menu_state: Mutex::new(None),
}
}
}
@@ -201,9 +212,8 @@ impl Tray {
};
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 = crate::core::runstate::RUN_STATE.state().tun_capable();
let system_proxy = verge.enable_system_proxy.unwrap_or(false);
let tun_mode = verge.enable_tun_mode.unwrap_or(false);
let mode = {
Config::clash()
.await
@@ -214,29 +224,81 @@ impl Tray {
.unwrap_or("rule")
.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();
logging_error!(
Type::Tray,
tray.set_menu(Some(
create_tray_menu(
app_handle,
Some(mode.as_str()),
*system_proxy,
*tun_mode,
tun_mode_available,
profiles_preview,
TrayMenuOptions {
is_lightweight_mode,
include_proxy_groups,
let fetch_proxy_groups =
include_proxy_groups && verge.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)
};
let verge = if fetch_proxy_groups {
Config::verge().await.latest_arc()
} else {
verge
};
let profiles = Config::profiles().await.latest_arc();
let menu_signature = menu_signature(&verge, &profiles, &mode);
let proxy_signature = proxy_nodes_data
.as_ref()
.map(|data| proxy_structure_signature(&mode, runtime_proxy_groups_order.as_ref(), data));
let (tx, rx) = tokio::sync::oneshot::channel();
let dispatch_handle = app_handle.clone();
app_handle
.run_on_main_thread(move || {
let _ = tx.send(reconcile(
&Self::global().menu_state,
ReconcileContext {
app_handle: &dispatch_handle,
tray: &tray,
options: TrayMenuOptions {
mode: Some(mode.as_str()),
system_proxy_enabled: system_proxy,
tun_mode_enabled: tun_mode,
is_lightweight_mode,
include_proxy_groups,
},
verge: &verge,
profiles: &profiles,
fetched_proxies: proxy_nodes_data.as_ref(),
order_map: runtime_proxy_groups_order.as_ref(),
menu_signature: &menu_signature,
proxy_signature: proxy_signature.as_deref(),
fetched: fetch_proxy_groups,
},
)
.await?,
))
);
));
})
.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, "托盘菜单更新成功");
Ok(())
@@ -422,11 +484,12 @@ fn create_hotkeys(hotkeys: &Option<Vec<String>>) -> HashMap<&str, &str> {
let mut parts = item.split(',');
match (parts.next(), parts.next()) {
(Some(func), Some(key)) => {
// 托盘菜单的 `accelerator` 在 Linux/Windows 下不支持小键盘按键,Windows 下也不支持 Win 键(前端上报为 `CMD`)
let uppercase_key = key.to_uppercase();
let unsupported =
uppercase_key.contains("NUMPAD") || (cfg!(windows) && uppercase_key.contains("CMD"));
if unsupported { None } else { Some((func, key)) }
// 托盘菜单中的 `accelerator` 属性,在 Linux/Windows 中都不支持小键盘按键的解析
if key.to_uppercase().contains("NUMPAD") {
None
} else {
Some((func, key))
}
}
_ => None,
}
@@ -439,7 +502,7 @@ fn create_hotkeys(hotkeys: &Option<Vec<String>>) -> HashMap<&str, &str> {
fn create_profile_menu_item(
app_handle: &AppHandle,
profiles_preview: Vec<IProfilePreview<'_>>,
) -> Result<Vec<CheckMenuItem<Wry>>> {
) -> Result<Vec<(String, CheckMenuItem<Wry>)>> {
profiles_preview
.into_iter()
.map(|profile| {
@@ -451,6 +514,7 @@ fn create_profile_menu_item(
profile.is_current,
None::<&str>,
)
.map(|item| (profile.uid.clone(), item))
.map_err(|e| e.into())
})
.collect()
@@ -459,93 +523,96 @@ fn create_profile_menu_item(
fn create_subcreate_proxy_menu_item(
app_handle: &AppHandle,
proxy_mode: &str,
proxy_group_order_map: Option<HashMap<String, usize>>,
proxy_nodes_data: Option<Proxies>,
) -> Vec<Submenu<Wry>> {
let proxy_submenus: Vec<Submenu<Wry>> = {
let mut submenus: Vec<(String, usize, Submenu<Wry>)> = Vec::new();
proxy_group_order_map: Option<&HashMap<String, usize>>,
proxy_nodes_data: &Proxies,
) -> (Vec<Submenu<Wry>>, Vec<ProxyGroupItems>) {
let mut submenus: Vec<(String, usize, Submenu<Wry>, Vec<ProxyNodeItem>)> = Vec::new();
// TODO: 应用启动时,内核还未启动完全,无法获取代理节点信息
if let Some(proxy_nodes_data) = proxy_nodes_data {
for (group_name, group_data) in proxy_nodes_data.proxies.iter() {
let should_show = match proxy_mode {
"global" => group_name == "GLOBAL",
_ => group_name != "GLOBAL",
} && !group_data.hidden.unwrap_or_default();
// TODO: 应用启动时,内核还未启动完全,无法获取代理节点信息
for (group_name, group_data) in proxy_nodes_data.proxies.iter() {
let should_show = match proxy_mode {
"global" => group_name == "GLOBAL",
_ => group_name != "GLOBAL",
} && !group_data.hidden.unwrap_or_default();
if !should_show {
continue;
}
let Some(all_proxies) = group_data.all.as_ref() else {
continue;
};
let now_proxy = group_data.now.as_deref().unwrap_or_default();
let group_items: Vec<CheckMenuItem<Wry>> = all_proxies
.iter()
.filter_map(|proxy_str| {
let is_selected = *proxy_str == now_proxy;
let item_id = format!("proxy_{}_{}", group_name, proxy_str);
let delay_text = proxy_nodes_data
.proxies
.get(proxy_str)
.and_then(|h| h.history.last())
.map(|h| match h.delay {
0 => "-ms".into(),
delay if delay >= 10000 => "-ms".into(),
_ => format!("{}ms", h.delay),
})
.unwrap_or_else(|| "-ms".into());
let display_text = format!("{} | {}", proxy_str, delay_text);
CheckMenuItem::with_id(app_handle, item_id, display_text, true, is_selected, None::<&str>)
.map_err(|e| logging!(warn, Type::Tray, "Failed to create proxy menu item: {}", e))
.ok()
})
.collect();
if group_items.is_empty() {
continue;
}
let group_display_name = group_name.to_string();
let group_items_refs: Vec<&dyn IsMenuItem<Wry>> =
group_items.iter().map(|item| item as &dyn IsMenuItem<Wry>).collect();
if let Ok(submenu) = Submenu::with_id_and_items(
app_handle,
format!("proxy_group_{}", group_name),
group_display_name,
true,
&group_items_refs,
) {
let insertion_index = submenus.len();
submenus.push((group_name.into(), insertion_index, submenu));
} else {
logging!(warn, Type::Tray, "Failed to create proxy group submenu: {}", group_name);
if !should_show {
continue;
}
let Some(all_proxies) = group_data.all.as_ref() else {
continue;
};
let now_proxy = group_data.now.as_deref().unwrap_or_default();
let mut group_items: Vec<CheckMenuItem<Wry>> = Vec::with_capacity(all_proxies.len());
let mut nodes: Vec<ProxyNodeItem> = Vec::with_capacity(all_proxies.len());
for proxy_str in all_proxies {
let is_selected = proxy_str == now_proxy;
let item_id = format!("proxy_{}_{}", group_name, proxy_str);
let display_text = format!(
"{} | {}",
proxy_str,
proxy_delay_text(proxy_nodes_data, proxy_str.as_str())
);
match CheckMenuItem::with_id(app_handle, item_id, &display_text, true, is_selected, None::<&str>) {
Ok(item) => {
group_items.push(item.clone());
nodes.push(ProxyNodeItem {
name: proxy_str.clone().into(),
item,
text: display_text.as_str().into(),
checked: is_selected,
});
}
Err(e) => logging!(warn, Type::Tray, "Failed to create proxy menu item: {}", e),
}
}
if let Some(order_map) = proxy_group_order_map.as_ref() {
submenus.sort_by(|(name_a, original_index_a, _), (name_b, original_index_b, _)| {
match (order_map.get(name_a), order_map.get(name_b)) {
(Some(index_a), Some(index_b)) => index_a.cmp(index_b),
(Some(_), None) => std::cmp::Ordering::Less,
(None, Some(_)) => std::cmp::Ordering::Greater,
(None, None) => original_index_a.cmp(original_index_b),
}
});
if group_items.is_empty() {
continue;
}
submenus.into_iter().map(|(_, _, submenu)| submenu).collect()
};
proxy_submenus
let group_display_name = group_name.to_string();
let group_items_refs: Vec<&dyn IsMenuItem<Wry>> =
group_items.iter().map(|item| item as &dyn IsMenuItem<Wry>).collect();
if let Ok(submenu) = Submenu::with_id_and_items(
app_handle,
format!("proxy_group_{}", group_name),
group_display_name,
true,
&group_items_refs,
) {
let insertion_index = submenus.len();
submenus.push((group_name.into(), insertion_index, submenu, nodes));
} else {
logging!(warn, Type::Tray, "Failed to create proxy group submenu: {}", group_name);
}
}
if let Some(order_map) = proxy_group_order_map {
submenus.sort_by(|(name_a, original_index_a, _, _), (name_b, original_index_b, _, _)| {
match (order_map.get(name_a), order_map.get(name_b)) {
(Some(index_a), Some(index_b)) => index_a.cmp(index_b),
(Some(_), None) => std::cmp::Ordering::Less,
(None, Some(_)) => std::cmp::Ordering::Greater,
(None, None) => original_index_a.cmp(original_index_b),
}
});
}
let mut groups = Vec::with_capacity(submenus.len());
let group_menus = submenus
.into_iter()
.map(|(name, _, submenu, nodes)| {
groups.push(ProxyGroupItems { name, nodes });
submenu
})
.collect();
(group_menus, groups)
}
fn create_proxy_menu_item(
@@ -584,53 +651,16 @@ fn create_proxy_menu_item(
Ok((proxies_submenu, inline_proxy_items))
}
async fn create_tray_menu(
fn build_tray_menu(
app_handle: &AppHandle,
mode: Option<&str>,
system_proxy_enabled: bool,
tun_mode_enabled: bool,
tun_mode_available: bool,
profiles_preview: Vec<IProfilePreview<'_>>,
options: TrayMenuOptions,
) -> Result<tauri::menu::Menu<Wry>> {
let current_proxy_mode = mode.unwrap_or("");
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();
}
options: TrayMenuOptions<'_>,
verge_settings: &IVerge,
profiles_config: &IProfiles,
proxy_group_order_map: Option<&HashMap<String, usize>>,
proxy_nodes_data: Option<&Proxies>,
) -> Result<(tauri::menu::Menu<Wry>, TrayMenuState)> {
let current_proxy_mode = options.mode.unwrap_or("");
let tun_mode_available = crate::core::runstate::RUN_STATE.state().tun_capable();
let tray_proxy_groups_display_mode = verge_settings
.tray_proxy_groups_display_mode
@@ -638,20 +668,19 @@ async fn create_tray_menu(
.unwrap_or("default");
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 version = env!("CARGO_PKG_VERSION");
let hotkeys = create_hotkeys(&verge_settings.hotkeys);
let profile_menu_items: Vec<CheckMenuItem<Wry>> = create_profile_menu_item(app_handle, profiles_preview)?;
let profiles_preview = profiles_config.profiles_preview().unwrap_or_default();
let profile_menu_items: Vec<(String, CheckMenuItem<Wry>)> = create_profile_menu_item(app_handle, profiles_preview)?;
let texts = MenuTexts::new();
let profile_menu_items_refs: Vec<&dyn IsMenuItem<Wry>> = profile_menu_items
.iter()
.map(|item| item as &dyn IsMenuItem<Wry>)
.map(|(_, item)| item as &dyn IsMenuItem<Wry>)
.collect();
let open_window = &MenuItem::with_id(
@@ -719,12 +748,20 @@ async fn create_tray_menu(
&profile_menu_items_refs,
)?;
let (proxies_menu, inline_proxy_items) = if include_proxy_groups {
let proxy_sub_menus =
let mut proxy_groups: Vec<ProxyGroupItems> = Vec::new();
let mut proxies_submenu_handle: Option<Submenu<Wry>> = None;
let (proxies_menu, inline_proxy_items) = if include_proxy_groups && let Some(proxy_nodes_data) = proxy_nodes_data {
let (proxy_sub_menus, groups) =
create_subcreate_proxy_menu_item(app_handle, current_proxy_mode, proxy_group_order_map, proxy_nodes_data);
proxy_groups = groups;
match tray_proxy_groups_display_mode {
"default" => create_proxy_menu_item(app_handle, false, proxy_sub_menus, &texts.proxies)?,
"default" => {
let (submenu, inline_items) =
create_proxy_menu_item(app_handle, false, proxy_sub_menus, &texts.proxies)?;
proxies_submenu_handle = submenu.clone();
(submenu, inline_items)
}
"inline" => create_proxy_menu_item(app_handle, true, proxy_sub_menus, &texts.proxies)?,
_ => (None, Vec::new()),
}
@@ -737,7 +774,7 @@ async fn create_tray_menu(
MenuIds::SYSTEM_PROXY,
&texts.system_proxy,
true,
system_proxy_enabled,
options.system_proxy_enabled,
hotkeys.get("toggle_system_proxy").copied(),
)?;
@@ -746,7 +783,7 @@ async fn create_tray_menu(
MenuIds::TUN_MODE,
&texts.tun_mode,
tun_mode_available,
tun_mode_enabled,
options.tun_mode_enabled,
hotkeys.get("toggle_tun_mode").copied(),
)?;
@@ -865,7 +902,18 @@ async fn create_tray_menu(
]);
let menu = tauri::menu::MenuBuilder::new(app_handle).items(&menu_items).build()?;
Ok(menu)
let state = TrayMenuState {
mode_items: [rule_mode.clone(), global_mode.clone(), direct_mode.clone()],
system_proxy_item: system_proxy.clone(),
tun_item: tun_mode.clone(),
lightweight_item: lightweight_mode.clone(),
profile_items: profile_menu_items,
proxies_submenu: proxies_submenu_handle,
proxy_groups,
signature: String::new(),
proxy_signature: None,
};
Ok((menu, state))
}
fn on_tray_icon_event(_tray_icon: &TrayIcon, tray_event: TrayIconEvent) {