Compare commits

..
Author SHA1 Message Date
oomeow da050d3e7e docs: update Changelog.md 2026-10-13 19:55:45 +08:00
oomeow 9a7b15eb15 fix(setting): default system titlebar to enabled if undefined 2026-10-12 21:17:53 +08:00
oomeow 98072a17a3 feat(bindings): add enable_system_titlebar option to Verge types 2026-10-12 21:16:06 +08:00
oomeow 32a093e44b Merge remote-tracking branch 'origin/dev' into refactor/custom-titlebar 2026-10-12 20:59:57 +08:00
oomeow 7ecec42f4d feat(window): add persistent system titlebar setting
- persist enable_system_titlebar in verge config and apply it to window decorations
- replace window provider/context with useWindowMaximized hook and inline controls
- add i18n field and tooltip for the setting across locales
2026-10-12 20:05:02 +08:00
Huanhuan Liand❤是纱雾酱哟~ 3693efcf6c fix(tests): reset test item state when delay test fails (#8231)
* fix(tests): reset test item state when delay test fails

* docs(changelog): note test-item stuck-state fix

Co-authored-by: ❤是纱雾酱哟~ <49941141+Dragon1573@users.noreply.github.com>
Signed-off-by: ❤是纱雾酱哟~ <49941141+Dragon1573@users.noreply.github.com>

---------

Signed-off-by: ❤是纱雾酱哟~ <49941141+Dragon1573@users.noreply.github.com>
Co-authored-by: ❤是纱雾酱哟~ <49941141+Dragon1573@users.noreply.github.com>
2026-10-12 10:25:58 +00:00
53 changed files with 968 additions and 830 deletions
Generated
+439 -225
View File
File diff suppressed because it is too large Load Diff
+3 -12
View File
@@ -49,15 +49,7 @@ split-debuginfo = "unpacked"
[patch.crates-io]
# Backport Windows menu command ID recycling on the muda 0.20 compatibility branch.
muda = { git = "https://github.com/wonfen/muda.git", rev = "547de49352791653fbdfa785f20f3c7ece8468e2" }
# tauri-specta has no tauri 3 release yet; ride the tauri team's port and the
# unpublished specta rc.26 line it pins. Drop all six entries once both ship
# on crates.io.
tauri-specta = { git = "https://github.com/tauri-apps/tauri-specta", branch = "tauri-v3" }
specta = { git = "https://github.com/specta-rs/specta.git", rev = "2757fa8899926dce7da999780c7edf2dc08a732a" }
specta-macros = { git = "https://github.com/specta-rs/specta.git", rev = "2757fa8899926dce7da999780c7edf2dc08a732a" }
specta-serde = { git = "https://github.com/specta-rs/specta.git", rev = "2757fa8899926dce7da999780c7edf2dc08a732a" }
specta-typescript = { git = "https://github.com/specta-rs/specta.git", rev = "2757fa8899926dce7da999780c7edf2dc08a732a" }
specta-util = { git = "https://github.com/specta-rs/specta.git", rev = "2757fa8899926dce7da999780c7edf2dc08a732a" }
[workspace.dependencies]
clash-verge-draft = { path = "crates/clash-verge-draft" }
clash-verge-media-unlock = { path = "crates/clash-verge-media-unlock" }
@@ -69,9 +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 = "3.0.0-alpha.4" }
tauri-runtime-wry = "3.0.0-alpha.4"
tauri-plugin-clipboard-manager = "3.0.0-alpha.2"
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"
+2
View File
@@ -4,6 +4,7 @@
<summary><strong> 🐞 修复问题 </strong></summary>
- 修复通过 URL 导入订阅时,即使导入的订阅并非当前使用的订阅,也会重新加载当前配置的问题
- 修复「首页」网站测速失败后卡片一直显示测试中的问题
</details>
@@ -16,6 +17,7 @@
<details>
<summary><strong> 🚀 优化改进 </strong></summary>
- 记住「启用系统标题栏」设置,Windows/macOS 即时生效,Linux 重启后生效
**🖥️ Windows**
+1 -1
View File
@@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2024"
[dependencies]
specta = { version = "=2.0.0-rc.26", features = ["derive", "function"] }
specta = { version = "=2.0.0-rc.25", features = ["derive", "function"] }
clash-verge-logging = { workspace = true }
chrono = { workspace = true }
log = { workspace = true }
@@ -5,7 +5,7 @@ edition = "2024"
rust-version = "1.91"
[dependencies]
specta = { version = "=2.0.0-rc.26", features = ["derive", "function"] }
specta = { version = "=2.0.0-rc.25", features = ["derive", "function"] }
tauri = { workspace = true }
tauri-plugin-clipboard-manager = { workspace = true }
parking_lot = { workspace = true }
+8 -8
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": "3.0.0-alpha.2",
"@tauri-apps/plugin-clipboard-manager": "^3.0.0-alpha.2",
"@tauri-apps/plugin-dialog": "^3.0.0-alpha.3",
"@tauri-apps/plugin-fs": "^3.0.0-alpha.2",
"@tauri-apps/plugin-http": "~3.0.0-alpha.2",
"@tauri-apps/plugin-opener": "3.0.0-alpha.2",
"@tauri-apps/plugin-updater": "3.0.0-alpha.2",
"@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": "3.0.0-alpha.4",
"@tauri-apps/cli": "2.12.1",
"@types/lodash-es": "^4.17.12",
"@types/node": "^24.13.6",
"@types/react": "19.3.0",
+87 -92
View File
@@ -200,26 +200,26 @@ importers:
specifier: ^3.14.13
version: 3.14.13(react-dom@19.3.0(react@19.3.0))(react@19.3.0)
'@tauri-apps/api':
specifier: 3.0.0-alpha.2
version: 3.0.0-alpha.2
specifier: 2.12.1
version: 2.12.1
'@tauri-apps/plugin-clipboard-manager':
specifier: ^3.0.0-alpha.2
version: 3.0.0-alpha.2
specifier: 2.4.0
version: 2.4.0
'@tauri-apps/plugin-dialog':
specifier: ^3.0.0-alpha.3
version: 3.0.0-alpha.3
specifier: 2.8.0
version: 2.8.0
'@tauri-apps/plugin-fs':
specifier: ^3.0.0-alpha.2
version: 3.0.0-alpha.2
specifier: 2.6.0
version: 2.6.0
'@tauri-apps/plugin-http':
specifier: ~3.0.0-alpha.2
version: 3.0.0-alpha.2
specifier: 2.8.0
version: 2.8.0
'@tauri-apps/plugin-opener':
specifier: 3.0.0-alpha.2
version: 3.0.0-alpha.2
specifier: 2.7.0
version: 2.7.0
'@tauri-apps/plugin-updater':
specifier: 3.0.0-alpha.2
version: 3.0.0-alpha.2
specifier: 2.13.1
version: 2.13.1
ahooks:
specifier: ^3.10.0
version: 3.10.0(react-dom@19.3.0(react@19.3.0))(react@19.3.0)
@@ -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: 3.0.0-alpha.4
version: 3.0.0-alpha.4
specifier: 2.12.1
version: 2.12.1
'@types/lodash-es':
specifier: ^4.17.12
version: 4.17.12
@@ -2129,102 +2129,99 @@ packages:
'@tauri-apps/api@2.12.1':
resolution: {integrity: sha512-DeyFHa3wynpyoqTDikDEDGTJIq4LQ5USfolQGRmGIWT6JMADyxZBTDa5cAdT3tDg73rUXufPaCwN7aXBos4OnQ==}
'@tauri-apps/api@3.0.0-alpha.2':
resolution: {integrity: sha512-W5MTTRAsbNqxSO5hf6U6gHwtTfW0ZnEyIWkmn9L6XYWFcSi0n1JtVQAIsJ6EZHLpJkchGEFwSeVBy+IcrtXl4g==}
'@tauri-apps/cli-darwin-arm64@3.0.0-alpha.4':
resolution: {integrity: sha512-QSySr8o6f8nD/6e8BG9QYQTExlAhsiM2c6ewNti3kzcVzDh7WvM/Jya93mKhQrSjKumq7bUa/jlgmFB24P2oNA==}
'@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@3.0.0-alpha.4':
resolution: {integrity: sha512-sx0fxH4CEWdWeB8THKzB4mF8P0ZXi1UXO/gVM4dzLjFpHcZbvMJ7B2cBeHNH3ED16aVKbbCGVYLDFVLh56x1Kg==}
'@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@3.0.0-alpha.4':
resolution: {integrity: sha512-eyABwsj3vJiyipJERTJCMYJfguXCxiL94Ux8SP9rwBao7cOEFgonhX2uG57/K4RekmZPFMekcfHp8JMXf88VCQ==}
'@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@3.0.0-alpha.4':
resolution: {integrity: sha512-HmYCfjTlpn3mwMh5A2PeZud6QBZJbstTQDRCbs6ikd8t9GurSGTJINVbONrauNKUXYd37c8h5eEERRrkzqpK+w==}
'@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@3.0.0-alpha.4':
resolution: {integrity: sha512-BgD2YHYkOLKJ8qIpiwnYmq1xMKhxwGEOeX+HRjyrtIb4Eonf4/qGKfH7AoN88MUjpUfm+Zn3AZ82bQBAFDhV9Q==}
'@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@3.0.0-alpha.4':
resolution: {integrity: sha512-AHl6DFQEtNDaI2YuIqNeSm7G3RWlHSz2R23+j1woFMXZjR48Dd7xSxthWybnxgy09Qw//8OeE5LX0u9oaC07ew==}
'@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@3.0.0-alpha.4':
resolution: {integrity: sha512-sfLIf/Dr3fio2gGAIXvjDdhESeQDyVKTTuhXfKV361gcTcx+/OkkctVw1FBKgdtRqDP0JVQI9y8sXdhuL0F61A==}
'@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@3.0.0-alpha.4':
resolution: {integrity: sha512-3aY8+4mvts+dia8VXfGhJYxhhU57fdcbL7IxKLrTt4ASrsXwCDspVf+yVfa0GKpsUWPIxsGA7dYt9oaviv8gkg==}
'@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@3.0.0-alpha.4':
resolution: {integrity: sha512-m3hVaoWavJBGHrKPGjhyCVW+IIbXMTP2CvqqG7Ak6yoVJLEYYTwkwfEq/dMHpX9Spw9iQE5pONK1vTatcJ/jPQ==}
'@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@3.0.0-alpha.4':
resolution: {integrity: sha512-v8J3IgwgFsOiuDPLCScYGAQjQZKj+46o1mMRVcx4gYo1JV9P6o/hblJX5nMT/oTTGdsAgao5PZNxUlX8WJGMFA==}
'@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@3.0.0-alpha.4':
resolution: {integrity: sha512-BfEHaKBdNImxb9KOVji9hROEVnPWl2ImshQmV9ASC61jraX2/xCJ+YNdaAi7yA18qAyQVb/CszsfhFWJHT+YbA==}
'@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@3.0.0-alpha.4':
resolution: {integrity: sha512-hu2CcWxH6yeo5CfLkEYvDOHW5aKdh0gY5DobR5UK+psBInfsc/qKetD0HA+Z41WvVQZsHJXk2uvPMUZP2ckAJg==}
'@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@3.0.0-alpha.2':
resolution: {integrity: sha512-87oq7PUtHZk0PZgvIp5KWsXWOIJtUz3qEiL+PFvV/dC4T4EByi+dx4/hZ+/BMiX7lchknPmA6WTmmx+530Xjgw==}
'@tauri-apps/plugin-clipboard-manager@2.4.0':
resolution: {integrity: sha512-yAuTffXc/sm+4oYnrwxbUkaJXQMTesPJ9dim0ADcA31rpboyhAN2TGQqs9VCcPIpb60LFOeePkvhpP2I3xhejg==}
'@tauri-apps/plugin-dialog@3.0.0-alpha.3':
resolution: {integrity: sha512-LjP/CwCaQ/uVhIjFbMG1JM9MY9+gvNchoRsQoOQpm1+y8dWKCb4/VFFXv+RuH6XbR11eIU6G+yL34hggc7iw2Q==}
'@tauri-apps/plugin-dialog@2.8.0':
resolution: {integrity: sha512-HSSD5C8UFE9ijD1wue84F7jjaIReFmj8AAPtS+xyjYRltJafIc4BiWHY7SJ6vbbew6u+YljEJHfyNfy5VwywXg==}
'@tauri-apps/plugin-fs@3.0.0-alpha.2':
resolution: {integrity: sha512-w/pmQ5swWqEgQhWMabreK5eMIiAHXHFKdb/SWGrZF4tTpVky/dUAyScFl7XiFRKpfr5cHSDiZAP/dsg0Ud9p9w==}
'@tauri-apps/plugin-fs@2.6.0':
resolution: {integrity: sha512-MhBAZ4rryd+h9fijd/GWDfArqiAeGI0ZNduV4wuRyUVI6wDZ+oDBY7atqtoXKDwlTUVSvpvmWJAY0ehQd/v6Uw==}
'@tauri-apps/plugin-http@3.0.0-alpha.2':
resolution: {integrity: sha512-imgCIw601u03ahDFu+ifiOKMAU6zab0taYp6h6Z7O9TcsU1SRqgXxbVcO84iLQ4bjELiFdAtUp0xqiNx+r8fvg==}
'@tauri-apps/plugin-http@2.8.0':
resolution: {integrity: sha512-cPvZvfUSaBkqGxef/dgbh/iEl6T3jQfSJyypi9OdcWr1VcuTt4ZRlk9DLAHEHF2q2NML+0UOkjyXRPNo6Cky6g==}
'@tauri-apps/plugin-opener@3.0.0-alpha.2':
resolution: {integrity: sha512-T3Y7Y/m+XybZqB2cI6SZD8St47GBONPTbFp+hSYFLHy5Lh0eNh0sv9TRyi7MElzUtUx0NWZltAOHuHzeo2UfWA==}
'@tauri-apps/plugin-opener@2.7.0':
resolution: {integrity: sha512-mBorYfVKh9Lt7s4ZFtKbMUTxBw+Y/sVmSlKCtkkoBgMTC/DIbt/kaPm67L/2djAUqDI1wbixy5OHHVmipjuGMw==}
'@tauri-apps/plugin-updater@3.0.0-alpha.2':
resolution: {integrity: sha512-WHRIi50gV9M2JDx9VZLxlpcuA3gvd7eABaqfNQEQTdKUSuIWyS/gQcbH5LpiuSWSkCTMMInUUBmost2LdZnnkA==}
'@tauri-apps/plugin-updater@2.13.1':
resolution: {integrity: sha512-+STDzJ0sdQLImjc63zkv6Nncimqeo6jyCWoTQtbSwgGLNj1qbP9I6LC4p5R5ocBk3gNKAZs6i/AJz0jxysytiw==}
'@tybys/wasm-util@0.10.4':
resolution: {integrity: sha512-W3c4gRigFS0T/Ma4qIYF3GDAc5AQdHb1yL5znJT1Zv1YaD9Kitx656wBjvr19qbiosmZT8lWDM5BEMynUqX65A==}
@@ -6910,78 +6907,76 @@ snapshots:
'@tauri-apps/api@2.12.1': {}
'@tauri-apps/api@3.0.0-alpha.2': {}
'@tauri-apps/cli-darwin-arm64@3.0.0-alpha.4':
'@tauri-apps/cli-darwin-arm64@2.12.1':
optional: true
'@tauri-apps/cli-darwin-x64@3.0.0-alpha.4':
'@tauri-apps/cli-darwin-x64@2.12.1':
optional: true
'@tauri-apps/cli-linux-arm-gnueabihf@3.0.0-alpha.4':
'@tauri-apps/cli-linux-arm-gnueabihf@2.12.1':
optional: true
'@tauri-apps/cli-linux-arm64-gnu@3.0.0-alpha.4':
'@tauri-apps/cli-linux-arm64-gnu@2.12.1':
optional: true
'@tauri-apps/cli-linux-arm64-musl@3.0.0-alpha.4':
'@tauri-apps/cli-linux-arm64-musl@2.12.1':
optional: true
'@tauri-apps/cli-linux-riscv64-gnu@3.0.0-alpha.4':
'@tauri-apps/cli-linux-riscv64-gnu@2.12.1':
optional: true
'@tauri-apps/cli-linux-x64-gnu@3.0.0-alpha.4':
'@tauri-apps/cli-linux-x64-gnu@2.12.1':
optional: true
'@tauri-apps/cli-linux-x64-musl@3.0.0-alpha.4':
'@tauri-apps/cli-linux-x64-musl@2.12.1':
optional: true
'@tauri-apps/cli-win32-arm64-msvc@3.0.0-alpha.4':
'@tauri-apps/cli-win32-arm64-msvc@2.12.1':
optional: true
'@tauri-apps/cli-win32-ia32-msvc@3.0.0-alpha.4':
'@tauri-apps/cli-win32-ia32-msvc@2.12.1':
optional: true
'@tauri-apps/cli-win32-x64-msvc@3.0.0-alpha.4':
'@tauri-apps/cli-win32-x64-msvc@2.12.1':
optional: true
'@tauri-apps/cli@3.0.0-alpha.4':
'@tauri-apps/cli@2.12.1':
optionalDependencies:
'@tauri-apps/cli-darwin-arm64': 3.0.0-alpha.4
'@tauri-apps/cli-darwin-x64': 3.0.0-alpha.4
'@tauri-apps/cli-linux-arm-gnueabihf': 3.0.0-alpha.4
'@tauri-apps/cli-linux-arm64-gnu': 3.0.0-alpha.4
'@tauri-apps/cli-linux-arm64-musl': 3.0.0-alpha.4
'@tauri-apps/cli-linux-riscv64-gnu': 3.0.0-alpha.4
'@tauri-apps/cli-linux-x64-gnu': 3.0.0-alpha.4
'@tauri-apps/cli-linux-x64-musl': 3.0.0-alpha.4
'@tauri-apps/cli-win32-arm64-msvc': 3.0.0-alpha.4
'@tauri-apps/cli-win32-ia32-msvc': 3.0.0-alpha.4
'@tauri-apps/cli-win32-x64-msvc': 3.0.0-alpha.4
'@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@3.0.0-alpha.2':
'@tauri-apps/plugin-clipboard-manager@2.4.0':
dependencies:
'@tauri-apps/api': 3.0.0-alpha.2
'@tauri-apps/api': 2.12.1
'@tauri-apps/plugin-dialog@3.0.0-alpha.3':
'@tauri-apps/plugin-dialog@2.8.0':
dependencies:
'@tauri-apps/api': 3.0.0-alpha.2
'@tauri-apps/api': 2.12.1
'@tauri-apps/plugin-fs@3.0.0-alpha.2':
'@tauri-apps/plugin-fs@2.6.0':
dependencies:
'@tauri-apps/api': 3.0.0-alpha.2
'@tauri-apps/api': 2.12.1
'@tauri-apps/plugin-http@3.0.0-alpha.2':
'@tauri-apps/plugin-http@2.8.0':
dependencies:
'@tauri-apps/api': 3.0.0-alpha.2
'@tauri-apps/api': 2.12.1
'@tauri-apps/plugin-opener@3.0.0-alpha.2':
'@tauri-apps/plugin-opener@2.7.0':
dependencies:
'@tauri-apps/api': 3.0.0-alpha.2
'@tauri-apps/api': 2.12.1
'@tauri-apps/plugin-updater@3.0.0-alpha.2':
'@tauri-apps/plugin-updater@2.13.1':
dependencies:
'@tauri-apps/api': 3.0.0-alpha.2
'@tauri-apps/api': 2.12.1
'@tybys/wasm-util@0.10.4':
dependencies:
-12
View File
@@ -10,15 +10,3 @@ allowBuilds:
unrs-resolver: true
minimumReleaseAgeExclude:
- dayjs@1.11.21
- '@tauri-apps/cli-darwin-arm64@3.0.0-alpha.4'
- '@tauri-apps/cli-darwin-x64@3.0.0-alpha.4'
- '@tauri-apps/cli-linux-arm-gnueabihf@3.0.0-alpha.4'
- '@tauri-apps/cli-linux-arm64-gnu@3.0.0-alpha.4'
- '@tauri-apps/cli-linux-arm64-musl@3.0.0-alpha.4'
- '@tauri-apps/cli-linux-riscv64-gnu@3.0.0-alpha.4'
- '@tauri-apps/cli-linux-x64-gnu@3.0.0-alpha.4'
- '@tauri-apps/cli-linux-x64-musl@3.0.0-alpha.4'
- '@tauri-apps/cli-win32-arm64-msvc@3.0.0-alpha.4'
- '@tauri-apps/cli-win32-ia32-msvc@3.0.0-alpha.4'
- '@tauri-apps/cli-win32-x64-msvc@3.0.0-alpha.4'
- '@tauri-apps/cli@3.0.0-alpha.4'
+20 -21
View File
@@ -23,15 +23,13 @@ tauri-dev = ["dep:tauri-plugin-devtools"]
tokio-trace = ["clash-verge-logging/tokio-trace"]
chrome-trace = ["clash-verge-logging/chrome-trace"]
clippy = ["tauri/test"]
# The perf-harness feature and its main.rs wiring are parked until
# tauri-plugin-wdio-webdriver publishes a tauri 3 release; restore both
# from git history together with the crate dependency.
perf-harness = ["dep:tauri-plugin-wdio-webdriver"]
[package.metadata.bundle]
identifier = "io.github.clash-verge-rev.clash-verge-rev"
[build-dependencies]
tauri-build = { version = "3.0.0-alpha.3", features = [] }
tauri-build = { version = "2.7.1", features = [] }
[dependencies]
clash-verge-draft = { workspace = true }
@@ -44,12 +42,12 @@ tauri-plugin-clash-verge-sysinfo = { workspace = true }
tauri-plugin-clipboard-manager = { workspace = true }
tauri = { workspace = true, features = [
"protocol-asset",
"devtools",
"tracing",
"tray-icon",
"image-ico",
"image-png",
] }
tauri-runtime-wry = { workspace = true, features = ["devtools"] }
parking_lot = { workspace = true }
anyhow = { workspace = true }
tokio = { workspace = true }
@@ -75,13 +73,13 @@ sysproxy = { git = "https://github.com/clash-verge-rev/sysproxy-rs.git", branch
"guard",
] }
network-interface = { version = "2.0.5", features = ["serde"] }
tauri-plugin-shell = "3.0.0-alpha.2"
tauri-plugin-opener = "3.0.0-alpha.2"
tauri-plugin-dialog = "3.0.0-alpha.3"
tauri-plugin-fs = "3.0.0-alpha.2"
tauri-plugin-process = "3.0.0-alpha.2"
tauri-plugin-deep-link = "3.0.0-alpha.2"
tauri-plugin-window-state = "3.0.0-alpha.2"
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.1"
tauri-plugin-window-state = "2.5.0"
zip = "9.0.1"
flate2 = "1.1.10"
reqwest_dav = "0.3.3"
@@ -93,13 +91,13 @@ getrandom = "0.4.3"
futures = "0.3.34"
gethostname = "1.1.0"
scopeguard = "1.2.0"
tauri-plugin-notification = "3.0.0-alpha.2"
tauri-plugin-notification = "2.5.1"
tokio-stream = "0.1.19"
backon = { version = "1.6.0", features = ["tokio-sleep"] }
tauri-plugin-http = "3.0.0-alpha.2"
tauri-plugin-http = "2.8.0"
tracing = { workspace = true }
tauri-plugin-devtools = { version = "3.0.0-alpha.0", optional = true }
tauri-plugin-mihomo = { git = "https://github.com/clash-verge-rev/tauri-plugin-mihomo", branch = "feat/tauri-v3" }
tauri-plugin-devtools = { version = "2.2.2", optional = true }
tauri-plugin-mihomo = { git = "https://github.com/clash-verge-rev/tauri-plugin-mihomo", branch = "main" }
async-trait = "0.1.92"
clash_verge_service_ipc = { git = "https://github.com/clash-verge-rev/clash-verge-service-ipc.git", tag = "v2.7.6", version = "2.7.6", features = [
"client",
@@ -111,14 +109,15 @@ webpki-roots = "1.0.9"
dark-light = "3.0.0"
bytes = "1.12.1"
tokio-util = { version = "0.7.19", features = ["time"] }
specta = { version = "=2.0.0-rc.26", features = ["derive", "function", "serde_json"] }
specta = { version = "=2.0.0-rc.25", features = ["derive", "function", "serde_json"] }
tauri-specta = { version = "=2.0.0-rc.25", features = ["derive", "typescript"] }
specta-typescript = "=0.0.13"
specta-typescript = "=0.0.12"
[target.'cfg(unix)'.dependencies]
libc = "0.2.190"
[target.'cfg(target_os = "macos")'.dependencies]
tauri-plugin-wdio-webdriver = { version = "=1.4.0", optional = true }
objc2 = "0.6.5"
objc2-foundation = { version = "0.3.2", features = [
"NSString",
@@ -150,9 +149,9 @@ windows = { version = "0.62.2", features = ["Win32_Globalization"] }
windows-sys.workspace = true
[target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies]
tauri-plugin-autostart = "3.0.0-alpha.2"
tauri-plugin-global-shortcut = "3.0.0-alpha.2"
tauri-plugin-updater = "3.0.0-alpha.2"
tauri-plugin-autostart = "2.7.0"
tauri-plugin-global-shortcut = "2.4.0"
tauri-plugin-updater = "2.13.1"
[dev-dependencies]
criterion = { workspace = true }
+1
View File
@@ -70,6 +70,7 @@
"dialog:allow-open",
"global-shortcut:allow-is-registered",
"global-shortcut:allow-register",
"global-shortcut:allow-register-all",
"global-shortcut:allow-unregister",
"global-shortcut:allow-unregister-all",
"process:allow-exit",
+5
View File
@@ -73,6 +73,9 @@ pub struct IVerge {
pub collapse_navbar: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub enable_system_titlebar: Option<bool>,
pub sysproxy_tray_icon: Option<bool>,
pub tun_tray_icon: Option<bool>,
@@ -374,6 +377,7 @@ impl IVerge {
menu_icon: Some("monochrome".into()),
notice_position: Some("top-right".into()),
collapse_navbar: Some(false),
enable_system_titlebar: Some(true),
common_tray_icon: Some(false),
sysproxy_tray_icon: Some(false),
tun_tray_icon: Some(false),
@@ -461,6 +465,7 @@ impl IVerge {
patch!(proxy_group_tools_position);
patch!(notice_position);
patch!(collapse_navbar);
patch!(enable_system_titlebar);
patch!(common_tray_icon);
patch!(sysproxy_tray_icon);
patch!(tun_tray_icon);
+1 -1
View File
@@ -24,7 +24,7 @@ pub async fn update_launch() -> Result<()> {
#[cfg(not(target_os = "windows"))]
{
let app_handle = Handle::app_handle();
let autostart_manager = app_handle.autostart();
let autostart_manager = app_handle.autolaunch();
if is_enable {
logging_error!(Type::System, "{:?}", autostart_manager.enable());
} else {
+30 -34
View File
@@ -23,9 +23,8 @@ use smartstring::alias::String;
use std::borrow::Cow;
use std::collections::HashMap;
use std::time::Duration;
use tauri::Manager as _;
use tauri::{
AppHandle, DynRuntime,
AppHandle, Wry,
menu::{CheckMenuItem, IsMenuItem, MenuEvent, MenuItem, PredefinedMenuItem, Submenu},
};
@@ -37,7 +36,7 @@ use menu_def::{MenuIds, MenuTexts};
// TODO: 是否需要将可变菜单抽离存储起来,后续直接更新对应菜单实例,无需重新创建菜单(待考虑)
type ProxyMenuItem = (Option<Submenu<DynRuntime>>, Vec<Box<dyn IsMenuItem<DynRuntime>>>);
type ProxyMenuItem = (Option<Submenu<Wry>>, Vec<Box<dyn IsMenuItem<Wry>>>);
const TRAY_CLICK_DEBOUNCE_MS: u64 = 300;
pub const TRAY_ID: &str = "clash-verge-rev-tray";
@@ -58,14 +57,14 @@ enum IconKind {
}
struct LatencyMenuItem {
item: MenuItem<DynRuntime>,
item: MenuItem<Wry>,
running: bool,
proxies: Vec<ProxyLatencyItem>,
provider_state: ProxyViewProviderState,
}
struct ProxyLatencyItem {
item: CheckMenuItem<DynRuntime>,
item: CheckMenuItem<Wry>,
name: std::string::String,
provider_name: Option<std::string::String>,
text: std::string::String,
@@ -567,7 +566,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<DynRuntime>>> {
) -> Result<Vec<CheckMenuItem<Wry>>> {
profiles_preview
.into_iter()
.map(|profile| {
@@ -678,7 +677,7 @@ fn create_subcreate_proxy_menu_item(
texts: &MenuTexts,
verge: &IVerge,
profiles: &crate::config::IProfiles,
) -> Vec<Submenu<DynRuntime>> {
) -> Vec<Submenu<Wry>> {
let Some(view) = view else { return Vec::new() };
view.groups
.iter()
@@ -740,8 +739,8 @@ fn create_subcreate_proxy_menu_item(
},
);
let separator = PredefinedMenuItem::separator(app_handle).ok()?;
let mut items: Vec<&dyn IsMenuItem<DynRuntime>> = vec![&test_item, &separator];
items.extend(group_items.iter().map(|item| item as &dyn IsMenuItem<DynRuntime>));
let mut items: Vec<&dyn IsMenuItem<Wry>> = vec![&test_item, &separator];
items.extend(group_items.iter().map(|item| item as &dyn IsMenuItem<Wry>));
Submenu::with_id_and_items(
app_handle,
format!("proxy_group_{}", group.name),
@@ -758,7 +757,7 @@ fn create_subcreate_proxy_menu_item(
fn create_proxy_menu_item(
app_handle: &AppHandle,
show_proxy_groups_inline: bool,
proxy_submenus: Vec<Submenu<DynRuntime>>,
proxy_submenus: Vec<Submenu<Wry>>,
proxies_text: &str,
) -> Result<ProxyMenuItem> {
let (proxies_submenu, inline_proxy_items) = if show_proxy_groups_inline {
@@ -766,13 +765,13 @@ fn create_proxy_menu_item(
None,
proxy_submenus
.into_iter()
.map(|submenu| Box::new(submenu) as Box<dyn IsMenuItem<DynRuntime>>)
.map(|submenu| Box::new(submenu) as Box<dyn IsMenuItem<Wry>>)
.collect(),
)
} else if !proxy_submenus.is_empty() {
let proxy_submenu_refs: Vec<&dyn IsMenuItem<DynRuntime>> = proxy_submenus
let proxy_submenu_refs: Vec<&dyn IsMenuItem<Wry>> = proxy_submenus
.iter()
.map(|submenu| submenu as &dyn IsMenuItem<DynRuntime>)
.map(|submenu| submenu as &dyn IsMenuItem<Wry>)
.collect();
(
@@ -799,10 +798,7 @@ async fn create_tray_menu(
tun_mode_available: bool,
profiles_preview: Vec<IProfilePreview<'_>>,
options: TrayMenuOptions,
) -> Result<(
tauri::menu::Menu<DynRuntime>,
HashMap<std::string::String, LatencyMenuItem>,
)> {
) -> Result<(tauri::menu::Menu<Wry>, HashMap<std::string::String, LatencyMenuItem>)> {
let current_proxy_mode = mode.unwrap_or("");
let mut verge_settings = Config::verge().await.latest_arc();
@@ -840,12 +836,12 @@ async fn create_tray_menu(
let hotkeys = create_hotkeys(&verge_settings.hotkeys);
let profile_menu_items: Vec<CheckMenuItem<DynRuntime>> = create_profile_menu_item(app_handle, profiles_preview)?;
let profile_menu_items: Vec<CheckMenuItem<Wry>> = create_profile_menu_item(app_handle, profiles_preview)?;
let texts = MenuTexts::new();
let profile_menu_items_refs: Vec<&dyn IsMenuItem<DynRuntime>> = profile_menu_items
let profile_menu_items_refs: Vec<&dyn IsMenuItem<Wry>> = profile_menu_items
.iter()
.map(|item| item as &dyn IsMenuItem<DynRuntime>)
.map(|item| item as &dyn IsMenuItem<Wry>)
.collect();
let open_window = &MenuItem::with_id(
@@ -898,9 +894,9 @@ async fn create_tray_menu(
outbound_modes_label.as_str(),
true,
&[
rule_mode as &dyn IsMenuItem<DynRuntime>,
global_mode as &dyn IsMenuItem<DynRuntime>,
direct_mode as &dyn IsMenuItem<DynRuntime>,
rule_mode as &dyn IsMenuItem<Wry>,
global_mode as &dyn IsMenuItem<Wry>,
direct_mode as &dyn IsMenuItem<Wry>,
],
)?)
};
@@ -1013,7 +1009,7 @@ async fn create_tray_menu(
&texts.more,
true,
&[
copy_env as &dyn IsMenuItem<DynRuntime>,
copy_env as &dyn IsMenuItem<Wry>,
close_all_connections,
restart_clash,
restart_app,
@@ -1030,13 +1026,13 @@ async fn create_tray_menu(
let separator = &PredefinedMenuItem::separator(app_handle)?;
let mut menu_items: Vec<&dyn IsMenuItem<DynRuntime>> = vec![open_window, separator];
let mut menu_items: Vec<&dyn IsMenuItem<Wry>> = vec![open_window, separator];
if show_outbound_modes_inline {
menu_items.extend_from_slice(&[
rule_mode as &dyn IsMenuItem<DynRuntime>,
global_mode as &dyn IsMenuItem<DynRuntime>,
direct_mode as &dyn IsMenuItem<DynRuntime>,
rule_mode as &dyn IsMenuItem<Wry>,
global_mode as &dyn IsMenuItem<Wry>,
direct_mode as &dyn IsMenuItem<Wry>,
]);
} else if let Some(ref outbound_modes) = outbound_modes {
menu_items.push(outbound_modes);
@@ -1056,14 +1052,14 @@ async fn create_tray_menu(
menu_items.extend_from_slice(&[
separator,
system_proxy as &dyn IsMenuItem<DynRuntime>,
tun_mode as &dyn IsMenuItem<DynRuntime>,
system_proxy as &dyn IsMenuItem<Wry>,
tun_mode as &dyn IsMenuItem<Wry>,
separator,
lightweight_mode as &dyn IsMenuItem<DynRuntime>,
open_dir as &dyn IsMenuItem<DynRuntime>,
more as &dyn IsMenuItem<DynRuntime>,
lightweight_mode as &dyn IsMenuItem<Wry>,
open_dir as &dyn IsMenuItem<Wry>,
more as &dyn IsMenuItem<Wry>,
separator,
quit as &dyn IsMenuItem<DynRuntime>,
quit as &dyn IsMenuItem<Wry>,
]);
let menu = tauri::menu::MenuBuilder::new(app_handle).items(&menu_items).build()?;
+2 -2
View File
@@ -132,7 +132,7 @@ impl TraySpeedController {
let app_handle = handle::Handle::app_handle();
if let Some(tray) = app_handle.tray_by_id(super::TRAY_ID) {
let result = tray.with_inner_blocking(|inner| {
let result = tray.with_inner_tray_icon(|inner| {
if let Some(status_item) = inner.ns_status_item() {
tray_speed::clear_speed_attributed_title(&status_item);
}
@@ -167,7 +167,7 @@ impl TraySpeedController {
fn apply_tray_speed(up: u64, down: u64) {
let app_handle = handle::Handle::app_handle();
if let Some(tray) = app_handle.tray_by_id(super::TRAY_ID) {
let result = tray.with_inner_blocking(move |inner| {
let result = tray.with_inner_tray_icon(move |inner| {
if let Some(status_item) = inner.ns_status_item() {
tray_speed::set_speed_attributed_title(&status_item, up, down);
}
+1
View File
@@ -67,6 +67,7 @@ verge_registry! {
proxy_group_tools_position => NoEffect;
notice_position => NoEffect;
collapse_navbar => NoEffect;
enable_system_titlebar => NoEffect;
sysproxy_tray_icon => [TrayIcon];
tun_tray_icon => [TrayIcon];
enable_tun_mode => Tun;
+2 -2
View File
@@ -1,12 +1,12 @@
use crate::cmd;
pub fn builder() -> tauri_specta::Builder<tauri::DynRuntime> {
pub fn builder() -> tauri_specta::Builder<tauri::Wry> {
tauri_specta::Builder::new()
.types(&platform_types())
.commands(tauri_specta::collect_commands![
tauri_plugin_clash_verge_sysinfo::commands::get_system_info,
tauri_plugin_clash_verge_sysinfo::commands::get_app_uptime,
tauri_plugin_clash_verge_sysinfo::commands::export_diagnostic_info::<tauri::DynRuntime>,
tauri_plugin_clash_verge_sysinfo::commands::export_diagnostic_info::<tauri::Wry>,
cmd::probe_listener,
cmd::save_proxy_ports,
cmd::get_sys_proxy,
+8 -4
View File
@@ -40,7 +40,7 @@ mod app_init {
}
/// Setup plugins for the Tauri builder
pub fn setup_plugins(builder: tauri::Builder) -> tauri::Builder {
pub fn setup_plugins(builder: tauri::Builder<tauri::Wry>) -> tauri::Builder<tauri::Wry> {
#[allow(unused_mut)]
let mut builder = builder
.plugin(tauri_plugin_clash_verge_sysinfo::init())
@@ -116,15 +116,19 @@ mod app_init {
/// Setup window state management
pub fn setup_window_state(app: &tauri::App) -> Result<(), Box<dyn std::error::Error>> {
logging!(debug, Type::Setup, "初始化窗口状态管理...");
// Decorations are config-driven (`enable_system_titlebar`); restoring a stale
// value here would override the builder's `.decorations(...)` on window recreate.
let state_flags =
tauri_plugin_window_state::StateFlags::all() & !tauri_plugin_window_state::StateFlags::DECORATIONS;
let window_state_plugin = tauri_plugin_window_state::Builder::new()
.with_filename(files::WINDOW_STATE)
.with_state_flags(tauri_plugin_window_state::StateFlags::default())
.with_state_flags(state_flags)
.build();
app.handle().plugin(window_state_plugin)?;
Ok(())
}
pub fn generate_handlers() -> impl Fn(tauri::ipc::Invoke) -> bool + Send + Sync + 'static {
pub fn generate_handlers() -> impl Fn(tauri::ipc::Invoke<tauri::Wry>) -> bool + Send + Sync + 'static {
{
let builder = crate::ipc::builder();
move |invoke| builder.invoke_handler()(invoke)
@@ -181,7 +185,7 @@ pub fn run() -> std::process::ExitCode {
#[cfg(target_os = "linux")]
utils::linux::workarounds::apply_wayland_webkit_fix();
let builder = app_init::setup_plugins(tauri::Builder::default().runtime(tauri_runtime_wry::Wry::default()))
let builder = app_init::setup_plugins(tauri::Builder::default())
.setup(|app| {
// Logger may not be ready yet, so mirror setup panics to stderr.
fn log_setup_panic(stage: &str, panic: Box<dyn std::any::Any + Send>) {
+11
View File
@@ -1,8 +1,19 @@
#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
use std::process::ExitCode;
#[cfg(not(all(feature = "perf-harness", target_os = "macos")))]
use std::sync::atomic::{AtomicUsize, Ordering};
#[cfg(all(feature = "perf-harness", target_os = "macos"))]
#[path = "../../scripts/perf/app.rs"]
mod perf;
#[cfg(all(feature = "perf-harness", target_os = "macos"))]
fn main() -> ExitCode {
perf::run()
}
#[cfg(not(all(feature = "perf-harness", target_os = "macos")))]
fn main() -> ExitCode {
let default_parallelism = std::thread::available_parallelism().map(|n| n.get()).unwrap_or(1);
let worker_limit = std::cmp::min(default_parallelism, 8);
+3 -10
View File
@@ -19,11 +19,6 @@ const DEFAULT_HEIGHT: f64 = 700.0;
const MINIMAL_WIDTH: f64 = 520.0;
const MINIMAL_HEIGHT: f64 = 520.0;
#[cfg(target_os = "linux")]
const DEFAULT_DECORATIONS: bool = false;
#[cfg(not(target_os = "linux"))]
const DEFAULT_DECORATIONS: bool = true;
const fn restored_window_size_is_too_small(width: u32, height: u32) -> bool {
width < MINIMAL_WIDTH as u32 || height < MINIMAL_HEIGHT as u32
}
@@ -49,6 +44,7 @@ pub async fn build_new_window() -> Result<WebviewWindow, String> {
let config = Config::verge().await;
let latest = config.latest_arc();
let decorations = latest.enable_system_titlebar.unwrap_or(true);
let start_page = latest.start_page.as_deref().unwrap_or("/");
let initial_theme_mode = match latest.theme_mode.as_deref() {
Some("dark") => "dark",
@@ -83,7 +79,7 @@ pub async fn build_new_window() -> Result<WebviewWindow, String> {
)
.title("Clash Verge")
.center()
.decorations(DEFAULT_DECORATIONS)
.decorations(decorations)
.fullscreen(false)
.inner_size(DEFAULT_WIDTH, DEFAULT_HEIGHT)
.min_inner_size(MINIMAL_WIDTH, MINIMAL_HEIGHT)
@@ -132,10 +128,7 @@ pub fn take_webview_needs_reload() -> bool {
/// reload visible webviews immediately, and defer a hidden main-window reload until activation.
/// Registering this callback replaces Tauri's default automatic reload.
#[cfg(target_os = "macos")]
pub fn on_web_content_process_terminated(
webview: &tauri::Webview,
_termination: &tauri::webview::WebContentProcessTermination,
) {
pub fn on_web_content_process_terminated(webview: &tauri::Webview) {
if handle::Handle::global().is_exiting() {
return;
}
+9 -9
View File
@@ -4,7 +4,7 @@ use clash_verge_logging::{Type, logging};
use std::pin::Pin;
use std::sync::LazyLock;
use std::time::Duration;
use tauri::{Manager as _, WebviewWindow};
use tauri::{Manager as _, WebviewWindow, Wry};
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum WindowOperationResult {
@@ -50,7 +50,7 @@ impl WindowManager {
handle::Handle::global().set_activation_policy_regular();
}
fn get_main_window_with_state() -> (Option<WebviewWindow>, WindowState) {
fn get_main_window_with_state() -> (Option<WebviewWindow<Wry>>, WindowState) {
let Some(window) = Self::get_main_window() else {
return (None, WindowState::NotExist);
};
@@ -76,7 +76,7 @@ impl WindowManager {
Self::get_main_window_with_state().1
}
pub fn get_main_window() -> Option<WebviewWindow> {
pub fn get_main_window() -> Option<WebviewWindow<Wry>> {
let app_handle = handle::Handle::app_handle();
app_handle.get_webview_window("main")
}
@@ -147,7 +147,7 @@ impl WindowManager {
}
}
fn hide_main_window(window: Option<&WebviewWindow>) -> WindowOperationResult {
fn hide_main_window(window: Option<&WebviewWindow<Wry>>) -> WindowOperationResult {
logging!(debug, Type::Window, "窗口可见,将隐藏窗口");
if let Some(window) = window {
match window.close() {
@@ -166,7 +166,7 @@ impl WindowManager {
}
}
fn activate_existing_main_window(window: Option<&WebviewWindow>) -> WindowOperationResult {
fn activate_existing_main_window(window: Option<&WebviewWindow<Wry>>) -> WindowOperationResult {
logging!(debug, Type::Window, "窗口存在但被隐藏或最小化,将激活窗口");
if let Some(window) = window {
Self::activate_window(window)
@@ -176,7 +176,7 @@ impl WindowManager {
}
}
fn activate_window(window: &WebviewWindow) -> WindowOperationResult {
fn activate_window(window: &WebviewWindow<Wry>) -> WindowOperationResult {
logging!(debug, Type::Window, "开始激活窗口");
#[cfg(target_os = "macos")]
Self::set_macos_activation_policy_regular();
@@ -233,15 +233,15 @@ impl WindowManager {
}
}
pub fn is_main_window_visible(window: Option<&WebviewWindow>) -> bool {
pub fn is_main_window_visible(window: Option<&WebviewWindow<Wry>>) -> bool {
window.map(|w| w.is_visible().unwrap_or(false)).unwrap_or(false)
}
pub fn is_main_window_focused(window: Option<&WebviewWindow>) -> bool {
pub fn is_main_window_focused(window: Option<&WebviewWindow<Wry>>) -> bool {
window.map(|w| w.is_focused().unwrap_or(false)).unwrap_or(false)
}
fn is_main_window_minimized(window: Option<&WebviewWindow>) -> bool {
fn is_main_window_minimized(window: Option<&WebviewWindow<Wry>>) -> bool {
window.map(|w| w.is_minimized().unwrap_or(false)).unwrap_or(false)
}
-13
View File
@@ -202,15 +202,9 @@
width: 100%;
display: flex;
justify-content: flex-end;
padding: 10px;
box-sizing: border-box;
height: 36px;
border-bottom: 1px solid var(--divider-color);
&-drag-region {
align-self: stretch;
flex: 1 1 auto;
}
}
.layout-content__left .the-logo {
@@ -235,16 +229,9 @@
width: 100%;
display: flex;
justify-content: flex-start;
padding: 10px;
box-sizing: border-box;
height: 36px;
border-bottom: 1px solid var(--divider-color);
&-drag-region {
align-self: stretch;
flex: 1 1 auto;
order: 1;
}
}
.layout-content__left {
+1 -1
View File
@@ -101,7 +101,7 @@ export const SystemInfoCard = () => {
try {
const result = await triggerCheckUpdate()
const info = result.data
if (!info) {
if (!info?.available) {
showNotice.success(
'settings.components.verge.advanced.notifications.latestVersion',
)
+1 -1
View File
@@ -16,7 +16,7 @@ export const UpdateButton = (props: Props) => {
const { updateInfo } = useUpdate()
if (!updateInfo) return null
if (!updateInfo?.available) return null
return (
<>
+69 -38
View File
@@ -1,13 +1,13 @@
import { Close, CropSquare, FilterNone, Minimize } from '@mui/icons-material'
import { Box, IconButton } from '@mui/material'
import { getCurrentWindow } from '@tauri-apps/api/window'
import {
forwardRef,
type MouseEvent,
type PointerEvent,
useCallback,
useImperativeHandle,
useEffect,
} from 'react'
import { useWindowControls } from '@/hooks/use-window'
import getSystem from '@/utils/get-system'
const RESIZE_HANDLES = [
@@ -21,9 +21,7 @@ const RESIZE_HANDLES = [
{ direction: 'NorthWest', position: 'north-west' },
] as const
export const WindowResizeHandles = () => {
const { currentWindow, maximized } = useWindowControls()
export const WindowResizeHandles = ({ maximized }: { maximized: boolean }) => {
const startResizeDragging = useCallback(
(event: PointerEvent<HTMLDivElement>) => {
if (event.button !== 0) return
@@ -33,14 +31,14 @@ export const WindowResizeHandles = () => {
const handle = RESIZE_HANDLES.find((item) => item.direction === direction)
if (handle) {
void currentWindow
void getCurrentWindow()
.startResizeDragging(handle.direction)
.catch((error) =>
console.warn('[WindowResizeHandles] 调整窗口大小失败:', error),
)
}
},
[currentWindow],
[],
)
if (getSystem() !== 'linux' || maximized) return null
@@ -63,44 +61,77 @@ export const WindowResizeHandles = () => {
)
}
export const WindowControls = forwardRef(function WindowControls(props, ref) {
export const WindowControls = ({
maximized,
setMaximized,
}: {
maximized: boolean
setMaximized: (value: boolean) => void
}) => {
const OS = getSystem()
const {
currentWindow,
maximized,
minimize,
close,
toggleFullscreen,
toggleMaximize,
} = useWindowControls()
useImperativeHandle(
ref,
() => ({
currentWindow,
maximized,
minimize,
close,
toggleFullscreen,
toggleMaximize,
}),
[
currentWindow,
maximized,
minimize,
close,
toggleFullscreen,
toggleMaximize,
],
const close = useCallback(async () => {
// Delay one frame so the UI can clear :hover before the window hides.
await new Promise((resolve) => setTimeout(resolve, 20))
await getCurrentWindow().close()
}, [])
const minimize = useCallback(async () => {
// Delay one frame so the UI can clear :hover before the window hides.
await new Promise((resolve) => setTimeout(resolve, 10))
await getCurrentWindow().minimize()
}, [])
const toggleMaximize = useCallback(async () => {
const currentWindow = getCurrentWindow()
if (await currentWindow.isMaximized()) {
await currentWindow.unmaximize()
setMaximized(false)
} else {
await currentWindow.maximize()
setMaximized(true)
}
}, [setMaximized])
useEffect(() => {
getCurrentWindow().setMinimizable(true)
}, [])
const handleTitlebarMouseDown = useCallback(
(event: MouseEvent<HTMLDivElement>) => {
// Left button only, and only on the first press: detail 2 is the second
// press of a double-click, which `handleTitlebarDoubleClick` turns into maximize.
if (event.button !== 0 || event.detail !== 1) return
// Only the empty bar area drags; the control buttons keep their own clicks.
if (event.target !== event.currentTarget) return
event.preventDefault()
void getCurrentWindow()
.startDragging()
.catch((error) => console.warn('[WindowControls] 开始拖动失败:', error))
},
[],
)
// 通过前端对 tauri 窗口进行翻转全屏时会短暂地与系统图标重叠渲染。
// 这可能是上游缺陷,保险起见跨平台以窗口的最大化翻转为准。
const handleTitlebarDoubleClick = useCallback(
(event: MouseEvent<HTMLDivElement>) => {
if (event.target !== event.currentTarget) return
void toggleMaximize().catch((error) =>
console.warn('[WindowControls] 切换最大化失败:', error),
)
},
[toggleMaximize],
)
return (
<Box
onMouseDown={handleTitlebarMouseDown}
onDoubleClick={handleTitlebarDoubleClick}
sx={{
display: 'flex',
justifyContent: OS === 'macos' ? 'flex-start' : 'flex-end',
width: '100%',
height: '100%',
px: 1,
gap: 1,
alignItems: 'center',
'> button': {
@@ -186,4 +217,4 @@ export const WindowControls = forwardRef(function WindowControls(props, ref) {
)}
</Box>
)
})
}
+47 -11
View File
@@ -12,16 +12,21 @@ import {
} from '@mui/material'
import { convertFileSrc } from '@tauri-apps/api/core'
import { join } from '@tauri-apps/api/path'
import { getCurrentWindow } from '@tauri-apps/api/window'
import { open as openDialog } from '@tauri-apps/plugin-dialog'
import { exists } from '@tauri-apps/plugin-fs'
import { forwardRef, useEffect, useImperativeHandle, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { BaseDialog, DialogRef, Switch, TooltipIcon } from '@/components/base'
import {
BaseDialog,
type DialogRef,
Switch,
TooltipIcon,
} from '@/components/base'
import { DEFAULT_HOVER_DELAY } from '@/components/proxy/proxy-group-navigator'
import { useVerge } from '@/hooks/use-verge'
import { useWindowDecorations } from '@/hooks/use-window'
import { copyIconFile, getAppDir } from '@/services/cmds'
import { copyIconFile, getAppDir, restartApp } from '@/services/cmds'
import { mutate } from '@/services/mutate'
import { showNotice } from '@/services/notice-service'
import getSystem from '@/utils/get-system'
@@ -58,8 +63,6 @@ export const LayoutViewer = forwardRef<DialogRef>((_, ref) => {
const [sysproxyIcon, setSysproxyIcon] = useState('')
const [tunIcon, setTunIcon] = useState('')
const { decorated, toggleDecorations } = useWindowDecorations()
useEffect(() => {
initIconPath()
}, [])
@@ -123,17 +126,50 @@ export const LayoutViewer = forwardRef<DialogRef>((_, ref) => {
<List>
<Item>
<ListItemText
primary={t(
'settings.components.verge.layout.fields.preferSystemTitlebar',
)}
primary={
<Box sx={{ display: 'flex', alignItems: 'center', gap: 0.5 }}>
<span>
{t(
'settings.components.verge.layout.fields.enableSystemTitlebar',
)}
</span>
<TooltipIcon
title={t(
'settings.components.verge.layout.tooltips.enableSystemTitlebar',
)}
sx={{ opacity: '0.7' }}
/>
</Box>
}
/>
<GuardState
value={decorated}
value={verge?.enable_system_titlebar ?? true}
valueProps="checked"
onCatch={onError}
onFormat={onSwitchFormat}
onChange={async () => {
await toggleDecorations()
onChange={(e) => onChangeData({ enable_system_titlebar: e })}
onGuard={async (e) => {
await patchVerge({ enable_system_titlebar: e })
if (OS === 'linux') {
void mutate(() => restartApp(), {
id: 'restart-app',
errorNotice: false,
}).catch((error) => {
console.warn('[LayoutViewer] failed to restart app:', error)
})
} else {
const currentWindow = getCurrentWindow()
try {
await currentWindow.setDecorations(e)
// macOS clears the Miniaturizable style bit when decorations are disabled; re-enable minimize.
await currentWindow.setMinimizable(true)
} catch (error) {
console.warn(
'[LayoutViewer] failed to apply system titlebar setting:',
error,
)
}
}
}}
>
<Switch edge="end" />
@@ -48,7 +48,7 @@ const SettingVergeAdvanced = ({ onError: _ }: Props) => {
try {
const info = await checkUpdate()
updateLastCheckTime()
if (!info) {
if (!info?.available) {
showNotice.success(
'settings.components.verge.advanced.notifications.latestVersion',
)
+9 -3
View File
@@ -1,5 +1,5 @@
import { LanguageRounded } from '@mui/icons-material'
import { Box, Divider, MenuItem, Menu, styled, alpha } from '@mui/material'
import { alpha, Box, Divider, Menu, MenuItem, styled } from '@mui/material'
import { useLockFn } from 'ahooks'
import { useCallback, useEffect, useRef, useState } from 'react'
import { useTranslation } from 'react-i18next'
@@ -29,8 +29,14 @@ export const TestItem = ({ itemData, onEdit, onDelete: removeTest }: Props) => {
const onDelay = useCallback(async () => {
setDelay(-2)
const result = await cmdTestDelay(url!)
setDelay(result)
try {
const result = await cmdTestDelay(url!)
setDelay(result)
} catch (err) {
// A failed test must not leave the item stuck in the loading state.
setDelay(-1)
showNotice.error(err)
}
}, [url])
const onEditTest = () => {
+1 -8
View File
@@ -7,10 +7,8 @@ import { useTranslation } from 'react-i18next'
import { BaseDialog } from '@/components/base'
import { useVerge } from '@/hooks/use-verge'
import type { IVergeTestItem_Serialize } from '@/services/bindings'
import { showNotice } from '@/services/notice-service'
interface Props {
onChange: (uid: string, patch?: Partial<IVergeTestItem>) => void
}
@@ -79,12 +77,7 @@ export const TestViewer = forwardRef<TestViewerRef, Props>(
if (openType === 'new') {
const uid = nanoid()
const item: IVergeTestItem_Serialize = {
uid,
name: form.name ?? null,
icon: form.icon ?? null,
url: form.url ?? null,
}
const item = { ...form, uid }
const newList = [...testList, item]
await patchVerge({ test_list: newList })
onChange(uid)
+41
View File
@@ -0,0 +1,41 @@
import { getCurrentWindow } from '@tauri-apps/api/window'
import { useEffect, useState } from 'react'
import debounce from '@/utils/debounce'
export const useWindowMaximized = (enabled: boolean) => {
const [maximized, setMaximized] = useState(false)
useEffect(() => {
if (!enabled) return
const currentWindow = getCurrentWindow()
let isUnmounted = false
const syncMaximized = async () => {
if (isUnmounted) return
try {
setMaximized(await currentWindow.isMaximized())
} catch (error) {
console.warn('[useWindowMaximized] 读取最大化状态失败:', error)
}
}
void syncMaximized()
const unlistenPromise = currentWindow.onResized(
debounce(syncMaximized, 300),
)
return () => {
isUnmounted = true
unlistenPromise
.then((unlisten) => unlisten())
.catch((err) =>
console.warn('[useWindowMaximized] 清理监听器失败:', err),
)
}
}, [enabled])
return { maximized, setMaximized }
}
-46
View File
@@ -1,46 +0,0 @@
import { use } from 'react'
import { WindowContext, type WindowContextType } from '@/providers/window'
const useWindow = () => {
const context = use(WindowContext)
if (context === undefined) {
throw new Error('useWindow must be used within WindowProvider')
}
return context
}
export const useWindowControls = () => {
const {
maximized,
minimize,
toggleMaximize,
close,
toggleFullscreen,
currentWindow,
} = useWindow()
return {
maximized,
minimize,
toggleMaximize,
close,
toggleFullscreen,
currentWindow,
} satisfies Pick<
WindowContextType,
| 'maximized'
| 'minimize'
| 'toggleMaximize'
| 'close'
| 'toggleFullscreen'
| 'currentWindow'
>
}
export const useWindowDecorations = () => {
const { decorated, toggleDecorations, refreshDecorated } = useWindow()
return { decorated, toggleDecorations, refreshDecorated } satisfies Pick<
WindowContextType,
'decorated' | 'toggleDecorations' | 'refreshDecorated'
>
}
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": {
"title": "إعدادات التخطيط",
"fields": {
"preferSystemTitlebar": "Prefer System Titlebar",
"enableSystemTitlebar": "تفعيل شريط عنوان النظام",
"trafficGraph": "مخطط حركة المرور",
"memoryUsage": "استهلاك الذاكرة",
"proxyGroupIcon": "أيقونة مجموعة الوكلاء",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "إيقاف عرض إحصاءات حركة المرور مؤقتًا عند فقدان التركيز"
},
"tooltips": {
"enableSystemTitlebar": "استخدام شريط عنوان النظام الأصلي بدلاً من شريط العنوان المخصص المدمج (يلزم إعادة التشغيل على Linux).",
"hoverNavigator": "Automatically scroll to the corresponding proxy group when hovering over alphabet letters",
"hoverNavigatorDelay": "Delay before auto scrolling when hovering, in milliseconds"
},
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": {
"title": "Layout-Einstellungen",
"fields": {
"preferSystemTitlebar": "System-Titelleiste bevorzugen",
"enableSystemTitlebar": "System-Titelleiste aktivieren",
"trafficGraph": "Verkehrsdiagramm",
"memoryUsage": "Kern-Speichernutzung",
"proxyGroupIcon": "Proxy-Gruppen-Symbol",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "Rendering der Verkehrsstatistiken bei Fokusverlust pausieren"
},
"tooltips": {
"enableSystemTitlebar": "Die native Titelleiste des Systems anstelle der integrierten benutzerdefinierten Titelleiste verwenden (unter Linux ist ein Neustart erforderlich).",
"hoverNavigator": "Beim Darüberfahren mit der Maus über die Buchstaben automatisch zur passenden Proxy-Gruppe scrollen",
"hoverNavigatorDelay": "Wartezeit vor dem automatischen Scrollen beim Darüberfahren, in Millisekunden"
},
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": {
"title": "Layout Setting",
"fields": {
"preferSystemTitlebar": "Prefer System Titlebar",
"enableSystemTitlebar": "Enable System Titlebar",
"trafficGraph": "Traffic Graph",
"memoryUsage": "Core Usage",
"proxyGroupIcon": "Proxy Group Icon",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "Pause rendering traffic statistics when blurred"
},
"tooltips": {
"enableSystemTitlebar": "Use the system's native title bar instead of the built-in custom title bar (restart required on Linux).",
"hoverNavigator": "Automatically scroll to the corresponding proxy group when hovering over alphabet letters",
"hoverNavigatorDelay": "Delay before auto scrolling when hovering, in milliseconds"
},
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": {
"title": "Configuración de la interfaz",
"fields": {
"preferSystemTitlebar": "Prefer System Titlebar",
"enableSystemTitlebar": "Activar barra de título del sistema",
"trafficGraph": "Gráfico de tráfico",
"memoryUsage": "Uso de memoria del núcleo",
"proxyGroupIcon": "Icono del grupo de proxy",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "Pausar el renderizado de estadísticas de tráfico al perder el foco"
},
"tooltips": {
"enableSystemTitlebar": "Usar la barra de título nativa del sistema en lugar de la barra de título personalizada integrada (se requiere reiniciar en Linux).",
"hoverNavigator": "Automatically scroll to the corresponding proxy group when hovering over alphabet letters",
"hoverNavigatorDelay": "Delay before auto scrolling when hovering, in milliseconds"
},
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": {
"title": "تنظیمات چیدمان",
"fields": {
"preferSystemTitlebar": "Prefer System Titlebar",
"enableSystemTitlebar": "فعالسازی نوار عنوان سیستم",
"trafficGraph": "نمودار ترافیک",
"memoryUsage": "استفاده از حافظه",
"proxyGroupIcon": "آیکون گروه پراکسی",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "توقف رندر آمار ترافیک هنگام از دست رفتن فوکوس"
},
"tooltips": {
"enableSystemTitlebar": "استفاده از نوار عنوان بومی سیستم بهجای نوار عنوان سفارشی داخلی (در لینوکس نیاز به راهاندازی مجدد است).",
"hoverNavigator": "Automatically scroll to the corresponding proxy group when hovering over alphabet letters",
"hoverNavigatorDelay": "Delay before auto scrolling when hovering, in milliseconds"
},
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": {
"title": "Pengaturan Tata Letak",
"fields": {
"preferSystemTitlebar": "Prefer System Titlebar",
"enableSystemTitlebar": "Aktifkan bilah judul sistem",
"trafficGraph": "Grafik Lalu Lintas",
"memoryUsage": "Penggunaan Memori",
"proxyGroupIcon": "Ikon Grup Proksi",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "Jeda render statistik lalu lintas saat jendela tidak fokus"
},
"tooltips": {
"enableSystemTitlebar": "Gunakan bilah judul asli sistem sebagai ganti bilah judul kustom bawaan (perlu mulai ulang di Linux).",
"hoverNavigator": "Automatically scroll to the corresponding proxy group when hovering over alphabet letters",
"hoverNavigatorDelay": "Delay before auto scrolling when hovering, in milliseconds"
},
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": {
"title": "レイアウト設定",
"fields": {
"preferSystemTitlebar": "Prefer System Titlebar",
"enableSystemTitlebar": "システムタイトルバーを有効にする",
"trafficGraph": "トラフィックグラフ",
"memoryUsage": "コアメモリ使用量",
"proxyGroupIcon": "プロキシグループアイコン",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "フォーカスを失ったときにトラフィック統計の描画を一時停止"
},
"tooltips": {
"enableSystemTitlebar": "内蔵のカスタムタイトルバーの代わりにシステム標準のタイトルバーを使用します(Linux では再起動が必要です)。",
"hoverNavigator": "Automatically scroll to the corresponding proxy group when hovering over alphabet letters",
"hoverNavigatorDelay": "Delay before auto scrolling when hovering, in milliseconds"
},
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": {
"title": "레이아웃 설정",
"fields": {
"preferSystemTitlebar": "시스템 제목 표시줄 사용",
"enableSystemTitlebar": "시스템 제목 표시줄 활성화",
"trafficGraph": "트래픽 그래프",
"memoryUsage": "메모리 사용량",
"proxyGroupIcon": "프록시 그룹 아이콘",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "포커스를 잃으면 트래픽 통계 렌더링 일시 중지"
},
"tooltips": {
"enableSystemTitlebar": "내장 사용자 지정 제목 표시줄 대신 시스템 기본 제목 표시줄을 사용합니다(Linux에서는 다시 시작해야 함).",
"hoverNavigator": "알파벳에 마우스를 올리면 해당 프록시 그룹으로 자동 스크롤합니다",
"hoverNavigatorDelay": "호버 시 자동 스크롤까지의 지연(밀리초)"
},
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": {
"title": "Configurações de Layout",
"fields": {
"preferSystemTitlebar": "Preferir Barra de Título do Sistema",
"enableSystemTitlebar": "Ativar barra de título do sistema",
"trafficGraph": "Gráfico de Tráfego",
"memoryUsage": "Uso do Núcleo",
"proxyGroupIcon": "Ícone do Grupo de Proxy",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "Pausar a renderização das estatísticas de tráfego quando sem foco"
},
"tooltips": {
"enableSystemTitlebar": "Usar a barra de título nativa do sistema em vez da barra de título personalizada integrada (é necessário reiniciar no Linux).",
"hoverNavigator": "Rola automaticamente até o grupo de proxy correspondente ao passar o mouse sobre as letras do alfabeto",
"hoverNavigatorDelay": "Atraso antes da rolagem automática ao passar o mouse, em milissegundos"
},
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": {
"title": "Настройки раскладки",
"fields": {
"preferSystemTitlebar": "Использовать системную панель заголовка",
"enableSystemTitlebar": "Включить системную панель заголовка",
"trafficGraph": "График трафика",
"memoryUsage": "Использование памяти",
"proxyGroupIcon": "Значок группы прокси",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "При потере фокуса приостанавливать отрисовку статистики трафика"
},
"tooltips": {
"enableSystemTitlebar": "Использовать системную панель заголовка вместо встроенной настраиваемой (в Linux требуется перезапуск).",
"hoverNavigator": "Автоматически прокручивает к группе прокси при наведении на буквы",
"hoverNavigatorDelay": "Задержка перед прокруткой при наведении (в миллисекундах)"
},
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": {
"title": "Düzen Ayarı",
"fields": {
"preferSystemTitlebar": "Sistem Başlık Çubuğunu Tercih Et",
"enableSystemTitlebar": "Sistem başlık çubuğunu etkinleştir",
"trafficGraph": "Trafik Grafiği",
"memoryUsage": "Çekirdek Kullanımı",
"proxyGroupIcon": "Vekil Grup Simgesi",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "Odak kaybolduğunda trafik istatistiklerinin işlenmesini duraklat"
},
"tooltips": {
"enableSystemTitlebar": "Yerleşik özel başlık çubuğu yerine sistemin yerel başlık çubuğunu kullan (Linux'ta yeniden başlatma gerekir).",
"hoverNavigator": "Alfabe harflerinin üzerine gelindiğinde ilgili vekil grubuna otomatik olarak kaydırır",
"hoverNavigatorDelay": "Üzerine gelindiğinde otomatik kaydırmadan önceki gecikme, milisaniye cinsinden"
},
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": {
"title": "Расположение көйләүләре",
"fields": {
"preferSystemTitlebar": "Prefer System Titlebar",
"enableSystemTitlebar": "Enable System Titlebar",
"trafficGraph": "Трафик графигы",
"memoryUsage": "Хәтер куллану",
"proxyGroupIcon": "Прокси төркеме иконкасы",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "Фокус югалганда трафик статистикасын сызуны туктатып тору"
},
"tooltips": {
"enableSystemTitlebar": "Use the system's native title bar instead of the built-in custom title bar (restart required on Linux).",
"hoverNavigator": "Automatically scroll to the corresponding proxy group when hovering over alphabet letters",
"hoverNavigatorDelay": "Delay before auto scrolling when hovering, in milliseconds"
},
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": {
"title": "界面设置",
"fields": {
"preferSystemTitlebar": "优先使用系统标题栏",
"enableSystemTitlebar": "启用系统标题栏",
"trafficGraph": "流量图显",
"memoryUsage": "内核占用",
"proxyGroupIcon": "代理组图标",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "失去焦点时暂停渲染流量统计"
},
"tooltips": {
"enableSystemTitlebar": "使用系统原生标题栏替代内置的自定义标题栏(Linux 下需重启生效)",
"hoverNavigator": "鼠标悬停在字母上时自动滚动到对应代理组",
"hoverNavigatorDelay": "鼠标悬停后触发自动跳转前等待的毫秒数"
},
+2 -1
View File
@@ -231,7 +231,7 @@
"layout": {
"title": "介面設定",
"fields": {
"preferSystemTitlebar": "優先使用系統標題欄",
"enableSystemTitlebar": "啟用系統標題欄",
"trafficGraph": "流量圖表",
"memoryUsage": "內核佔用",
"proxyGroupIcon": "代理組圖示",
@@ -251,6 +251,7 @@
"pauseRenderTrafficStatsOnBlur": "失去焦點時暫停渲染流量統計"
},
"tooltips": {
"enableSystemTitlebar": "使用系統原生標題欄取代內建的自訂標題欄(Linux 需重新啟動生效)",
"hoverNavigator": "滑鼠懸停在字母上時自動捲動到對應代理組",
"hoverNavigatorDelay": "滑鼠懸停後觸發自動跳轉前等待的毫秒數"
},
+3 -6
View File
@@ -12,7 +12,6 @@ import { BaseErrorBoundary } from './components/base'
import { router } from './pages/_routers'
import { preloadHomePageCards } from './pages/home'
import { AppDataProvider } from './providers/app-data-provider'
import { WindowProvider } from './providers/window'
import { FALLBACK_LANGUAGE, initializeLanguage } from './services/i18n'
import {
preloadAppData,
@@ -50,11 +49,9 @@ const initializeApp = (initialThemeMode: 'light' | 'dark') => {
<ComposeContextProvider contexts={contexts}>
<BaseErrorBoundary>
<SWRConfig value={swrConfig}>
<WindowProvider>
<AppDataProvider>
<RouterProvider router={router} />
</AppDataProvider>
</WindowProvider>
<AppDataProvider>
<RouterProvider router={router} />
</AppDataProvider>
</SWRConfig>
</BaseErrorBoundary>
</ComposeContextProvider>
+9 -13
View File
@@ -1,7 +1,7 @@
import { Paper, ThemeProvider } from '@mui/material'
import dayjs from 'dayjs'
import relativeTime from 'dayjs/plugin/relativeTime'
import { useCallback, useEffect, useMemo, useRef } from 'react'
import { useCallback, useEffect, useMemo } from 'react'
import { useTranslation } from 'react-i18next'
import { Outlet, useNavigate } from 'react-router'
@@ -16,7 +16,7 @@ import {
} from '@/components/layout/window-controller'
import { useI18n } from '@/hooks/use-i18n'
import { useVerge } from '@/hooks/use-verge'
import { useWindowDecorations } from '@/hooks/use-window'
import { useWindowMaximized } from '@/hooks/use-window-maximized'
import { useEventBus } from '@/services/bus'
import { handleNoticeMessage } from '@/services/notice-handlers'
import { useThemeMode } from '@/services/states'
@@ -46,21 +46,17 @@ const Layout = () => {
const { switchLanguage } = useI18n()
const navigate = useNavigate()
const themeReady = useMemo(() => Boolean(theme), [theme])
const windowControlsRef = useRef<any>(null)
const { decorated } = useWindowDecorations()
const showCustomTitlebar = verge?.enable_system_titlebar === false
const { maximized, setMaximized } = useWindowMaximized(showCustomTitlebar)
const customTitlebar = useMemo(
() =>
decorated === false ? (
showCustomTitlebar ? (
<div className="the_titlebar">
<div
className="the_titlebar-drag-region"
data-tauri-drag-region="true"
/>
<WindowControls ref={windowControlsRef} />
<WindowControls maximized={maximized} setMaximized={setMaximized} />
</div>
) : null,
[decorated],
[showCustomTitlebar, maximized, setMaximized],
)
useLoadingOverlay(themeReady)
@@ -154,9 +150,9 @@ const Layout = () => {
: {},
]}
>
{decorated === false && <WindowResizeHandles />}
{showCustomTitlebar && <WindowResizeHandles maximized={maximized} />}
{/* Custom titlebar - rendered only when decorated is false, memoized for performance */}
{/* Custom titlebar - rendered only when the system titlebar is not preferred, memoized for performance */}
{customTitlebar}
<div className="layout-content">
-3
View File
@@ -1,3 +0,0 @@
export { WindowContext } from './window-context'
export type { WindowContextType } from './window-context'
export { WindowProvider } from './window-provider'
-18
View File
@@ -1,18 +0,0 @@
import { getCurrentWindow } from '@tauri-apps/api/window'
import { createContext } from 'react'
export interface WindowContextType {
decorated: boolean | null
maximized: boolean | null
toggleDecorations: () => Promise<void>
refreshDecorated: () => Promise<boolean>
minimize: () => Promise<void>
close: () => Promise<void>
toggleMaximize: () => Promise<void>
toggleFullscreen: () => Promise<void>
currentWindow: ReturnType<typeof getCurrentWindow>
}
export const WindowContext = createContext<WindowContextType | undefined>(
undefined,
)
-111
View File
@@ -1,111 +0,0 @@
import { getCurrentWindow } from '@tauri-apps/api/window'
import React, { useCallback, useEffect, useMemo, useState } from 'react'
import debounce from '@/utils/debounce'
import { WindowContext } from './window-context'
export const WindowProvider: React.FC<{ children: React.ReactNode }> = ({
children,
}) => {
const currentWindow = useMemo(() => getCurrentWindow(), [])
const [decorated, setDecorated] = useState<boolean | null>(null)
const [maximized, setMaximized] = useState<boolean | null>(null)
const close = useCallback(async () => {
// Delay one frame so the UI can clear :hover before the window hides.
await new Promise((resolve) => setTimeout(resolve, 20))
await currentWindow.close()
}, [currentWindow])
const minimize = useCallback(async () => {
// Delay one frame so the UI can clear :hover before the window hides.
await new Promise((resolve) => setTimeout(resolve, 10))
await currentWindow.minimize()
}, [currentWindow])
useEffect(() => {
let isUnmounted = false
let lastWidth = -1
let lastHeight = -1
const checkMaximized = debounce(
async (event: { payload: { width: number; height: number } }) => {
if (isUnmounted) return
const { width, height } = event.payload
if (width === lastWidth && height === lastHeight) return
lastWidth = width
lastHeight = height
const value = await currentWindow.isMaximized()
setMaximized(value)
},
300,
)
const unlistenPromise = currentWindow.onResized(checkMaximized)
return () => {
isUnmounted = true
unlistenPromise
.then((unlisten) => unlisten())
.catch((err) => console.warn('[WindowProvider] 清理监听器失败:', err))
}
}, [currentWindow])
const toggleMaximize = useCallback(async () => {
if (await currentWindow.isMaximized()) {
await currentWindow.unmaximize()
setMaximized(false)
} else {
await currentWindow.maximize()
setMaximized(true)
}
}, [currentWindow])
const toggleFullscreen = useCallback(async () => {
await currentWindow.setFullscreen(!(await currentWindow.isFullscreen()))
}, [currentWindow])
const refreshDecorated = useCallback(async () => {
const val = await currentWindow.isDecorated()
setDecorated(val)
return val
}, [currentWindow])
const toggleDecorations = useCallback(async () => {
const currentVal = await currentWindow.isDecorated()
await currentWindow.setDecorations(!currentVal)
setDecorated(!currentVal)
}, [currentWindow])
useEffect(() => {
refreshDecorated()
currentWindow.setMinimizable?.(true)
}, [currentWindow, refreshDecorated])
const contextValue = useMemo(
() => ({
decorated,
maximized,
toggleDecorations,
refreshDecorated,
minimize,
close,
toggleMaximize,
toggleFullscreen,
currentWindow,
}),
[
decorated,
maximized,
toggleDecorations,
refreshDecorated,
minimize,
close,
toggleMaximize,
toggleFullscreen,
currentWindow,
],
)
return <WindowContext value={contextValue}>{children}</WindowContext>
}
+119 -106
View File
@@ -53,11 +53,16 @@ export const commands = {
/** Reads the saved mode without depending on strict mihomo `/configs` deserialization. */
getClashMode: () => __TAURI_INVOKE<string | null>('get_clash_mode'),
changeClashCore: (clashCore: string) =>
__TAURI_INVOKE<CommandFailure_Serialize | null>('change_clash_core', {
clashCore,
}),
__TAURI_INVOKE<{
/** Stable frontend identifier, when classified. */
code?: string | null
/** Human-readable diagnostic detail. */
detail: string
/** Requested operation when known by the command. */
operation?: FailedOperation | null
} | null>('change_clash_core', { clashCore }),
getRuntimeConfig: () =>
__TAURI_INVOKE<YamlMapping | null>('get_runtime_config'),
__TAURI_INVOKE<{ [key in string]: JsonValue } | null>('get_runtime_config'),
getProxyView: () => __TAURI_INVOKE<ProxyViewV1_Serialize>('get_proxy_view'),
getRuntimeYaml: () => __TAURI_INVOKE<string>('get_runtime_yaml'),
getRuntimeLogs: () => __TAURI_INVOKE<RuntimeLogs>('get_runtime_logs'),
@@ -65,7 +70,7 @@ export const commands = {
__TAURI_INVOKE<string>('get_runtime_proxy_chain_config', {
proxyChainExitNode,
}),
updateProxyChainConfigInRuntime: (proxyChainConfig: YamlValue | null) =>
updateProxyChainConfigInRuntime: (proxyChainConfig: JsonValue | null) =>
__TAURI_INVOKE<ValidationOutcome>('update_proxy_chain_config_in_runtime', {
proxyChainConfig,
}),
@@ -95,11 +100,18 @@ export const commands = {
takeDnsOverrideNotice: () =>
__TAURI_INVOKE<boolean>('take_dns_override_notice'),
takeServiceFallbackNotice: () =>
__TAURI_INVOKE<ServiceFallbackNotice | null>(
'take_service_fallback_notice',
),
__TAURI_INVOKE<
| { kind: 'unavailable' }
| { kind: 'coreRejected'; reason: string }
| { kind: 'notAutoStarted' }
| null
>('take_service_fallback_notice'),
getCoreStartupError: () =>
__TAURI_INVOKE<CoreFailure | null>('get_core_startup_error'),
__TAURI_INVOKE<
| { kind: 'startFailed'; detail: string }
| { kind: 'serviceCoreStopped'; detail: string }
| null
>('get_core_startup_error'),
takeServiceRepairNotice: () =>
__TAURI_INVOKE<boolean>('take_service_repair_notice'),
takeServiceOwnerNotice: () =>
@@ -128,7 +140,7 @@ export const commands = {
__TAURI_INVOKE<NetworkInterfaceView[]>('get_network_interfaces_info'),
getProfiles: () => __TAURI_INVOKE<IProfiles_Serialize>('get_profiles'),
enhanceProfiles: () => __TAURI_INVOKE<ValidationOutcome>('enhance_profiles'),
patchProfilesConfig: (profiles: ProfileSelection_Deserialize) =>
patchProfilesConfig: (profiles: ProfileSelection) =>
__TAURI_INVOKE<ValidationOutcome>('patch_profiles_config', { profiles }),
viewProfile: (index: string) =>
__TAURI_INVOKE<null>('view_profile', { index }),
@@ -136,12 +148,52 @@ export const commands = {
__TAURI_INVOKE<null>('patch_profile', { index, profile }),
createProfile: (item: ProfileCreate_Deserialize, fileData: string | null) =>
__TAURI_INVOKE<null>('create_profile', { item, fileData }),
importProfile: (url: string, option: PrfOption_Deserialize | null) =>
__TAURI_INVOKE<null>('import_profile', { url, option }),
importProfile: (
url: string,
option: {
user_agent?: string | null
with_proxy?: boolean | null
self_proxy?: boolean | null
update_interval?: number | null
/**
* HTTP request timeout in seconds
* default is 60 seconds
*/
timeout_seconds?: number | null
/** default is `false` */
danger_accept_invalid_certs?: boolean | null
allow_auto_update?: boolean | null
merge?: string | null
script?: string | null
rules?: string | null
proxies?: string | null
groups?: string | null
} | null,
) => __TAURI_INVOKE<null>('import_profile', { url, option }),
reorderProfile: (activeId: string, overId: string) =>
__TAURI_INVOKE<null>('reorder_profile', { activeId, overId }),
updateProfile: (index: string, option: PrfOption_Deserialize | null) =>
__TAURI_INVOKE<null>('update_profile', { index, option }),
updateProfile: (
index: string,
option: {
user_agent?: string | null
with_proxy?: boolean | null
self_proxy?: boolean | null
update_interval?: number | null
/**
* HTTP request timeout in seconds
* default is 60 seconds
*/
timeout_seconds?: number | null
/** default is `false` */
danger_accept_invalid_certs?: boolean | null
allow_auto_update?: boolean | null
merge?: string | null
script?: string | null
rules?: string | null
proxies?: string | null
groups?: string | null
} | null,
) => __TAURI_INVOKE<null>('update_profile', { index, option }),
deleteProfile: (index: string) =>
__TAURI_INVOKE<null>('delete_profile', { index }),
readProfileFile: (index: string) =>
@@ -211,11 +263,11 @@ export type CommandFailure_Deserialize = {
/** Structured command failure returned to the frontend. */
export type CommandFailure_Serialize = {
/** Stable frontend identifier, when classified. */
code?: string
code?: string | null
/** Human-readable diagnostic detail. */
detail: string
/** Requested operation when known by the command. */
operation?: FailedOperation
operation?: FailedOperation | null
}
/** Why the Core is not running, kept until a Core starts again. */
@@ -278,27 +330,14 @@ export type IProfiles_Serialize = {
/** ### `verge.yaml` schema */
export type IVerge = IVerge_Serialize | IVerge_Deserialize
export type IVergeTestItem =
| IVergeTestItem_Serialize
| IVergeTestItem_Deserialize
export type IVergeTestItem_Deserialize = {
export type IVergeTestItem = {
uid?: string | null
name?: string | null
icon?: string | null
url?: string | null
}
export type IVergeTestItem_Serialize = {
uid: string | null
name: string | null
icon: string | null
url: string | null
}
export type IVergeTheme = IVergeTheme_Serialize | IVergeTheme_Deserialize
export type IVergeTheme_Deserialize = {
export type IVergeTheme = {
primary_color?: string | null
secondary_color?: string | null
primary_text?: string | null
@@ -311,19 +350,6 @@ export type IVergeTheme_Deserialize = {
css_injection?: string | null
}
export type IVergeTheme_Serialize = {
primary_color: string | null
secondary_color: string | null
primary_text: string | null
secondary_text: string | null
info_color: string | null
error_color: string | null
warning_color: string | null
success_color: string | null
font_family: string | null
css_injection: string | null
}
/** ### `verge.yaml` schema */
export type IVerge_Deserialize = {
/** silent | error | warn | info | debug | trace */
@@ -349,6 +375,7 @@ export type IVerge_Deserialize = {
proxy_group_tools_position?: string | null
notice_position?: string | null
collapse_navbar?: boolean | null
enable_system_titlebar?: boolean | null
sysproxy_tray_icon?: boolean | null
tun_tray_icon?: boolean | null
enable_tun_mode?: boolean | null
@@ -366,7 +393,7 @@ export type IVerge_Deserialize = {
proxy_auto_config?: boolean | null
pac_file_content?: string | null
proxy_host?: string | null
theme_setting?: IVergeTheme_Deserialize | null
theme_setting?: IVergeTheme | null
web_ui_list?: string[] | null
clash_core?: string | null
/** format: {func},{key} */
@@ -382,7 +409,7 @@ export type IVerge_Deserialize = {
auto_delay_detection_interval_minutes?: number | null
enable_builtin_enhanced?: boolean | null
proxy_layout_column?: number | null
test_list?: IVergeTestItem_Deserialize[] | null
test_list?: IVergeTestItem[] | null
/** 0: 不清理; 1: 1天;2: 7天; 3: 30天; 4: 90天 */
auto_log_clean?: number | null
enable_auto_backup_schedule?: boolean | null
@@ -431,14 +458,15 @@ export type IVerge_Serialize = {
traffic_graph: boolean | null
/** show memory info (only for Clash Meta) */
enable_memory_usage: boolean | null
enable_group_icon?: boolean
enable_group_icon?: boolean | null
pause_render_traffic_stats_on_blur: boolean | null
common_tray_icon?: boolean
menu_icon?: string
menu_order?: string[]
proxy_group_tools_position?: string
notice_position?: string
common_tray_icon?: boolean | null
menu_icon?: string | null
menu_order?: string[] | null
proxy_group_tools_position?: string | null
notice_position?: string | null
collapse_navbar: boolean | null
enable_system_titlebar?: boolean | null
sysproxy_tray_icon: boolean | null
tun_tray_icon: boolean | null
enable_tun_mode: boolean | null
@@ -456,11 +484,11 @@ export type IVerge_Serialize = {
proxy_auto_config: boolean | null
pac_file_content: string | null
proxy_host: string | null
theme_setting: IVergeTheme_Serialize | null
theme_setting: IVergeTheme | null
web_ui_list: string[] | null
clash_core?: string
clash_core?: string | null
/** format: {func},{key} */
hotkeys?: string[]
hotkeys?: string[] | null
enable_global_hotkey: boolean | null
home_cards: JsonValue | null
auto_close_connection: boolean | null
@@ -472,7 +500,7 @@ export type IVerge_Serialize = {
auto_delay_detection_interval_minutes: number | null
enable_builtin_enhanced: boolean | null
proxy_layout_column: number | null
test_list: IVergeTestItem_Serialize[] | null
test_list: IVergeTestItem[] | null
/** 0: 不清理; 1: 1天;2: 7天; 3: 30天; 4: 90天 */
auto_log_clean: number | null
enable_auto_backup_schedule: boolean | null
@@ -484,10 +512,10 @@ export type IVerge_Serialize = {
verge_socks_enabled: boolean | null
verge_port: number | null
verge_http_enabled: boolean | null
webdav_url?: string
webdav_username?: string
webdav_password?: string
tray_proxy_groups_display_mode?: string
webdav_url?: string | null
webdav_username?: string | null
webdav_password?: string | null
tray_proxy_groups_display_mode?: string | null
tray_inline_outbound_modes: boolean | null
enable_auto_light_weight_mode: boolean | null
/** 自动进入轻量模式的延迟(分钟) */
@@ -632,7 +660,7 @@ export type PrfItem_Deserialize = {
file?: string | null
desc?: string | null
url?: string | null
selected?: PrfSelected_Deserialize[] | null
selected?: PrfSelected[] | null
latency_test_urls?: { [key in string]: string } | null
extra?: PrfExtra | null
updated?: number | null
@@ -646,14 +674,14 @@ export type PrfItem_Serialize = {
type: string | null
name: string | null
file: string | null
desc?: string
url?: string
selected?: PrfSelected_Serialize[]
latency_test_urls?: { [key in string]: string }
extra?: PrfExtra
desc?: string | null
url?: string | null
selected?: PrfSelected[] | null
latency_test_urls?: { [key in string]: string } | null
extra?: PrfExtra | null
updated: number | null
option?: PrfOption_Serialize
home?: string
option?: PrfOption_Serialize | null
home?: string | null
}
export type PrfOption = PrfOption_Serialize | PrfOption_Deserialize
@@ -679,18 +707,18 @@ export type PrfOption_Deserialize = {
}
export type PrfOption_Serialize = {
user_agent?: string
with_proxy?: boolean
self_proxy?: boolean
update_interval?: number
user_agent?: string | null
with_proxy?: boolean | null
self_proxy?: boolean | null
update_interval?: number | null
/**
* HTTP request timeout in seconds
* default is 60 seconds
*/
timeout_seconds?: number
timeout_seconds?: number | null
/** default is `false` */
danger_accept_invalid_certs?: boolean
allow_auto_update?: boolean
danger_accept_invalid_certs?: boolean | null
allow_auto_update?: boolean | null
merge: string | null
script: string | null
rules: string | null
@@ -698,18 +726,11 @@ export type PrfOption_Serialize = {
groups: string | null
}
export type PrfSelected = PrfSelected_Serialize | PrfSelected_Deserialize
export type PrfSelected_Deserialize = {
export type PrfSelected = {
name?: string | null
now?: string | null
}
export type PrfSelected_Serialize = {
name: string | null
now: string | null
}
export type ProfileCreate = ProfileCreate_Serialize | ProfileCreate_Deserialize
export type ProfileCreate_Deserialize = {
@@ -739,7 +760,7 @@ export type ProfileDnsSettings_Deserialize = {
export type ProfileDnsSettings_Serialize = {
enabled: boolean
confirmation?: string
confirmation?: string | null
}
export type ProfilePatch = ProfilePatch_Serialize | ProfilePatch_Deserialize
@@ -750,7 +771,7 @@ export type ProfilePatch_Deserialize = {
desc?: string | null
file?: string | null
url?: string | null
selected?: PrfSelected_Deserialize[] | null
selected?: PrfSelected[] | null
latency_test_urls?: { [key in string]: string } | null
extra?: PrfExtra | null
updated?: number | null
@@ -763,25 +784,17 @@ export type ProfilePatch_Serialize = {
desc: string | null
file: string | null
url: string | null
selected: PrfSelected_Serialize[] | null
selected: PrfSelected[] | null
latency_test_urls: { [key in string]: string } | null
extra: PrfExtra | null
updated: number | null
option: PrfOption_Serialize | null
}
export type ProfileSelection =
| ProfileSelection_Serialize
| ProfileSelection_Deserialize
export type ProfileSelection_Deserialize = {
export type ProfileSelection = {
current?: string | null
}
export type ProfileSelection_Serialize = {
current: string | null
}
export type ProfileUpdatePayload = {
uid: string
}
@@ -814,11 +827,11 @@ export type ProxyGroupView_Deserialize = {
export type ProxyGroupView_Serialize = {
name: string
type: string
now?: string
fixed?: string
hidden?: boolean
icon?: string
testUrl?: string
now?: string | null
fixed?: string | null
hidden?: boolean | null
icon?: string | null
testUrl?: string | null
history: DelayHistory[]
extra: { [key in string]: ProxyLatencyHistory }
members: ProxyMemberRef[]
@@ -863,10 +876,10 @@ export type ProxyNodeView_Serialize = {
type: string
history: DelayHistory[]
extra: { [key in string]: ProxyLatencyHistory }
id?: string
hidden?: boolean
icon?: string
testUrl?: string
id?: string | null
hidden?: boolean | null
icon?: string | null
testUrl?: string | null
source: ProxyNodeSource
} & ProxyCapabilities
@@ -895,8 +908,8 @@ export type ProxyProviderView_Deserialize = {
export type ProxyProviderView_Serialize = {
name: string
vehicleType: ProxyProviderVehicleType
updatedAt?: string
subscriptionInfo?: ProxySubscriptionInfo
updatedAt?: string | null
subscriptionInfo?: ProxySubscriptionInfo | null
proxyRecordIds: string[]
}
+2 -1
View File
@@ -533,7 +533,7 @@ export const translationKeys = [
'settings.components.verge.theme.actions.editCss',
'settings.components.verge.theme.dialogs.editCssTitle',
'settings.components.verge.layout.title',
'settings.components.verge.layout.fields.preferSystemTitlebar',
'settings.components.verge.layout.fields.enableSystemTitlebar',
'settings.components.verge.layout.fields.trafficGraph',
'settings.components.verge.layout.fields.memoryUsage',
'settings.components.verge.layout.fields.proxyGroupIcon',
@@ -551,6 +551,7 @@ export const translationKeys = [
'settings.components.verge.layout.fields.enableTrayIcon',
'settings.components.verge.layout.fields.enableTraySpeed',
'settings.components.verge.layout.fields.pauseRenderTrafficStatsOnBlur',
'settings.components.verge.layout.tooltips.enableSystemTitlebar',
'settings.components.verge.layout.tooltips.hoverNavigator',
'settings.components.verge.layout.tooltips.hoverNavigatorDelay',
'settings.components.verge.layout.options.icon.monochrome',
+2 -1
View File
@@ -746,6 +746,7 @@ export interface TranslationResources {
fields: {
collapseNavBar: string
commonTrayIcon: string
enableSystemTitlebar: string
enableTrayIcon: string
enableTraySpeed: string
hoverNavigator: string
@@ -753,7 +754,6 @@ export interface TranslationResources {
memoryUsage: string
navIcon: string
pauseRenderTrafficStatsOnBlur: string
preferSystemTitlebar: string
proxyGroupIcon: string
proxyGroupsDisplayMode: string
showOutboundModesInline: string
@@ -783,6 +783,7 @@ export interface TranslationResources {
}
title: string
tooltips: {
enableSystemTitlebar: string
hoverNavigator: string
hoverNavigatorDelay: string
}
+1 -1
View File
@@ -8,7 +8,7 @@ export default function getSystem() {
if (/win64|win32/i.test(ua) || platform === 'win32') return 'windows'
if (/linux/i.test(ua)) return 'linux'
if (/linux/i.test(ua) || platform === 'linux') return 'linux'
return 'unknown'
}