Compare commits

..
36 changed files with 2613 additions and 774 deletions
Generated
+123 -187
View File
@@ -150,7 +150,7 @@ dependencies = [
"objc2-core-foundation", "objc2-core-foundation",
"objc2-core-graphics", "objc2-core-graphics",
"objc2-foundation", "objc2-foundation",
"parking_lot 0.12.5", "parking_lot",
"percent-encoding", "percent-encoding",
"windows-sys 0.60.2", "windows-sys 0.60.2",
"wl-clipboard-rs", "wl-clipboard-rs",
@@ -159,9 +159,9 @@ dependencies = [
[[package]] [[package]]
name = "arc-swap" name = "arc-swap"
version = "1.9.3" version = "1.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "380962b3d93b99642e50b9febf72c071e97e8b32d86eae9cf4ef582bb960ae6e" checksum = "c049c0be4daef0b145cb3555416b3b8ef5b7888a38aea1a3a155801fe7b0810b"
dependencies = [ dependencies = [
"rustversion", "rustversion",
] ]
@@ -1117,7 +1117,7 @@ dependencies = [
"objc2-app-kit", "objc2-app-kit",
"objc2-foundation", "objc2-foundation",
"open", "open",
"parking_lot 0.13.0", "parking_lot",
"percent-encoding", "percent-encoding",
"regex", "regex",
"reqwest 0.13.5", "reqwest 0.13.5",
@@ -1175,7 +1175,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"criterion", "criterion",
"parking_lot 0.13.0", "parking_lot",
"tokio", "tokio",
] ]
@@ -1198,7 +1198,7 @@ dependencies = [
"anyhow", "anyhow",
"flexi_logger", "flexi_logger",
"log", "log",
"parking_lot 0.13.0", "parking_lot",
"tracing-estuary", "tracing-estuary",
] ]
@@ -1255,8 +1255,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "facfbc410a385e0b0cbca8179dd9e363ed70e8455960bf7545137e90ca753b66" checksum = "facfbc410a385e0b0cbca8179dd9e363ed70e8455960bf7545137e90ca753b66"
dependencies = [ dependencies = [
"crossbeam-utils", "crossbeam-utils",
"lock_api 0.4.14", "lock_api",
"parking_lot_core 0.9.12", "parking_lot_core",
"polonius-the-crab", "polonius-the-crab",
] ]
@@ -1333,7 +1333,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c" checksum = "117725a109d387c937a1533ce01b450cbde6b88abceea8473c4d7a85853cda3c"
dependencies = [ dependencies = [
"lazy_static", "lazy_static",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@@ -1767,18 +1767,8 @@ version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d"
dependencies = [ dependencies = [
"darling_core 0.23.0", "darling_core",
"darling_macro 0.23.0", "darling_macro",
]
[[package]]
name = "darling"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed17f5901b6630b993ca003def43f2f8ef4014fc13b047b57aad617ff32bc2ec"
dependencies = [
"darling_core 0.24.1",
"darling_macro 0.24.1",
] ]
[[package]] [[package]]
@@ -1794,41 +1784,17 @@ dependencies = [
"syn 2.0.119", "syn 2.0.119",
] ]
[[package]]
name = "darling_core"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6837e2cf7485aaae18f86181d2f0e9a7ed297a025e220aeabf63fdebd3a2ddff"
dependencies = [
"ident_case",
"proc-macro2",
"quote",
"strsim",
"syn 3.0.5",
]
[[package]] [[package]]
name = "darling_macro" name = "darling_macro"
version = "0.23.0" version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d"
dependencies = [ dependencies = [
"darling_core 0.23.0", "darling_core",
"quote", "quote",
"syn 2.0.119", "syn 2.0.119",
] ]
[[package]]
name = "darling_macro"
version = "0.24.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2ac7135c3ef02b2f7833bbeb1be5ba7f966dcde8a87c6b87f65a778d71a02785"
dependencies = [
"darling_core 0.24.1",
"quote",
"syn 3.0.5",
]
[[package]] [[package]]
name = "dashmap" name = "dashmap"
version = "6.2.1" version = "6.2.1"
@@ -1838,9 +1804,9 @@ dependencies = [
"cfg-if", "cfg-if",
"crossbeam-utils", "crossbeam-utils",
"hashbrown 0.14.5", "hashbrown 0.14.5",
"lock_api 0.4.14", "lock_api",
"once_cell", "once_cell",
"parking_lot_core 0.9.12", "parking_lot_core",
] ]
[[package]] [[package]]
@@ -1918,9 +1884,9 @@ dependencies = [
[[package]] [[package]]
name = "devtools-core" name = "devtools-core"
version = "0.4.2" version = "0.4.1"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c9e85926bda763dd434fcfbcfb24af4b2c1a8c3e3c34bcd5d844c48f655cca51" checksum = "c53d416e473377276435d49d731a75d253b96ee4339831cd85653702b94d8298"
dependencies = [ dependencies = [
"bytes", "bytes",
"devtools-wire-format", "devtools-wire-format",
@@ -2048,7 +2014,7 @@ dependencies = [
"libc", "libc",
"option-ext", "option-ext",
"redox_users 0.5.2", "redox_users 0.5.2",
"windows-sys 0.61.2", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@@ -2307,7 +2273,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.61.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@@ -2823,7 +2789,7 @@ dependencies = [
"gobject-sys 0.21.5", "gobject-sys 0.21.5",
"libc", "libc",
"system-deps 7.0.8", "system-deps 7.0.8",
"windows-sys 0.61.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@@ -3377,7 +3343,7 @@ dependencies = [
"js-sys", "js-sys",
"log", "log",
"wasm-bindgen", "wasm-bindgen",
"windows-core 0.62.2", "windows-core 0.61.2",
] ]
[[package]] [[package]]
@@ -3391,12 +3357,12 @@ dependencies = [
[[package]] [[package]]
name = "ico" name = "ico"
version = "0.6.0" version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d75e94d66081e4d8dcd098bd8dc5254800fea14a26389b298410158d834de167" checksum = "3e795dff5605e0f04bff85ca41b51a96b83e80b281e96231bcaaf1ac35103371"
dependencies = [ dependencies = [
"byteorder", "byteorder",
"png", "png 0.17.16",
] ]
[[package]] [[package]]
@@ -3582,7 +3548,7 @@ dependencies = [
"byteorder-lite", "byteorder-lite",
"moxcms", "moxcms",
"num-traits", "num-traits",
"png", "png 0.18.1",
"tiff", "tiff",
] ]
@@ -3891,7 +3857,7 @@ dependencies = [
"http", "http",
"httparse", "httparse",
"libc", "libc",
"parking_lot 0.12.5", "parking_lot",
"path-tree", "path-tree",
"pin-project-lite", "pin-project-lite",
"rand 0.10.2", "rand 0.10.2",
@@ -4051,20 +4017,11 @@ dependencies = [
"scopeguard", "scopeguard",
] ]
[[package]]
name = "lock_api"
version = "0.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "65d91b22da179613dcf4d6ec8349563f52a363f6849e03b00dd0a185ec4a59af"
dependencies = [
"scopeguard",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.35" version = "0.4.34"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5d15f86d12da44cd8d4ec3c73984537eb7f4cadcb9776a575a0a8a05ca11005c" checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6"
[[package]] [[package]]
name = "lru-slab" name = "lru-slab"
@@ -4074,9 +4031,9 @@ checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154"
[[package]] [[package]]
name = "lzma-rust2" name = "lzma-rust2"
version = "0.24.0" version = "0.23.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "490750560fa7851fbbf5d3ab1a8750f09350ed361ed8aed85b1e1e40587812ee" checksum = "f1c46f3fcd2e888afdb01a07efc071cb21300e07c68d32c3e8b01b5c8cb1fcaa"
dependencies = [ dependencies = [
"sha2 0.11.1", "sha2 0.11.1",
] ]
@@ -4254,10 +4211,10 @@ dependencies = [
"objc2-core-foundation", "objc2-core-foundation",
"objc2-foundation", "objc2-foundation",
"once_cell", "once_cell",
"png", "png 0.18.1",
"serde", "serde",
"thiserror 2.0.20", "thiserror 2.0.20",
"windows-sys 0.61.2", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@@ -4424,7 +4381,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@@ -4806,7 +4763,7 @@ version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
dependencies = [ dependencies = [
"parking_lot_core 0.9.12", "parking_lot_core",
] ]
[[package]] [[package]]
@@ -4829,9 +4786,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381"
[[package]] [[package]]
name = "open" name = "open"
version = "5.4.5" version = "5.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8d8c8b8e725ad26404d6896eb2f064294a0a7b4f1d9120210d85544684c4ca95" checksum = "aa576c76302b7b808eecc68061e67336c47833ef9d22caa74dda10fa9675eebc"
dependencies = [ dependencies = [
"dunce", "dunce",
"is-wsl", "is-wsl",
@@ -4901,7 +4858,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.61.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@@ -4965,18 +4922,8 @@ version = "0.12.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
dependencies = [ dependencies = [
"lock_api 0.4.14", "lock_api",
"parking_lot_core 0.9.12", "parking_lot_core",
]
[[package]]
name = "parking_lot"
version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9cf937ca4e1d0b7578784559063422fc39ae7c5a00f02471f2614d5486fd226"
dependencies = [
"lock_api 0.5.0",
"parking_lot_core 0.10.0",
] ]
[[package]] [[package]]
@@ -4987,19 +4934,7 @@ checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"redox_syscall 0.5.18", "redox_syscall",
"smallvec",
"windows-link 0.2.1",
]
[[package]]
name = "parking_lot_core"
version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9cfff060068cc18aa7815010fcaf80c68c7fb9e85fc7f5c0f9bf142c21d19826"
dependencies = [
"libc",
"redox_syscall 0.9.4",
"smallvec", "smallvec",
"windows-link 0.2.1", "windows-link 0.2.1",
] ]
@@ -5290,6 +5225,19 @@ dependencies = [
"plotters-backend", "plotters-backend",
] ]
[[package]]
name = "png"
version = "0.17.16"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526"
dependencies = [
"bitflags 1.3.2",
"crc32fast",
"fdeflate",
"flate2",
"miniz_oxide 0.8.9",
]
[[package]] [[package]]
name = "png" name = "png"
version = "0.18.1" version = "0.18.1"
@@ -5575,7 +5523,7 @@ dependencies = [
"once_cell", "once_cell",
"socket2", "socket2",
"tracing", "tracing",
"windows-sys 0.61.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@@ -5719,15 +5667,6 @@ dependencies = [
"bitflags 2.13.2", "bitflags 2.13.2",
] ]
[[package]]
name = "redox_syscall"
version = "0.9.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "737970939a87c6fa31e7acad13307bccbb017a073b695b6089a2c484f929e20e"
dependencies = [
"bitflags 2.13.2",
]
[[package]] [[package]]
name = "redox_users" name = "redox_users"
version = "0.4.6" version = "0.4.6"
@@ -5925,9 +5864,9 @@ dependencies = [
[[package]] [[package]]
name = "rfd" name = "rfd"
version = "0.17.2" version = "0.16.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "20dafead71c16a34e1ff357ddefc8afc11e7d51d6d2b9fbd07eaa48e3e540220" checksum = "a15ad77d9e70a92437d8f74c35d99b4e4691128df018833e99f90bcd36152672"
dependencies = [ dependencies = [
"block2", "block2",
"dispatch2", "dispatch2",
@@ -5935,18 +5874,16 @@ dependencies = [
"gobject-sys 0.18.0", "gobject-sys 0.18.0",
"gtk-sys", "gtk-sys",
"js-sys", "js-sys",
"libc",
"log", "log",
"objc2", "objc2",
"objc2-app-kit", "objc2-app-kit",
"objc2-core-foundation", "objc2-core-foundation",
"objc2-foundation", "objc2-foundation",
"percent-encoding",
"raw-window-handle", "raw-window-handle",
"wasm-bindgen", "wasm-bindgen",
"wasm-bindgen-futures", "wasm-bindgen-futures",
"web-sys", "web-sys",
"windows-sys 0.61.2", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@@ -6090,7 +6027,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys 0.4.15", "linux-raw-sys 0.4.15",
"windows-sys 0.59.0", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@@ -6103,7 +6040,7 @@ dependencies = [
"errno", "errno",
"libc", "libc",
"linux-raw-sys 0.12.1", "linux-raw-sys 0.12.1",
"windows-sys 0.61.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@@ -6162,7 +6099,7 @@ dependencies = [
"security-framework", "security-framework",
"security-framework-sys", "security-framework-sys",
"webpki-root-certs", "webpki-root-certs",
"windows-sys 0.61.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@@ -6540,7 +6477,7 @@ version = "3.21.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660"
dependencies = [ dependencies = [
"darling 0.23.0", "darling",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 2.0.119", "syn 2.0.119",
@@ -6782,7 +6719,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys 0.61.2", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@@ -6800,7 +6737,7 @@ dependencies = [
"objc2-foundation", "objc2-foundation",
"objc2-quartz-core", "objc2-quartz-core",
"raw-window-handle", "raw-window-handle",
"redox_syscall 0.5.18", "redox_syscall",
"tracing", "tracing",
"wasm-bindgen", "wasm-bindgen",
"web-sys", "web-sys",
@@ -6835,9 +6772,9 @@ dependencies = [
[[package]] [[package]]
name = "specta" name = "specta"
version = "2.0.0-rc.26" version = "2.0.0-rc.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ed155e448f42fc4be7a5cbc12bd43f6c549981f7c32fcba429ff6bc57cc4f30c" checksum = "38f9a30cbcbb7011f1da7d73483983bf838af123883e45f2b36ed76328df9c50"
dependencies = [ dependencies = [
"paste", "paste",
"rustc_version 0.4.1", "rustc_version 0.4.1",
@@ -6847,9 +6784,9 @@ dependencies = [
[[package]] [[package]]
name = "specta-macros" name = "specta-macros"
version = "2.0.0-rc.26" version = "2.0.0-rc.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b34b2516e16a9ddd3dc4e73cd6454cfe21fe209f8cb5a6f37346fe3774d759b3" checksum = "2ce14957ecc2897f1f848b8255b6531d13ddf49cbcf506b7c2c9fb1d005593bb"
dependencies = [ dependencies = [
"Inflector", "Inflector",
"proc-macro2", "proc-macro2",
@@ -6859,9 +6796,9 @@ dependencies = [
[[package]] [[package]]
name = "specta-serde" name = "specta-serde"
version = "0.0.13" version = "0.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4983177ed0eb28235080261953de5f9ebae8a2d107851b5852420bd2ab246245" checksum = "ee8a72b755ddb8949fd8f17c5db43f0e8a806ea587d9bc602ee3f73240c00029"
dependencies = [ dependencies = [
"specta", "specta",
"specta-macros", "specta-macros",
@@ -6869,18 +6806,18 @@ dependencies = [
[[package]] [[package]]
name = "specta-typescript" name = "specta-typescript"
version = "0.0.13" version = "0.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3ecbe75dc1d27b36141b50b02b7c73bed00945710cee6edffa86d296a159a6f9" checksum = "639404ee95557f2f8b7e4cb773ffefd45304c7ab8ba21ac83b69051595e083c0"
dependencies = [ dependencies = [
"specta", "specta",
] ]
[[package]] [[package]]
name = "specta-util" name = "specta-util"
version = "0.0.13" version = "0.0.12"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d9bf531215ea70fba8264edecf98f12600462cf359f2038602a47aeda5ecd5b8" checksum = "29b1fc02b446f7244a92924fe68c0555921209f1d342990cd1539e9138e69502"
dependencies = [ dependencies = [
"specta", "specta",
] ]
@@ -6910,7 +6847,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901"
dependencies = [ dependencies = [
"new_debug_unreachable", "new_debug_unreachable",
"parking_lot 0.12.5", "parking_lot",
"phf_shared 0.13.1", "phf_shared 0.13.1",
"precomputed-hash", "precomputed-hash",
] ]
@@ -7046,9 +6983,9 @@ dependencies = [
[[package]] [[package]]
name = "sysinfo" name = "sysinfo"
version = "0.40.1" version = "0.39.6"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0e8a3edbe0394ce115f3917a00349dd242be09eb5a091ed509eaf67b55cfb8a8" checksum = "d2071df9448915b71c4fe6d25deaf1c22f12bd234f01540b77312bb8e41361e6"
dependencies = [ dependencies = [
"libc", "libc",
"memchr", "memchr",
@@ -7157,7 +7094,7 @@ dependencies = [
"objc2-app-kit", "objc2-app-kit",
"objc2-foundation", "objc2-foundation",
"objc2-ui-kit", "objc2-ui-kit",
"parking_lot 0.12.5", "parking_lot",
"percent-encoding", "percent-encoding",
"raw-window-handle", "raw-window-handle",
"tao-macros", "tao-macros",
@@ -7211,9 +7148,9 @@ checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca"
[[package]] [[package]]
name = "tauri" name = "tauri"
version = "2.12.5" version = "2.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5fb0244332cfb9cee7e2a568bf5da29a36ed8fdbd717f6f47a66b6b7b358c072" checksum = "f316751027fc2644221d1a448564d519efb07067fda584879d9cda966391d212"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"bytes", "bytes",
@@ -7266,9 +7203,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-build" name = "tauri-build"
version = "2.7.4" version = "2.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "adddb0e648752aaac5da9c3c3c6a68e1e87cab55e3b757b18a9441ef8c3ac614" checksum = "641dd5fd297c99ab92717b0428cca5b9c6f960b2ac96ca131b3eebc9d70b8ddc"
dependencies = [ dependencies = [
"anyhow", "anyhow",
"cargo_toml", "cargo_toml",
@@ -7289,16 +7226,16 @@ dependencies = [
[[package]] [[package]]
name = "tauri-codegen" name = "tauri-codegen"
version = "2.7.4" version = "2.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fb37631cf71dab5e8ab55e1bdd701e18a52653d90b78e71d0e5598b1e0a61316" checksum = "a6585c5b536c00f039d12ce70a9755172d5423bbcfd3969863baa70314c3e02f"
dependencies = [ dependencies = [
"base64 0.23.1", "base64 0.23.1",
"brotli", "brotli",
"ico", "ico",
"json-patch", "json-patch",
"plist", "plist",
"png", "png 0.18.1",
"proc-macro2", "proc-macro2",
"quote", "quote",
"semver 1.0.28", "semver 1.0.28",
@@ -7316,9 +7253,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-macros" name = "tauri-macros"
version = "2.7.4" version = "2.7.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0cc38db58854c10fb5f91a887ed8bf82d3239bb92b5d8db86eda09e96d9963d1" checksum = "e2e83a0cfa1bae376e8fbbdc0b1692707aed50cd6072ce85cae4789af5d6c19f"
dependencies = [ dependencies = [
"heck 0.5.0", "heck 0.5.0",
"proc-macro2", "proc-macro2",
@@ -7364,7 +7301,7 @@ version = "0.1.0"
dependencies = [ dependencies = [
"deelevate", "deelevate",
"libc", "libc",
"parking_lot 0.13.0", "parking_lot",
"serde", "serde",
"specta", "specta",
"sysinfo", "sysinfo",
@@ -7389,9 +7326,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-deep-link" name = "tauri-plugin-deep-link"
version = "2.6.3" version = "2.6.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ab551d9bec27bb79d1c7fe5ed38c5cfd1cebb86efa745acc65f5853f345f6e56" checksum = "1b3c64e24ebcbc63aaa2d88a9c51e2507084b46620baf39ecb0baa0738a08bb4"
dependencies = [ dependencies = [
"dunce", "dunce",
"plist", "plist",
@@ -7410,9 +7347,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-devtools" name = "tauri-plugin-devtools"
version = "2.2.3" version = "2.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "299a62101a16c14b914ebfaf529b4b64ad9ca534a0ece1c21fc9308d1e3ba9db" checksum = "1acb4d1b8490ee79166b72c3f8c989e066650a11aa94dfd0d71fb05c61cdbed0"
dependencies = [ dependencies = [
"async-stream", "async-stream",
"bytes", "bytes",
@@ -7437,9 +7374,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-dialog" name = "tauri-plugin-dialog"
version = "2.8.4" version = "2.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ce0fb9c2b6d34a9720627ed930ebc8af26f61e648695aecaeb03d5c33c5896f5" checksum = "b7461aaacf1901915ea7b9f5cd74f8224a351ed96af0a62c07fce934003cb34b"
dependencies = [ dependencies = [
"log", "log",
"raw-window-handle", "raw-window-handle",
@@ -7543,13 +7480,13 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-notification" name = "tauri-plugin-notification"
version = "2.8.0" version = "2.7.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5bc6c194a95a535abdfbe3bc1af9a43935b8b9b29c216b8e71af8b59132703cf" checksum = "b939d7b19c8af1381a72ef8474b3aba2b56c1ddac2c23f0c8e96abbcf86b198b"
dependencies = [ dependencies = [
"log", "log",
"notify-rust", "notify-rust",
"rand 0.10.2", "rand 0.9.5",
"serde", "serde",
"serde_json", "serde_json",
"serde_repr", "serde_repr",
@@ -7595,9 +7532,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-shell" name = "tauri-plugin-shell"
version = "2.6.0" version = "2.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7375b25f72f70f6168f27f1ba3a413e30eaa0a2098cff53a4046ebfe9e2f142d" checksum = "8567d42b2c7264355daa1df415ec77f52aa42e885924c86a969be2e920e05521"
dependencies = [ dependencies = [
"encoding_rs", "encoding_rs",
"libc", "libc",
@@ -7652,9 +7589,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-plugin-wdio-webdriver" name = "tauri-plugin-wdio-webdriver"
version = "1.5.1" version = "1.5.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5a775b0b59f6f12b38d1f28667311a89cd07f532f540e27d65c6eadfba1156d8" checksum = "4f76676d510d271d80abcf8b0552987b5b4f0eae5e3f906e9b5660d43c97c62e"
dependencies = [ dependencies = [
"async-trait", "async-trait",
"axum", "axum",
@@ -7725,9 +7662,9 @@ dependencies = [
[[package]] [[package]]
name = "tauri-runtime-wry" name = "tauri-runtime-wry"
version = "2.12.3" version = "2.12.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e1aa9d8b7a120f879f44fdc1053f7b7dbf416bf0bbc6a72b714dec46b782b52f" checksum = "8d61892587aea2a85245bcaec98c6fe46268874c0a0deae5535fb71ca7b93eae"
dependencies = [ dependencies = [
"gtk", "gtk",
"http", "http",
@@ -7735,6 +7672,7 @@ dependencies = [
"log", "log",
"objc2", "objc2",
"objc2-app-kit", "objc2-app-kit",
"once_cell",
"percent-encoding", "percent-encoding",
"raw-window-handle", "raw-window-handle",
"softbuffer", "softbuffer",
@@ -7751,12 +7689,11 @@ dependencies = [
[[package]] [[package]]
name = "tauri-specta" name = "tauri-specta"
version = "2.0.0-rc.26" version = "2.0.0-rc.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fe71fc209fa4c49f959644f1ae2d70f72d5675bcb3d3ad8518122fa7b330994e" checksum = "ee080f36d2ac17ce2f3a82fb53f02d664e8345457de51b56dad3c394dacc41a2"
dependencies = [ dependencies = [
"heck 0.5.0", "heck 0.5.0",
"paste",
"serde", "serde",
"serde_json", "serde_json",
"specta", "specta",
@@ -7770,15 +7707,15 @@ dependencies = [
[[package]] [[package]]
name = "tauri-specta-macros" name = "tauri-specta-macros"
version = "2.0.0-rc.26" version = "2.0.0-rc.25"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dbf5689a1dbf2611e2351b79ba042ca089d0a3ce45274695e79084cd732a5aec" checksum = "6a59dfdce06c98d8d211619bea5fdb39486d8a8c558e12b2d2ce255972320012"
dependencies = [ dependencies = [
"darling 0.24.1", "darling",
"heck 0.5.0", "heck 0.5.0",
"proc-macro2", "proc-macro2",
"quote", "quote",
"syn 3.0.5", "syn 2.0.119",
] ]
[[package]] [[package]]
@@ -7848,10 +7785,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd"
dependencies = [ dependencies = [
"fastrand", "fastrand",
"getrandom 0.4.3", "getrandom 0.3.4",
"once_cell", "once_cell",
"rustix 1.1.4", "rustix 1.1.4",
"windows-sys 0.61.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@@ -8104,9 +8041,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20"
[[package]] [[package]]
name = "tokio" name = "tokio"
version = "1.54.1" version = "1.53.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b52fd3255258d26fac7264ab5a76b53c13367136dc011d3d6ad4f8ed070396a3" checksum = "ce3335fa71841cda333a58d7615b03901380ecf09d59b3296d21f8bbac0dde4e"
dependencies = [ dependencies = [
"bytes", "bytes",
"libc", "libc",
@@ -8462,7 +8399,7 @@ dependencies = [
"flexi_logger", "flexi_logger",
"log", "log",
"nu-ansi-term", "nu-ansi-term",
"parking_lot 0.12.5", "parking_lot",
"tokio", "tokio",
"tracing", "tracing",
"tracing-log", "tracing-log",
@@ -8514,10 +8451,10 @@ dependencies = [
"objc2-core-graphics", "objc2-core-graphics",
"objc2-foundation", "objc2-foundation",
"once_cell", "once_cell",
"png", "png 0.18.1",
"serde", "serde",
"thiserror 2.0.20", "thiserror 2.0.20",
"windows-sys 0.61.2", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@@ -8626,7 +8563,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
dependencies = [ dependencies = [
"memoffset", "memoffset",
"tempfile", "tempfile",
"windows-sys 0.61.2", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@@ -9072,9 +9009,9 @@ dependencies = [
[[package]] [[package]]
name = "webpki-roots" name = "webpki-roots"
version = "1.0.11" version = "1.0.9"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f5f280bbb2b287bc8e7de7e3852945a0ff50d1bbcb5ed42b6c7a568c546df171" checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a"
dependencies = [ dependencies = [
"rustls-pki-types", "rustls-pki-types",
] ]
@@ -9168,7 +9105,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [ dependencies = [
"windows-sys 0.61.2", "windows-sys 0.52.0",
] ]
[[package]] [[package]]
@@ -9189,7 +9126,7 @@ dependencies = [
"objc2-foundation", "objc2-foundation",
"objc2-quartz-core", "objc2-quartz-core",
"raw-window-handle", "raw-window-handle",
"windows-sys 0.61.2", "windows-sys 0.60.2",
"windows-version", "windows-version",
] ]
@@ -9764,7 +9701,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7d6f32a0ff4a9f6f01231eb2059cc85479330739333e0e58cadf03b6af2cca10" checksum = "7d6f32a0ff4a9f6f01231eb2059cc85479330739333e0e58cadf03b6af2cca10"
dependencies = [ dependencies = [
"cfg-if", "cfg-if",
"windows-sys 0.61.2", "windows-sys 0.60.2",
] ]
[[package]] [[package]]
@@ -10096,9 +10033,9 @@ dependencies = [
[[package]] [[package]]
name = "zip" name = "zip"
version = "9.3.0" version = "9.1.3"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "eaeb7f16d2605b49aef586996ded29e65642fab7259a3077e537351df6d80426" checksum = "1156b39d142a34b999c2310a50455e0cb3760bf7efad0122f796d813bee26ccb"
dependencies = [ dependencies = [
"aes", "aes",
"bzip2", "bzip2",
@@ -10109,7 +10046,6 @@ dependencies = [
"getrandom 0.4.3", "getrandom 0.4.3",
"hmac", "hmac",
"indexmap 2.14.0", "indexmap 2.14.0",
"libc",
"lzma-rust2", "lzma-rust2",
"memchr", "memchr",
"nix", "nix",
+4 -4
View File
@@ -61,20 +61,20 @@ tracing-estuary = { git = "https://github.com/Tunglies/tracing-estuary.git", bra
tauri-plugin-clash-verge-sysinfo = { path = "crates/tauri-plugin-clash-verge-sysinfo" } tauri-plugin-clash-verge-sysinfo = { path = "crates/tauri-plugin-clash-verge-sysinfo" }
tracing = "0.1.44" tracing = "0.1.44"
tauri = { version = "2.12.5" } tauri = { version = "2.12.4" }
tauri-plugin-clipboard-manager = "2.5.0" tauri-plugin-clipboard-manager = "2.5.0"
parking_lot = "0.13.0" parking_lot = { version = "0.12.5", features = ["hardware-lock-elision"] }
anyhow = "1.0.104" anyhow = "1.0.104"
chrono = "0.4.45" chrono = "0.4.45"
criterion = { version = "0.8.2", features = ["async_tokio"] } criterion = { version = "0.8.2", features = ["async_tokio"] }
tokio = { version = "1.54.1", features = [ tokio = { version = "1.53.3", features = [
"rt-multi-thread", "rt-multi-thread",
"macros", "macros",
"time", "time",
"sync", "sync",
] } ] }
flexi_logger = "0.31.10" flexi_logger = "0.31.10"
log = "0.4.35" log = "0.4.34"
regex = "1.13.1" regex = "1.13.1"
reqwest = { version = "0.13.5", features = [ reqwest = { version = "0.13.5", features = [
"json", "json",
+1 -1
View File
@@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2024" edition = "2024"
[dependencies] [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 } clash-verge-logging = { workspace = true }
chrono = { workspace = true } chrono = { workspace = true }
log = { workspace = true } log = { workspace = true }
@@ -5,14 +5,14 @@ edition = "2024"
rust-version = "1.91" rust-version = "1.91"
[dependencies] [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 = { workspace = true }
tauri-plugin-clipboard-manager = { workspace = true } tauri-plugin-clipboard-manager = { workspace = true }
parking_lot = { workspace = true } parking_lot = { workspace = true }
serde = { workspace = true, features = ["derive"] } serde = { workspace = true, features = ["derive"] }
# sysinfo 0.38.2 conflicts with dark-light # sysinfo 0.38.2 conflicts with dark-light
# see https://github.com/GuillaumeGomez/sysinfo/issues/1623 # see https://github.com/GuillaumeGomez/sysinfo/issues/1623
sysinfo = { version = "0.40.1", features = ["network", "system"] } sysinfo = { version = "0.39.6", features = ["network", "system"] }
[target.'cfg(not(windows))'.dependencies] [target.'cfg(not(windows))'.dependencies]
libc = "0.2.191" libc = "0.2.191"
@@ -27,9 +27,9 @@ pub struct SysInfo {
impl Default for SysInfo { impl Default for SysInfo {
#[inline] #[inline]
fn default() -> Self { fn default() -> Self {
let system_name = System::name().unwrap_or_else(|_| "Null".into()); let system_name = System::name().unwrap_or_else(|| "Null".into());
let system_version = System::long_os_version().unwrap_or_else(|_| "Null".into()); let system_version = System::long_os_version().unwrap_or_else(|| "Null".into());
let system_kernel_version = System::kernel_version().unwrap_or_else(|_| "Null".into()); let system_kernel_version = System::kernel_version().unwrap_or_else(|| "Null".into());
let system_arch = System::cpu_arch(); let system_arch = System::cpu_arch();
Self { Self {
system_name, system_name,
@@ -130,14 +130,9 @@ pub fn current_gid() -> u32 {
#[inline] #[inline]
pub fn list_network_interfaces() -> Vec<String> { pub fn list_network_interfaces() -> Vec<String> {
let Ok(mut networks) = Networks::new() else { let mut networks = Networks::new();
return Vec::new();
};
networks.refresh(false); networks.refresh(false);
networks networks.keys().map(|name| name.to_owned()).collect()
.keys()
.map(|name| name.to_string_lossy().into_owned())
.collect()
} }
#[inline] #[inline]
+34
View File
@@ -256,6 +256,40 @@ export default defineConfig([
}, },
}, },
}, },
{
files: ['scripts/react/*.{mjs,mts,ts,tsx}'],
languageOptions: {
globals: { ...globals.browser, ...globals.node },
parserOptions: {
projectService: false,
project: './scripts/react/tsconfig.json',
},
},
settings: {
'import-x/resolver-next': [
createTypeScriptImportResolver({
project: './scripts/react/tsconfig.json',
}),
],
},
},
{
files: ['scripts/react/*.{mjs,mts}'],
languageOptions: {
parserOptions: {
projectService: false,
project: './scripts/react/tsconfig.node.json',
},
},
},
{
files: ['scripts/react/{boundaries,material,scene}.tsx'],
// The isolated fixtures use full reloads and preserve aliased Hook names.
rules: {
'react-refresh/only-export-components': 'off',
'@eslint-react/no-unnecessary-use-prefix': 'off',
},
},
{ {
files: ['src/**/*.{ts,tsx,js,jsx,mts,mjs}'], files: ['src/**/*.{ts,tsx,js,jsx,mts,mjs}'],
rules: ipcBoundaryRules(), rules: ipcBoundaryRules(),
+10 -1
View File
@@ -1,6 +1,15 @@
{ {
"$schema": "https://unpkg.com/knip@6/schema.json", "$schema": "https://unpkg.com/knip@6/schema.json",
"entry": ["src/polyfills/*.js!", "scripts/check-contract-changes.mjs!"], "entry": [
"src/polyfills/*.js!",
"scripts/check-contract-changes.mjs!",
"scripts/react/scene.tsx!",
"scripts/react/profiler.ts!",
"scripts/react/boundaries.tsx!",
"scripts/react/material.tsx!",
"scripts/react/yaml.ts!",
"scripts/react/wdyr.ts!"
],
"project": ["src/**/*.{js,jsx,ts,tsx,sass,scss}!", "scripts/**/*.{js,mjs}!"], "project": ["src/**/*.{js,jsx,ts,tsx,sass,scss}!", "scripts/**/*.{js,mjs}!"],
"include": [ "include": [
"files", "files",
+12 -5
View File
@@ -40,7 +40,11 @@
"perf:build": "node scripts/perf/build.mjs", "perf:build": "node scripts/perf/build.mjs",
"perf:run": "node scripts/perf/run.mjs", "perf:run": "node scripts/perf/run.mjs",
"perf:compare": "node scripts/perf/compare.mjs", "perf:compare": "node scripts/perf/compare.mjs",
"perf:test": "node --test scripts/perf/report.test.mjs" "perf:test": "node --test scripts/perf/report.test.mjs",
"dev:profile": "cross-env CVR_REACT_DIAGNOSTICS=profiler node scripts/dev.mjs dev",
"react:ab": "node scripts/react/ab.mjs",
"dev:wdyr": "cross-env CVR_REACT_DIAGNOSTICS=wdyr node scripts/dev.mjs dev",
"audit:react": "node scripts/react/doctor.mjs"
}, },
"dependencies": { "dependencies": {
"@dnd-kit/collision": "^0.5.0", "@dnd-kit/collision": "^0.5.0",
@@ -56,7 +60,7 @@
"@tanstack/react-virtual": "^3.14.13", "@tanstack/react-virtual": "^3.14.13",
"@tauri-apps/api": "2.12.4", "@tauri-apps/api": "2.12.4",
"@tauri-apps/plugin-clipboard-manager": "2.5.0", "@tauri-apps/plugin-clipboard-manager": "2.5.0",
"@tauri-apps/plugin-dialog": "2.8.4", "@tauri-apps/plugin-dialog": "2.8.3",
"@tauri-apps/plugin-fs": "2.7.1", "@tauri-apps/plugin-fs": "2.7.1",
"@tauri-apps/plugin-http": "2.8.3", "@tauri-apps/plugin-http": "2.8.3",
"@tauri-apps/plugin-opener": "2.7.2", "@tauri-apps/plugin-opener": "2.7.2",
@@ -94,7 +98,7 @@
"@biomejs/biome": "^2.5.14", "@biomejs/biome": "^2.5.14",
"@eslint-react/eslint-plugin": "^5.20.5", "@eslint-react/eslint-plugin": "^5.20.5",
"@eslint/js": "^10.0.1", "@eslint/js": "^10.0.1",
"@tauri-apps/cli": "2.12.4", "@tauri-apps/cli": "2.12.3",
"@types/lodash-es": "^4.17.12", "@types/lodash-es": "^4.17.12",
"@types/node": "^26.0.0", "@types/node": "^26.0.0",
"@types/react": "19.3.0", "@types/react": "19.3.0",
@@ -102,7 +106,7 @@
"@types/validator": "^13.15.10", "@types/validator": "^13.15.10",
"@vitejs/plugin-legacy": "^8.2.3", "@vitejs/plugin-legacy": "^8.2.3",
"@vitejs/plugin-react": "^6.1.1", "@vitejs/plugin-react": "^6.1.1",
"@wdio/tauri-service": "1.5.1", "@wdio/tauri-service": "1.5.0",
"adm-zip": "^0.6.1", "adm-zip": "^0.6.1",
"axios": "^1.20.0", "axios": "^1.20.0",
"cli-color": "^2.0.4", "cli-color": "^2.0.4",
@@ -134,7 +138,10 @@
"vite-plugin-svgr": "^5.2.0", "vite-plugin-svgr": "^5.2.0",
"vitest": "^5.0.1", "vitest": "^5.0.1",
"webdriverio": "10.1.0", "webdriverio": "10.1.0",
"ws": "8.22.1" "ws": "8.22.1",
"playwright": "1.64.1",
"@welldone-software/why-did-you-render": "10.0.1",
"react-doctor": "0.9.21"
}, },
"lint-staged": { "lint-staged": {
"*.{ts,tsx,js,mjs}": [ "*.{ts,tsx,js,mjs}": [
+1104 -166
View File
File diff suppressed because it is too large Load Diff
-21
View File
@@ -113,27 +113,6 @@
"ghcr.io/devcontainers/features/rust" "ghcr.io/devcontainers/features/rust"
], ],
"groupName": "Rust toolchain" "groupName": "Rust toolchain"
},
{
"description": "Update the Specta contract toolchain together",
"matchManagers": ["cargo"],
"matchPackageNames": ["specta", "tauri-specta", "specta-typescript"],
"groupName": "specta",
"groupSlug": "specta",
"prCreation": "immediate",
"automerge": false
},
{
"description": "Keep Specta aligned with the supported tauri-specta release",
"matchManagers": ["cargo"],
"matchPackageNames": ["specta", "tauri-specta"],
"allowedVersions": "/^2\\.0\\.0-rc\\.26$/"
},
{
"description": "Keep the TypeScript exporter compatible with Specta rc.26",
"matchManagers": ["cargo"],
"matchPackageNames": ["specta-typescript"],
"allowedVersions": "/^0\\.0\\.13$/"
} }
], ],
"customManagers": [ "customManagers": [
+47
View File
@@ -0,0 +1,47 @@
# React diagnostics
`pnpm dev:profile` runs the usual development launcher with a root React
Profiler. In WebView DevTools, `window.__CVR_REACT_PROFILER__.read()` returns
copies of the latest 1,000 standard Profiler callback records; `.clear()` drops
them. Records contain timing and commit metadata, never business props. Normal
development and production builds do not load the collector.
`pnpm dev:wdyr` instead initializes why-did-you-render before loading the app
and uses its development JSX runtime. It reports avoidable renders for
`ProxyItem`, `ProxyItemMini` and `ProxyTunCard` in the console. The modes are
mutually exclusive; `CVR_REACT_DIAGNOSTICS` accepts only `profiler` or `wdyr` on
the development server. Each mode uses its own Vite cache. Production builds
ignore this variable and include neither diagnostic entry nor runtime.
`pnpm audit:react` scans supported source files under `src`. Pass file paths to
limit the scan, for example `pnpm audit:react src/hooks/use-proxy-delay-state.ts`;
add `--json` for the original structured report. Diagnostics are advisory.
Execution errors and incomplete scans fail the command. Telemetry, remote scores
and supply-chain scanning are disabled; the command does not fix code, erase
historical diagnostics or run in CI.
Install the browser explicitly with `pnpm exec playwright install chromium`.
Then run `pnpm react:ab --base <ref> --head <ref>`. Both Git trees run against
one installed dependency set and the same fixed DNS, delay-hook and proxy-tab
fixtures. The command never installs a browser or changes either checkout.
It retains source hashes, dependency identity, browser/environment information,
behavior assertions, render observations and profiles under `/tmp/cvr-react-ab-*`.
Each version and mode has separate Vite caches and output. Three pairs alternate
order in ordinary development, Strict Mode and production. A behavior failure
returns a nonzero exit status. DNS stale serialization and same-value renders
are observations so a known baseline can be compared with its fix. Append
`--assert dns` to require the head version to keep the latest serialized YAML in sync with
continuous field edits; this is the focused DNS regression assertion.
`--assert delay` requires same-value delay updates to retain their reference and memoized consumer,
while each changed field (including clearing elapsed time) propagates.
`--assert tabs` requires zero tab renders during unrelated parent updates. Use
`--assert dns --assert delay --assert tabs` for a cumulative candidate.
The fixtures execute the actual selected components and hook. IPC, data,
editor and UI rendering boundaries are replaced, and outgoing network requests
are rejected. DNS serialization is observed at `js-yaml.dump`; tab execution is
observed at the Material Paper boundary. These are component results, not native
Tauri integration, real DNS/proxy/TUN writes, or whole-application CPU/RSS gains.
Production React does not emit Profiler callbacks. Timing is diagnostic and has
no hard acceptance threshold; measure native release CPU/RSS separately and
calibrate A/A before claiming a performance gain. Keep raw reports out of Git.
+596
View File
@@ -0,0 +1,596 @@
import assert from 'node:assert/strict'
import { spawnSync } from 'node:child_process'
import { createHash } from 'node:crypto'
import {
cp,
mkdir,
mkdtemp,
readFile,
readdir,
realpath,
symlink,
writeFile,
} from 'node:fs/promises'
import os from 'node:os'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { parseArgs } from 'node:util'
import react from '@vitejs/plugin-react'
import { chromium } from 'playwright'
import { build, createServer, preview } from 'vite'
const { values } = parseArgs({
options: {
base: { type: 'string' },
head: { type: 'string' },
assert: { type: 'string', multiple: true },
},
})
if (!values.base || !values.head)
throw new Error('Usage: pnpm react:ab --base <ref> --head <ref>')
const assertions = new Set(values.assert ?? [])
for (const name of assertions) {
if (!['dns', 'delay', 'tabs'].includes(name))
throw new Error(`Unknown regression assertion: ${name}`)
}
const harness = path.dirname(fileURLToPath(import.meta.url))
const root = path.resolve(harness, '../..')
const output = await mkdtemp('/tmp/cvr-react-ab-')
const dependencies = path.join(root, 'node_modules')
const frozenHarness = path.join(output, 'harness')
await cp(harness, frozenHarness, {
recursive: true,
filter: (file) => !path.basename(file).startsWith('tsconfig'),
})
const files = [
'src/components/setting/mods/dns-viewer.tsx',
'src/hooks/use-proxy-delay-state.ts',
'src/components/home/proxy-tun-card.tsx',
]
const hash = (content) => createHash('sha256').update(content).digest('hex')
function git(...args) {
const result = spawnSync('git', args, {
cwd: root,
maxBuffer: 128 * 1024 * 1024,
})
if (result.status !== 0) throw new Error(result.stderr.toString())
return result.stdout
}
const manifest = {
assertions: [...assertions],
environment: {
platform: process.platform,
architecture: process.arch,
node: process.version,
os: os.release(),
lock: hash(await readFile(path.join(root, 'pnpm-lock.yaml'))),
},
harness: Object.fromEntries(
await Promise.all(
(await readdir(frozenHarness)).map(async (file) => [
file,
hash(await readFile(path.join(frozenHarness, file))),
]),
),
),
installedLock: hash(
await readFile(path.join(dependencies, '.pnpm/lock.yaml')),
),
sources: {},
runs: [],
}
let browser
try {
// Playwright intentionally fails here if its explicitly installed browser is missing.
browser = await chromium.launch({ headless: true })
manifest.environment.browser = browser.version()
for (const [variant, ref] of Object.entries({
base: values.base,
head: values.head,
})) {
const tree = git('rev-parse', '--verify', `${ref}^{tree}`).toString().trim()
const source = path.join(output, variant)
await mkdir(source)
const archive = git('archive', tree, 'src')
const extracted = spawnSync('tar', ['-x', '-C', source], { input: archive })
if (extracted.status !== 0) throw new Error(extracted.stderr.toString())
await cp(frozenHarness, path.join(source, 'fixture'), { recursive: true })
await symlink(dependencies, path.join(source, 'node_modules'), 'dir')
manifest.sources[variant] = {
ref,
tree,
files: Object.fromEntries(
await Promise.all(
files.map(async (file) => [
file,
hash(await readFile(path.join(source, file))),
]),
),
),
}
}
for (const mode of ['development', 'strict', 'production']) {
const servers = {}
try {
for (const variant of ['base', 'head']) {
const source = path.join(output, variant)
const fixture = path.join(source, 'fixture')
const boundary = path.join(fixture, 'boundaries.tsx')
const aliases = [
'@/hooks/use-clash',
'@/hooks/use-verge',
'@/hooks/use-profiles',
'@/hooks/use-system-state',
'@/hooks/use-system-proxy-state',
'@/services/states',
'@/services/cmds',
'@/services/mutate',
'@/services/notice-service',
'@/components/base',
'@/components/shared/proxy-control-switches',
'react-i18next',
].map((find) => ({ find, replacement: boundary }))
const config = {
configFile: false,
root: fixture,
cacheDir: path.join(output, 'cache', variant, mode),
plugins: [react()],
resolve: {
alias: [
...aliases,
{
find: '@/services/delay',
replacement: path.join(fixture, 'delay.ts'),
},
{
find: /^@mui\/material$/,
replacement: path.join(fixture, 'material.tsx'),
},
{
find: 'fixture-material',
replacement: path.join(dependencies, '@mui/material/index.mjs'),
},
{ find: 'js-yaml', replacement: path.join(fixture, 'yaml.ts') },
{
find: 'fixture-yaml',
replacement: path.join(
dependencies,
'js-yaml/dist/js-yaml.mjs',
),
},
{ find: '@', replacement: path.join(source, 'src') },
{
find: /^react(?=\/|$)/,
replacement: path.join(dependencies, 'react'),
},
{
find: /^react-dom(?=\/|$)/,
replacement: path.join(dependencies, 'react-dom'),
},
],
dedupe: ['react', 'react-dom'],
},
define: { OS_PLATFORM: JSON.stringify(process.platform) },
server: {
host: '127.0.0.1',
port: 0,
hmr: false,
fs: {
allow: [
output,
await realpath(output),
await realpath(dependencies),
],
},
},
build: {
outDir: path.join(output, 'dist', variant, mode),
emptyOutDir: true,
},
preview: { host: '127.0.0.1', port: 0 },
}
if (mode === 'production') {
await build(config)
servers[variant] = await preview(config)
} else {
servers[variant] = await createServer(config)
await servers[variant].listen()
}
}
for (const [pair, order] of [
['base', 'head'],
['head', 'base'],
['base', 'head'],
].entries()) {
for (const variant of order) {
const label = `${mode}-${pair + 1}-${variant}`
const context = await browser.newContext()
const errors = []
const address = servers[variant].httpServer.address()
const origin = `http://127.0.0.1:${address.port}`
await context.route('**/*', (route) => {
if (new URL(route.request().url()).origin === origin)
return route.continue()
errors.push(`Unexpected network: ${route.request().url()}`)
return route.abort()
})
const page = await context.newPage()
page.on('pageerror', (error) => errors.push(String(error)))
const result = { label, variant, mode, pair: pair + 1, errors }
try {
await page.goto(`${origin}/${mode === 'strict' ? '?strict' : ''}`)
await page.waitForSelector('#delay', { timeout: 60000 })
await page.waitForFunction(
() => window.__REACT_AB__.read().listeners.length === 1,
)
result.tabs = await tabs(page)
if (variant === 'head' && assertions.has('tabs')) {
assert.equal(
result.tabs.unrelated,
0,
'Unrelated parent updates must not rerender the memoized tabs',
)
}
result.dns = await dns(page)
if (variant === 'head' && assertions.has('dns')) {
for (const edit of result.dns.edits) {
assert.equal(
edit.serialized.at(-1),
edit.value,
'Latest DNS serialization must match the edited field',
)
}
}
result.delay = await delay(page)
if (variant === 'head' && assertions.has('delay')) {
for (const [index, observation] of result.delay.entries()) {
assert.equal(
observation.sameReference,
index === 1,
'Preserve the reference only when all three delay fields match',
)
}
assert.equal(
result.delay[1].childRenders,
result.delay[0].childRenders,
'Equal delay state must not rerender the memoized consumer',
)
}
result.profile = await page.evaluate(() => {
const before = window.__CVR_REACT_PROFILER__.read()
window.__CVR_REACT_PROFILER__.clear()
return { before, after: window.__CVR_REACT_PROFILER__.read() }
})
assert.equal(result.profile.after.length, 0)
result.collector = await page.evaluate(() =>
window.__REACT_AB__.checkCollector(),
)
assert.deepEqual(result.collector, {
length: 1000,
first: '5',
last: '1004',
copied: true,
cleared: true,
})
if (mode !== 'production')
assert(
result.profile.before.length > 0 &&
result.profile.before.length <= 1000,
)
assert.deepEqual(errors, [])
assert.deepEqual(
await page.evaluate(() =>
window.__REACT_AB__.fixture.notices.filter(
(notice) => notice.error,
),
),
[],
)
result.ok = true
} catch (error) {
result.snapshot = await page
.evaluate(() => window.__REACT_AB__?.read())
.catch(() => null)
result.ok = false
result.error = String(error)
throw error
} finally {
manifest.runs.push(result)
await writeFile(
path.join(output, `${label}.json`),
JSON.stringify(result, null, 2),
)
await context.close()
console.log(`${label}: ${result.ok ? 'pass' : 'FAIL'}`)
}
}
}
} finally {
for (const server of Object.values(servers)) {
if (server.close) await server.close()
else
await new Promise((resolve, reject) =>
server.httpServer.close((error) =>
error ? reject(error) : resolve(),
),
)
}
}
}
} finally {
await browser?.close()
await writeFile(
path.join(output, 'report.json'),
JSON.stringify(manifest, null, 2),
)
console.log(`Evidence: ${output}`)
}
async function settle(page) {
await page.waitForTimeout(100)
}
async function tabs(page) {
const buttons = page.locator('#tabs .MuiPaper-root')
const readTabs = () =>
page.evaluate(() =>
Object.fromEntries(
window.__REACT_AB__.fixture.tabRenders.map((tab) => [
tab.label.split('.').at(-1),
tab,
]),
),
)
assert.equal(await buttons.count(), 2)
assert.equal((await readTabs()).systemProxy.active, true)
assert.equal((await readTabs()).tunMode.active, false)
const before = await page.evaluate(
() => window.__REACT_AB__.fixture.tabRenders.length,
)
for (let index = 0; index < 5; index++) {
await page.evaluate(() => window.__REACT_AB__.update())
await settle(page)
}
const unrelated =
(await page.evaluate(() => window.__REACT_AB__.fixture.tabRenders.length)) -
before
await buttons.nth(1).click()
await settle(page)
assert.equal((await readTabs()).systemProxy.active, false)
assert.equal((await readTabs()).tunMode.active, true)
assert.equal(
await page.evaluate(() =>
localStorage.getItem('clash-verge-proxy-active-tab'),
),
'tun',
)
assert(
(await page.locator('#fixture-controls').textContent()).endsWith(
'toggles.tunMode',
),
)
await page.evaluate(() => window.__REACT_AB__.update({ mounted: false }))
await settle(page)
assert(
(await page.locator('#fixture-controls').textContent()).endsWith(
'toggles.tunMode',
),
)
await page.evaluate(() => window.__REACT_AB__.update({ mounted: true }))
await settle(page)
assert.equal((await readTabs()).tunMode.active, true)
assert.equal((await readTabs()).systemProxy.active, false)
await buttons.nth(0).click()
await settle(page)
assert.equal((await readTabs()).systemProxy.active, true)
assert.equal((await readTabs()).tunMode.active, false)
assert.equal(
await page.evaluate(() =>
localStorage.getItem('clash-verge-proxy-active-tab'),
),
'system',
)
await page.evaluate(() => {
window.__REACT_AB__.fixture.indicator = true
window.__REACT_AB__.fixture.verge.enable_tun_mode = true
window.__REACT_AB__.update()
})
await settle(page)
assert(Object.values(await readTabs()).every((tab) => tab.indicator))
await page.evaluate(() => {
window.__REACT_AB__.fixture.language = 'zh'
window.__REACT_AB__.update()
})
await settle(page)
const updated = Object.values(await readTabs())
assert(updated.every((tab) => tab.label.startsWith('zh:')))
assert(
(await page.locator('#tabs').textContent()).includes(
'zh:home.components.proxyTun.status.systemProxyEnabled',
),
)
const beforeColor = await buttons
.nth(0)
.evaluate((element) => getComputedStyle(element).backgroundColor)
await page.evaluate(() => {
window.__REACT_AB__.fixture.theme = 'dark'
window.__REACT_AB__.update()
})
await settle(page)
const color = await buttons
.nth(0)
.evaluate((element) => getComputedStyle(element).backgroundColor)
assert.notEqual(color, beforeColor)
return { unrelated, updated, beforeColor, color }
}
async function dns(page) {
await page.evaluate(() => window.__REACT_AB__.open())
const listen = page.locator('#dns input[placeholder=":53"]')
await page.waitForFunction(
() =>
document.querySelector('#dns input[placeholder=":53"]')?.value ===
':5353',
)
const edits = []
for (const value of [':54', ':55', ':56']) {
await page.evaluate(() => {
window.__REACT_AB__.fixture.serialized.length = 0
})
await listen.fill(value)
await settle(page)
assert.equal(await listen.inputValue(), value)
edits.push({
value,
serialized: await page.evaluate(() =>
window.__REACT_AB__.fixture.serialized.map(
(config) => config.dns.listen,
),
),
})
}
await page.evaluate(() => {
window.__REACT_AB__.fixture.serialized.length = 0
})
await listen.fill(':')
await listen.pressSequentially('567', { delay: 0 })
await settle(page)
assert.equal(await listen.inputValue(), ':567')
edits.push({
value: ':567',
rapid: true,
serialized: await page.evaluate(() =>
window.__REACT_AB__.fixture.serialized.map((config) => config.dns.listen),
),
})
await page.locator('#save-dns').click()
await page.waitForFunction(
() => window.__REACT_AB__.fixture.saved.length === 1,
)
assert.equal(
await page.evaluate(() => window.__REACT_AB__.fixture.saved[0].dns.listen),
':567',
)
await page.evaluate(() => window.__REACT_AB__.open())
await page.waitForSelector('#dns')
await page
.getByRole('button', { name: /shared.actions.resetToDefault$/ })
.click()
await settle(page)
assert.equal(await listen.inputValue(), ':53')
await page
.getByRole('button', { name: /shared.editorModes.advanced$/ })
.click()
await settle(page)
const editor = page.locator('#fixture-monaco')
assert.match(await editor.inputValue(), /listen: ["']?:53/)
await editor.fill(
'dns:\n enable: true\n listen: ":8053"\nhosts:\n example.test: 127.0.0.1\n',
)
await page
.getByRole('button', { name: /shared.editorModes.visualization$/ })
.click()
await settle(page)
assert.equal(await listen.inputValue(), ':8053')
await page
.getByRole('button', { name: /shared.editorModes.advanced$/ })
.click()
await settle(page)
await page.locator('#save-dns').click()
await page.waitForFunction(
() => window.__REACT_AB__.fixture.saved.length === 2,
)
const saved = await page.evaluate(() => window.__REACT_AB__.fixture.saved)
assert.equal(saved[1].dns.listen, ':8053')
assert.equal(saved[1].hosts['example.test'], '127.0.0.1')
return { edits, saved }
}
async function delay(page) {
const updates = [
{ delay: 10, elapsed: 12, updatedAt: 100 },
{ delay: 10, elapsed: 12, updatedAt: 100 },
{ delay: 11, elapsed: 12, updatedAt: 100 },
{ delay: 11, elapsed: 13, updatedAt: 100 },
{ delay: 11, elapsed: 13, updatedAt: 101 },
{ delay: 11, updatedAt: 101 },
{ delay: -1, updatedAt: 102 },
{ delay: 0, updatedAt: 103 },
{ delay: 1000000, elapsed: 20, updatedAt: 104 },
]
const observations = []
for (const update of updates) {
await page.evaluate((value) => {
window.__REACT_AB__.remember()
window.__REACT_AB__.delay.emit('Node 1', 'Group 1', value)
}, update)
await settle(page)
const result = await page.evaluate(() => window.__REACT_AB__.read())
assert.deepEqual(result.delay, update)
observations.push({
update,
sameReference: result.sameReference,
childRenders: result.childRenders,
})
}
await page.evaluate(() => window.__REACT_AB__.request())
await settle(page)
assert.equal(
await page.evaluate(() => window.__REACT_AB__.read().delay.delay),
-2,
)
await page.evaluate(() =>
window.__REACT_AB__.delay.finish({ delay: 0, updatedAt: 200 }),
)
await settle(page)
assert.equal(
await page.evaluate(() => window.__REACT_AB__.read().delay.delay),
0,
)
await page.evaluate(() => window.__REACT_AB__.request())
await settle(page)
assert.equal(
await page.evaluate(() => window.__REACT_AB__.read().delay.delay),
-2,
)
await page.evaluate(() =>
window.__REACT_AB__.delay.finish({
delay: 1000000,
elapsed: 20,
updatedAt: 201,
}),
)
await settle(page)
assert.deepEqual(
await page.evaluate(() => window.__REACT_AB__.read().delay),
{ delay: 1000000, elapsed: 20, updatedAt: 201 },
)
await page.evaluate(() => window.__REACT_AB__.update({ name: 'Node 2' }))
await settle(page)
let state = await page.evaluate(() => window.__REACT_AB__.read())
assert.deepEqual(state.listeners, ['Group 1:Node 2'])
assert.deepEqual(state.delay, { delay: -1, updatedAt: 0 })
await page.evaluate(() => window.__REACT_AB__.update({ group: 'Group 2' }))
await settle(page)
state = await page.evaluate(() => window.__REACT_AB__.read())
assert.deepEqual(state.listeners, ['Group 2:Node 2'])
for (const name of ['DIRECT', 'Unknown']) {
await page.evaluate((name) => window.__REACT_AB__.update({ name }), name)
await settle(page)
const preset = await page.evaluate(() => window.__REACT_AB__.read())
assert.deepEqual(preset.listeners, [])
assert.deepEqual(preset.delay, { delay: -1, updatedAt: 0 })
}
await page.evaluate(() => window.__REACT_AB__.update({ name: 'Node 2' }))
await settle(page)
assert.deepEqual(
await page.evaluate(() => window.__REACT_AB__.read().listeners),
['Group 2:Node 2'],
)
await page.evaluate(() => window.__REACT_AB__.update({ mounted: false }))
await settle(page)
assert.deepEqual(
await page.evaluate(() => window.__REACT_AB__.read().listeners),
[],
)
return observations
}
+68
View File
@@ -0,0 +1,68 @@
import { Switch as MuiSwitch } from '@mui/material'
import { fixture } from './state'
const translations = {
en: (key: string) => `en:${key}`,
zh: (key: string) => `zh:${key}`,
}
export const useTranslation = () => ({
t: translations[fixture.language as 'en' | 'zh'],
})
export const useVerge = () => ({ verge: fixture.verge })
export const useProfiles = () => ({ current: { uid: 'fixture' } })
export const useClash = () => ({
mutateClash: () => {
throw new Error('Unexpected mutateClash')
},
})
export const useSystemState = () => ({ isTunModeAvailable: fixture.available })
export const useSystemProxyState = () => ({ indicator: fixture.indicator })
export const useThemeMode = () => fixture.theme
export const getDnsConfigContent = async () => {
fixture.commands.push('getDnsConfigContent')
return fixture.initialDns
}
export const saveDnsConfig = async (config: any) => {
fixture.commands.push('saveDnsConfig')
fixture.saved.push(structuredClone(config))
}
export const validateDnsConfig = async () => {
fixture.commands.push('validateDnsConfig')
return { status: 'valid' }
}
export const applyDnsConfig = () => {
throw new Error('Unexpected applyDnsConfig')
}
export const mutate = (fn: () => unknown) => fn()
export const showNotice = {
error: (...args: any[]) => fixture.notices.push({ error: args }),
success: (...args: any[]) => fixture.notices.push({ success: args }),
}
export const Switch = MuiSwitch
export function BaseDialog({ open, title, children, onClose, onOk }: any) {
return open ? (
<section id="dns">
{title}
<button id="close-dns" onClick={onClose}>
close
</button>
<button id="save-dns" onClick={onOk}>
save
</button>
{children}
</section>
) : null
}
export function MonacoEditor({ value, onChange }: any) {
return (
<textarea
id="fixture-monaco"
value={value}
onChange={(event) => onChange(event.target.value)}
/>
)
}
export default function ProxyControlSwitches({ label }: any) {
return <output id="fixture-controls">{label}</output>
}
+36
View File
@@ -0,0 +1,36 @@
type Update = { delay: number; elapsed?: number; updatedAt: number }
const listeners = new Map<string, (update: Update) => void>()
const cache = new Map<string, Update>()
let finish: ((update: Update) => void) | undefined
const key = (name: string, group: string) => `${group}:${name}`
export const delayFixture = {
listeners,
emit(name: string, group: string, update: Update) {
cache.set(key(name, group), update)
listeners.get(key(name, group))?.(update)
},
finish(update: Update) {
if (!finish) throw new Error('No pending delay request')
finish(update)
finish = undefined
},
}
export default {
setListener(name: string, group: string, listener: (update: Update) => void) {
listeners.set(key(name, group), listener)
},
removeListener(name: string, group: string) {
listeners.delete(key(name, group))
},
getDelayUpdate(name: string, group: string) {
return cache.get(key(name, group))
},
getDelayFix() {
return -1
},
checkDelay() {
return new Promise<Update>((resolve) => {
finish = resolve
})
},
}
+77
View File
@@ -0,0 +1,77 @@
import { spawnSync } from 'node:child_process'
import { statSync } from 'node:fs'
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import { parseArgs } from 'node:util'
import { globSync } from 'glob'
const { values, positionals } = parseArgs({
options: { json: { type: 'boolean' } },
allowPositionals: true,
})
const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../..')
const files = positionals.length
? positionals
: globSync('src/**/*.{js,jsx,ts,tsx,mjs,cjs,mts,cts,html}', {
cwd: root,
nodir: true,
}).sort()
if (files.length === 0) throw new Error('No source files found')
for (const file of files) {
if (
!/\.(?:[jt]sx?|[cm][jt]s|html)$/.test(file) ||
!statSync(path.resolve(root, file)).isFile()
)
throw new Error(`Expected a source file: ${file}`)
}
const executable = fileURLToPath(
new URL('../bin/react-doctor.js', import.meta.resolve('react-doctor')),
)
const result = spawnSync(
process.execPath,
[
executable,
...files,
'--json',
'--no-telemetry',
'--no-supply-chain',
'--blocking',
'none',
'--yes',
],
{
cwd: root,
encoding: 'utf8',
stdio: ['ignore', 'pipe', 'inherit'],
maxBuffer: 64 * 1024 * 1024,
},
)
if (result.error) throw result.error
if (result.signal)
throw new Error(`React Doctor terminated by ${result.signal}`)
const report = JSON.parse(result.stdout)
if (values.json) {
process.stdout.write(result.stdout)
} else {
for (const diagnostic of report.diagnostics) {
console.log(
`${diagnostic.filePath}:${diagnostic.line}:${diagnostic.column} ${diagnostic.severity} ${diagnostic.plugin}/${diagnostic.rule}\n ${diagnostic.message}`,
)
}
console.log(
`${report.summary.totalDiagnosticCount} advisory diagnostics across ${report.projects.reduce((count, project) => count + project.analyzedFileCount, 0)} analyzed files`,
)
}
// In 0.9.21, --blocking none also masks linter execution failures.
const incomplete = report.projects.filter((project) => !project.complete)
if (report.error) console.error(report.error.message)
for (const project of incomplete) {
console.error(
'Incomplete React Doctor scan:',
project.directory,
project.skippedCheckReasons,
)
}
process.exitCode =
result.status || (!report.ok || incomplete.length > 0 ? 1 : 0)
+5
View File
@@ -0,0 +1,5 @@
<!doctype html>
<html lang="en">
<head><meta charset="UTF-8" /><title>React component A/B</title></head>
<body><div id="root"></div><script type="module" src="./scene.tsx"></script></body>
</html>
+16
View File
@@ -0,0 +1,16 @@
import { Paper as MuiPaper } from 'fixture-material'
export * from 'fixture-material'
import { fixture } from './state'
export function Paper(props: any) {
const { onClick, elevation, children, sx } = props
if (onClick && sx?.maxWidth === 160) {
fixture.tabRenders.push({
active: elevation === 2,
indicator: Boolean(children[2]),
label: children[1].props.children,
})
}
return <MuiPaper {...props} />
}
+19
View File
@@ -0,0 +1,19 @@
import type { ProfilerOnRenderCallback } from 'react'
export type ProfileRecord = Parameters<ProfilerOnRenderCallback>
const records: ProfileRecord[] = []
export const recordRender: ProfilerOnRenderCallback = (...record) => {
if (records.length === 1000) records.shift()
records.push(record)
}
export const profiler = {
read: () => records.map((record) => [...record]),
clear: () => {
records.length = 0
},
}
Object.assign(window, { __CVR_REACT_PROFILER__: profiler })
+124
View File
@@ -0,0 +1,124 @@
import { ThemeProvider, createTheme } from '@mui/material'
import {
Profiler,
StrictMode,
memo,
useEffect,
useMemo,
useRef,
useState,
} from 'react'
import { createRoot } from 'react-dom/client'
import { ProxyTunCard } from '@/components/home/proxy-tun-card'
import { DnsViewer } from '@/components/setting/mods/dns-viewer'
import { useProxyDelayState } from '@/hooks/use-proxy-delay-state'
import { delayFixture } from './delay'
import { profiler, recordRender } from './profiler'
import { fixture } from './state'
const member = (name: string): any =>
name === 'Unknown'
? {
kind: 'unresolved',
ref: { kind: 'unresolved', name, reason: 'missing' },
}
: {
kind: 'node',
ref: { kind: 'node', recordId: name, name },
node: { name, history: [], source: { kind: 'core', proxyName: name } },
}
let update: (patch?: any) => void
let open: () => void
let delay: ReturnType<typeof useProxyDelayState>
let previous: unknown
let childRenders = 0
const DelayValue = memo(({ state }: any) => {
childRenders++
return <output id="delay">{JSON.stringify(state)}</output>
})
function DelayProbe({ name, group }: any) {
const resolved = useMemo(() => member(name), [name])
const current = useProxyDelayState(resolved, group)
useEffect(() => {
delay = current
})
return <DelayValue state={current.delayState} />
}
function Scene() {
const [state, setState] = useState({
revision: 0,
name: 'Node 1',
group: 'Group 1',
mounted: true,
})
const ref = useRef<any>(null)
useEffect(() => {
update = (patch = {}) =>
setState((old) => ({ ...old, ...patch, revision: old.revision + 1 }))
open = () => ref.current.open()
}, [])
const mode = fixture.theme
const theme = useMemo(() => createTheme({ palette: { mode } }), [mode])
return (
<ThemeProvider theme={theme}>
<main data-revision={state.revision}>
<section id="tabs" key={state.mounted ? 'mounted' : 'reset'}>
<ProxyTunCard />
</section>
<DnsViewer ref={ref} />
{state.mounted && <DelayProbe name={state.name} group={state.group} />}
</main>
</ThemeProvider>
)
}
const strict = new URLSearchParams(location.search).has('strict')
const app = (
<Profiler id="fixtures" onRender={recordRender}>
<Scene />
</Profiler>
)
createRoot(document.getElementById('root')!).render(
strict ? <StrictMode>{app}</StrictMode> : app,
)
Object.assign(window, {
__REACT_AB__: {
update: (patch?: any) => update(patch),
open: () => open(),
fixture,
delay: delayFixture,
request: () => {
void delay.onDelay()
},
remember: () => {
previous = delay.delayState
},
checkCollector: () => {
profiler.clear()
for (let index = 0; index < 1005; index++)
recordRender(String(index), 'update', 1, 2, 3, 4)
const records = profiler.read()
records[0][0] = 'changed copy'
const preserved = profiler.read()
const result = {
length: preserved.length,
first: preserved[0][0],
last: preserved.at(-1)![0],
copied: preserved[0][0] === '5',
cleared: false,
}
profiler.clear()
result.cleared = profiler.read().length === 0
return result
},
read: () => ({
fixture,
profile: profiler.read(),
childRenders,
sameReference: previous === delay.delayState,
delay: delay.delayState,
listeners: [...delayFixture.listeners.keys()],
}),
},
})
+13
View File
@@ -0,0 +1,13 @@
export const fixture = {
language: 'en',
theme: 'light' as 'light' | 'dark',
verge: { enable_tun_mode: false },
indicator: false,
available: true,
initialDns: 'dns:\n enable: true\n listen: ":5353"\n',
saved: [] as any[],
serialized: [] as any[],
tabRenders: [] as any[],
notices: [] as any[],
commands: [] as string[],
}
+12
View File
@@ -0,0 +1,12 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"paths": {
"@/*": ["../../src/*"],
"@root/*": ["../../*"],
"fixture-material": ["../../node_modules/@mui/material"],
"fixture-yaml": ["../../node_modules/js-yaml"]
}
},
"include": ["./*.ts", "./*.tsx", "../../src"]
}
+8
View File
@@ -0,0 +1,8 @@
{
"extends": "../../tsconfig.json",
"compilerOptions": {
"allowJs": true,
"types": ["node"]
},
"include": ["./*.mjs", "./*.mts"]
}
+61
View File
@@ -0,0 +1,61 @@
import path from 'node:path'
import { normalizePath, type Plugin } from 'vite'
export function reactDiagnostics(mode: string | undefined): Plugin[] {
if (!mode) return []
return [
{
name: 'react-diagnostics',
enforce: 'pre',
apply: 'serve',
config() {
if (mode !== 'profiler' && mode !== 'wdyr')
throw new Error(`Unknown CVR_REACT_DIAGNOSTICS mode: ${mode}`)
return {
cacheDir: path.resolve('node_modules/.vite', mode),
...(mode === 'wdyr'
? {
oxc: {
jsx: {
importSource: '@welldone-software/why-did-you-render',
},
},
optimizeDeps: {
include: [
'@welldone-software/why-did-you-render',
'@welldone-software/why-did-you-render/jsx-dev-runtime',
],
},
}
: {}),
}
},
transformIndexHtml(html) {
if (mode !== 'wdyr') return html
const entry = 'src="./main.tsx"'
if (!html.includes(entry))
throw new Error('WDYR entry no longer matches index.html')
return html.replace(
entry,
`src="/@fs/${normalizePath(path.resolve('scripts/react/wdyr.ts'))}"`,
)
},
transform(code, id) {
if (mode !== 'profiler') return
if (id !== normalizePath(path.resolve('src/main.tsx'))) return
const open = '<React.StrictMode>'
const close = '</React.StrictMode>'
if (!code.includes(open) || !code.includes(close)) {
throw new Error('React profiling entry no longer matches main.tsx')
}
return {
code: `import { Profiler } from 'react'\nimport { recordRender } from '../scripts/react/profiler'\n${code}`
.replace(open, `<Profiler id="app" onRender={recordRender}>${open}`)
.replace(close, `${close}</Profiler>`),
map: null,
}
},
},
]
}
+10
View File
@@ -0,0 +1,10 @@
import whyDidYouRender from '@welldone-software/why-did-you-render'
import React from 'react'
whyDidYouRender(React, {
include: [/^(ProxyItem|ProxyItemMini|ProxyTunCard)$/],
trackAllPureComponents: false,
trackHooks: true,
})
await import('../../src/main')
+9
View File
@@ -0,0 +1,9 @@
import * as yaml from 'fixture-yaml'
export * from 'fixture-yaml'
import { fixture } from './state'
export function dump(value: any, options: any) {
fixture.serialized.push(structuredClone(value))
return yaml.dump(value, options)
}
+15 -15
View File
@@ -29,7 +29,7 @@ perf-harness = ["dep:tauri-plugin-wdio-webdriver"]
identifier = "io.github.clash-verge-rev.clash-verge-rev" identifier = "io.github.clash-verge-rev.clash-verge-rev"
[build-dependencies] [build-dependencies]
tauri-build = { version = "2.7.4", features = [] } tauri-build = { version = "2.7.3", features = [] }
[dependencies] [dependencies]
clash-verge-draft = { workspace = true } clash-verge-draft = { workspace = true }
@@ -58,8 +58,8 @@ smartstring = { workspace = true, features = ["serde"] }
bitflags = { workspace = true } bitflags = { workspace = true }
socket2 = { workspace = true } socket2 = { workspace = true }
warp = { version = "0.4.3", features = ["server"] } warp = { version = "0.4.3", features = ["server"] }
open = "5.4.5" open = "5.4.4"
rfd = { version = "0.17.2", default-features = false, features = ["common-controls-v6"] } rfd = { version = "0.16.0", default-features = false, features = ["common-controls-v6"] }
dirs = "7.0.0" dirs = "7.0.0"
dunce = "1.0.5" dunce = "1.0.5"
nanoid = "0.5" nanoid = "0.5"
@@ -72,14 +72,14 @@ sysproxy = { git = "https://github.com/clash-verge-rev/sysproxy-rs.git", branch
"guard", "guard",
] } ] }
network-interface = { version = "2.0.5", features = ["serde"] } network-interface = { version = "2.0.5", features = ["serde"] }
tauri-plugin-shell = "2.6.0" tauri-plugin-shell = "2.5.0"
tauri-plugin-opener = "2.7.2" tauri-plugin-opener = "2.7.2"
tauri-plugin-dialog = "2.8.4" tauri-plugin-dialog = "2.8.3"
tauri-plugin-fs = "2.7.1" tauri-plugin-fs = "2.7.1"
tauri-plugin-process = "2.4.0" tauri-plugin-process = "2.4.0"
tauri-plugin-deep-link = "2.6.3" tauri-plugin-deep-link = "2.6.2"
tauri-plugin-window-state = "2.5.0" tauri-plugin-window-state = "2.5.0"
zip = "9.2.2" zip = "9.1.3"
flate2 = "1.1.10" flate2 = "1.1.10"
reqwest_dav = "0.3.3" reqwest_dav = "0.3.3"
aes-gcm = { version = "0.11.1" } aes-gcm = { version = "0.11.1" }
@@ -89,27 +89,27 @@ getrandom = "0.4.3"
futures = "0.3.34" futures = "0.3.34"
gethostname = "1.1.0" gethostname = "1.1.0"
scopeguard = "1.2.0" scopeguard = "1.2.0"
tauri-plugin-notification = "2.8.0" tauri-plugin-notification = "2.7.0"
tokio-stream = "0.1.19" tokio-stream = "0.1.19"
backon = { version = "1.6.0", features = ["tokio-sleep"] } backon = { version = "1.6.0", features = ["tokio-sleep"] }
tauri-plugin-http = "2.8.3" tauri-plugin-http = "2.8.3"
tracing = { workspace = true } tracing = { workspace = true }
tauri-plugin-devtools = { version = "2.2.3", optional = true } tauri-plugin-devtools = { version = "2.2.2", optional = true }
tauri-plugin-mihomo = { git = "https://github.com/clash-verge-rev/tauri-plugin-mihomo", rev = "7a03d16f4d39b1bb885b7babe66b9e153e34b615" } tauri-plugin-mihomo = { git = "https://github.com/clash-verge-rev/tauri-plugin-mihomo", rev = "7a03d16f4d39b1bb885b7babe66b9e153e34b615" }
async-trait = "0.1.92" async-trait = "0.1.92"
clash_verge_service_ipc = { git = "https://github.com/clash-verge-rev/clash-verge-service-ipc.git", tag = "v2.7.7", version = "2.7.7", features = [ clash_verge_service_ipc = { git = "https://github.com/clash-verge-rev/clash-verge-service-ipc.git", tag = "v2.7.7", version = "2.7.7", features = [
"client", "client",
] } ] }
arc-swap = "1.9.3" arc-swap = "1.9.2"
tokio-rustls = "0.26.6" tokio-rustls = "0.26.6"
rustls = { version = "0.23.46", features = ["ring"] } rustls = { version = "0.23.46", features = ["ring"] }
webpki-roots = "1.0.11" webpki-roots = "1.0.9"
dark-light = "3.0.0" dark-light = "3.0.0"
bytes = "1.12.1" bytes = "1.12.1"
tokio-util = { version = "0.7.20", features = ["time"] } tokio-util = { version = "0.7.20", 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.26", features = ["derive", "typescript"] } 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] [target.'cfg(unix)'.dependencies]
libc = "0.2.191" libc = "0.2.191"
@@ -119,7 +119,7 @@ libc = "0.2.191"
clash-verge-signal = { workspace = true } clash-verge-signal = { workspace = true }
[target.'cfg(target_os = "macos")'.dependencies] [target.'cfg(target_os = "macos")'.dependencies]
tauri-plugin-wdio-webdriver = { version = "=1.5.1", optional = true } tauri-plugin-wdio-webdriver = { version = "=1.5.0", optional = true }
objc2 = "0.6.5" objc2 = "0.6.5"
objc2-foundation = { version = "0.3.2", features = [ objc2-foundation = { version = "0.3.2", features = [
"NSString", "NSString",
+7 -12
View File
@@ -14,7 +14,7 @@ import {
useTheme, useTheme,
Fade, Fade,
} from '@mui/material' } from '@mui/material'
import { useState, useMemo, useCallback, memo, FC } from 'react' import { useState, useMemo, memo, FC } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import ProxyControlSwitches from '@/components/shared/proxy-control-switches' import ProxyControlSwitches from '@/components/shared/proxy-control-switches'
@@ -152,15 +152,10 @@ export const ProxyTunCard: FC = () => {
showNotice.error(err) showNotice.error(err)
} }
const handleSystemTabClick = useCallback(() => { const handleTabChange = (tab: string) => {
setActiveTab('system') setActiveTab(tab)
localStorage.setItem(LOCAL_STORAGE_TAB_KEY, 'system') localStorage.setItem(LOCAL_STORAGE_TAB_KEY, tab)
}, []) }
const handleTunTabClick = useCallback(() => {
setActiveTab('tun')
localStorage.setItem(LOCAL_STORAGE_TAB_KEY, 'tun')
}, [])
const tabDescription = useMemo(() => { const tabDescription = useMemo(() => {
if (activeTab === 'system') { if (activeTab === 'system') {
@@ -196,14 +191,14 @@ export const ProxyTunCard: FC = () => {
> >
<TabButton <TabButton
isActive={activeTab === 'system'} isActive={activeTab === 'system'}
onClick={handleSystemTabClick} onClick={() => handleTabChange('system')}
icon={ComputerRounded} icon={ComputerRounded}
label={t('settings.sections.system.toggles.systemProxy')} label={t('settings.sections.system.toggles.systemProxy')}
hasIndicator={systemProxyIndicator} hasIndicator={systemProxyIndicator}
/> />
<TabButton <TabButton
isActive={activeTab === 'tun'} isActive={activeTab === 'tun'}
onClick={handleTunTabClick} onClick={() => handleTabChange('tun')}
icon={TroubleshootRounded} icon={TroubleshootRounded}
label={t('settings.sections.system.toggles.tunMode')} label={t('settings.sections.system.toggles.tunMode')}
hasIndicator={enable_tun_mode! && isTunModeAvailable} hasIndicator={enable_tun_mode! && isTunModeAvailable}
+2 -27
View File
@@ -183,7 +183,6 @@ function useProxyRenderState(
renderList, renderList,
onProxies, onProxies,
onHeadState, onHeadState,
setHeadState: setListHeadState,
handleCheckAll, handleCheckAll,
saveScrollPosition, saveScrollPosition,
getScrollPosition, getScrollPosition,
@@ -378,17 +377,12 @@ function NormalProxyGroups(props: { mode: string }) {
renderList, renderList,
onProxies, onProxies,
onHeadState, onHeadState,
setHeadState,
handleCheckAll, handleCheckAll,
getScrollPosition, getScrollPosition,
saveScrollPosition, saveScrollPosition,
} = useProxyRenderState(mode, false, null) } = useProxyRenderState(mode, false, null)
const emptyList = useEmptyRenderList() const emptyList = useEmptyRenderList()
const { onDragEnd: onHeaderDragEnd } = useProxyGroupHeaderLayout() const { onDragEnd: onHeaderDragEnd } = useProxyGroupHeaderLayout()
const { proxyView } = useProxiesData()
const globalGroupName = proxyView?.global?.name
// The revealed group sits below GLOBAL's rows, so a click must bring it into view.
const pendingRevealRef = useRef<string | null>(null)
const renderFirstRef = useRef(true) const renderFirstRef = useRef(true)
// Do not persist intermediate positions produced while restoring virtual scroll. // Do not persist intermediate positions produced while restoring virtual scroll.
const isRestoringRef = useRef(false) const isRestoringRef = useRef(false)
@@ -483,17 +477,9 @@ function NormalProxyGroups(props: { mode: string }) {
if (!['Selector', 'URLTest', 'Fallback'].includes(group.type)) return if (!['Selector', 'URLTest', 'Fallback'].includes(group.type)) return
if (!isInteractableMember(member)) return if (!isInteractableMember(member)) return
if (group.name === globalGroupName) {
pendingRevealRef.current =
member.kind === 'group' ? member.ref.name : null
// The selection refetches the view, so skip onHeadState's refetch.
if (member.kind === 'group') {
setHeadState(member.ref.name, { open: true })
}
}
select(group.name, member.ref.name, group.now!, group.fixed!) select(group.name, member.ref.name, group.now!, group.fixed!)
}, },
[globalGroupName, select, setHeadState], [select],
) )
const handleLocation = useStableCallback((group: ProxyGroupView) => { const handleLocation = useStableCallback((group: ProxyGroupView) => {
@@ -539,17 +525,6 @@ function NormalProxyGroups(props: { mode: string }) {
return Array.from(new Set(names)) return Array.from(new Set(names))
}, [renderList]) }, [renderList])
useEffect(() => {
if (mode !== 'global') {
pendingRevealRef.current = null
return
}
const groupName = pendingRevealRef.current
if (!groupName || !proxyGroupNames.includes(groupName)) return
pendingRevealRef.current = null
handleGroupLocationByName(groupName)
}, [handleGroupLocationByName, mode, proxyGroupNames])
const handleGroupToggle = useCallback( const handleGroupToggle = useCallback(
async (group: ProxyGroupView) => { async (group: ProxyGroupView) => {
const index = renderList.findIndex( const index = renderList.findIndex(
@@ -633,7 +608,7 @@ function NormalProxyGroups(props: { mode: string }) {
/> />
</DragDropProvider> </DragDropProvider>
{mode === 'rule' && ( {(mode === 'rule' || mode === 'global') && (
<ProxyGroupNavigator <ProxyGroupNavigator
proxyGroupNames={proxyGroupNames} proxyGroupNames={proxyGroupNames}
onGroupLocation={handleGroupLocationByName} onGroupLocation={handleGroupLocationByName}
+19 -35
View File
@@ -6,7 +6,6 @@ import { useVerge } from '@/hooks/use-verge'
import { useAppRefreshers, useProxiesData } from '@/providers/app-data-context' import { useAppRefreshers, useProxiesData } from '@/providers/app-data-context'
import delayManager, { type DelaySnapshot } from '@/services/delay' import delayManager, { type DelaySnapshot } from '@/services/delay'
import { import {
findCurrentGroupMember,
isInteractableMember, isInteractableMember,
resolveMember, resolveMember,
selectGlobalChainNodes, selectGlobalChainNodes,
@@ -79,18 +78,6 @@ const resolveOccurrences = (view: ProxyViewV1, group: ProxyGroupView) =>
member: resolveMember(view, member), member: resolveMember(view, member),
})) }))
// mihomo selects DIRECT in GLOBAL by default, so the selected built-in stays visible.
const HIDDEN_GLOBAL_BUILTINS = new Set(['DIRECT', 'REJECT'])
const isHiddenGlobalBuiltin = (
group: ProxyGroupView,
{ member }: ResolvedMemberOccurrence,
) =>
member.kind === 'node' &&
member.node.source.kind === 'core' &&
HIDDEN_GLOBAL_BUILTINS.has(member.node.source.proxyName) &&
member.ref.name !== group.now
const memberKey = ( const memberKey = (
group: ProxyGroupView, group: ProxyGroupView,
occurrence: ResolvedMemberOccurrence, occurrence: ResolvedMemberOccurrence,
@@ -222,10 +209,8 @@ export const useRenderList = (
const renderGroups = useMemo(() => { const renderGroups = useMemo(() => {
if (!proxyView) return [] if (!proxyView) return []
if (mode === 'rule') return proxyView.groups if (mode === 'rule') return proxyView.groups
const { global } = proxyView const global = proxyView.global ? [proxyView.global] : []
if (!global) return [] return mode === 'global' ? [...global, ...proxyView.groups] : global
const current = findCurrentGroupMember(proxyView, global)?.member
return current?.kind === 'group' ? [global, current.group] : [global]
}, [mode, proxyView]) }, [mode, proxyView])
// Every group this list draws, so a test settling in any of them re-sorts that group. // Every group this list draws, so a test settling in any of them re-sorts that group.
@@ -275,10 +260,10 @@ export const useRenderList = (
})) }))
} }
const useRule = mode === 'rule' || mode === 'global'
const cache = groupCacheRef.current const cache = groupCacheRef.current
const retList = renderGroups.flatMap((group) => { const retList = renderGroups.flatMap((group) => {
const isFlat = group === proxyView.global
const headState = headStates[group.name] || DEFAULT_STATE const headState = headStates[group.name] || DEFAULT_STATE
const cached = cache.get(group.name) const cached = cache.get(group.name)
if ( if (
@@ -294,24 +279,19 @@ export const useRenderList = (
} }
const ret: IRenderItem[] = [ const ret: IRenderItem[] = [
isFlat {
? { type: 1, key: `head-${group.name}`, group, headState } type: 0,
: { key: group.name,
type: 0, group,
key: group.name, headState,
group, icon: group.icon!,
headState, testUrl: group.testUrl!,
icon: group.icon!, },
testUrl: group.testUrl!,
},
] ]
if (headState.open || isFlat) { if (headState.open || !useRule) {
const resolved = resolveOccurrences(proxyView, group)
const occurrences = filterSort( const occurrences = filterSort(
isFlat resolveOccurrences(proxyView, group),
? resolved.filter((item) => !isHiddenGlobalBuiltin(group, item))
: resolved,
group.name, group.name,
headState.filterText, headState.filterText,
headState.sortType, headState.sortType,
@@ -322,6 +302,9 @@ export const useRenderList = (
useRegularExpression: headState.filterUseRegularExpression, useRegularExpression: headState.filterUseRegularExpression,
}, },
) )
if (!useRule) {
ret.push({ type: 1, key: `head-${group.name}`, group, headState })
}
if (occurrences.length === 0) { if (occurrences.length === 0) {
ret.push({ type: 3, key: `empty-${group.name}`, group, headState }) ret.push({ type: 3, key: `empty-${group.name}`, group, headState })
} else if (col > 1) { } else if (col > 1) {
@@ -360,8 +343,9 @@ export const useRenderList = (
return ret return ret
}) })
const filtered = const filtered = !useRule
mode === 'rule' ? retList.filter((item) => !item.group.hidden) : retList ? retList.slice(1)
: retList.filter((item) => !item.group.hidden)
if ( if (
prevListRef.current.length === filtered.length && prevListRef.current.length === filtered.length &&
filtered.every((item, index) => item === prevListRef.current[index]) filtered.every((item, index) => item === prevListRef.current[index])
+14 -4
View File
@@ -586,10 +586,20 @@ export function DnsViewer({ ref }: { ref?: Ref<DialogRef> }) {
? event.target.checked ? event.target.checked
: event.target.value : event.target.value
setValues((prev) => ({ setValues((prev) => {
...prev, const newValues = {
[field]: value, ...prev,
})) [field]: value,
}
if (visualization) {
setTimeout(() => {
updateYamlFromValues()
}, 0)
}
return newValues
})
} }
return ( return (
+4 -18
View File
@@ -7,10 +7,6 @@ import { useTranslation } from 'react-i18next'
import { BaseDialog } from '@/components/base' import { BaseDialog } from '@/components/base'
import { useVerge } from '@/hooks/use-verge' import { useVerge } from '@/hooks/use-verge'
import type {
IVergeTestItem_Deserialize,
IVergeTestItem_Serialize,
} from '@/services/bindings'
import { showNotice } from '@/services/notice-service' import { showNotice } from '@/services/notice-service'
interface Props { interface Props {
@@ -31,7 +27,7 @@ export const TestViewer = forwardRef<TestViewerRef, Props>(
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
const { verge, patchVerge } = useVerge() const { verge, patchVerge } = useVerge()
const testList = verge?.test_list ?? [] const testList = verge?.test_list ?? []
const { control, ...formIns } = useForm<IVergeTestItem_Deserialize>({ const { control, ...formIns } = useForm<IVergeTestItem>({
defaultValues: { defaultValues: {
name: '', name: '',
icon: '', icon: '',
@@ -41,16 +37,11 @@ export const TestViewer = forwardRef<TestViewerRef, Props>(
const patchTestList = async ( const patchTestList = async (
uid: string, uid: string,
patch: Partial<IVergeTestItem_Deserialize>, patch: Partial<IVergeTestItem>,
) => { ) => {
const newList = testList.map((x) => { const newList = testList.map((x) => {
if (x.uid === uid) { if (x.uid === uid) {
return { return { ...x, ...patch }
uid: x.uid,
name: patch.name === undefined ? x.name : patch.name,
icon: patch.icon === undefined ? x.icon : patch.icon,
url: patch.url === undefined ? x.url : patch.url,
}
} }
return x return x
}) })
@@ -86,12 +77,7 @@ export const TestViewer = forwardRef<TestViewerRef, Props>(
if (openType === 'new') { if (openType === 'new') {
const uid = nanoid() const uid = nanoid()
const item: IVergeTestItem_Serialize = { const item = { ...form, uid }
uid,
name: form.name ?? null,
icon: form.icon ?? null,
url: form.url ?? null,
}
const newList = [...testList, item] const newList = [...testList, item]
await patchVerge({ test_list: newList }) await patchVerge({ test_list: newList })
onChange(uid) onChange(uid)
+1 -6
View File
@@ -17,12 +17,7 @@ const PRESET_PROXY_NAMES = [
'COMPATIBLE', 'COMPATIBLE',
] ]
const identity = (previous: DelayUpdate, next: DelayUpdate): DelayUpdate => const identity = (_: DelayUpdate, next: DelayUpdate): DelayUpdate => next
previous.delay === next.delay &&
previous.elapsed === next.elapsed &&
previous.updatedAt === next.updatedAt
? previous
: next
const INITIAL_DELAY: DelayUpdate = { delay: -1, updatedAt: 0 } const INITIAL_DELAY: DelayUpdate = { delay: -1, updatedAt: 0 }
+149 -255
View File
@@ -23,10 +23,7 @@ export const commands = {
__TAURI_INVOKE<string[]>('get_network_interfaces'), __TAURI_INVOKE<string[]>('get_network_interfaces'),
getSystemHostname: () => __TAURI_INVOKE<string>('get_system_hostname'), getSystemHostname: () => __TAURI_INVOKE<string>('get_system_hostname'),
restartApp: () => __TAURI_INVOKE<null>('restart_app'), restartApp: () => __TAURI_INVOKE<null>('restart_app'),
installUpdate: ( installUpdate: (version: string, onEvent: Channel<DownloadEvent>) =>
version: string,
onEvent: Channel<DownloadEvent_Deserialize>,
) =>
__TAURI_INVOKE<InstallUpdateOutcome>('install_update', { __TAURI_INVOKE<InstallUpdateOutcome>('install_update', {
version, version,
onEvent, onEvent,
@@ -37,12 +34,11 @@ export const commands = {
upgradeClashCore: (force: boolean) => upgradeClashCore: (force: boolean) =>
__TAURI_INVOKE<CoreUpgradeReport>('upgrade_clash_core', { force }), __TAURI_INVOKE<CoreUpgradeReport>('upgrade_clash_core', { force }),
/** Returns one coherent core/service snapshot instead of independently refreshed state. */ /** Returns one coherent core/service snapshot instead of independently refreshed state. */
getRuntimeState: () => getRuntimeState: () => __TAURI_INVOKE<RunStateView>('get_runtime_state'),
__TAURI_INVOKE<RunStateView_Serialize>('get_runtime_state'),
getPendingFailures: () => getPendingFailures: () =>
__TAURI_INVOKE<PendingFailure[]>('get_pending_failures'), __TAURI_INVOKE<PendingFailure[]>('get_pending_failures'),
getSidecarFailure: () => getSidecarFailure: () =>
__TAURI_INVOKE<SidecarFailureSnapshot_Serialize>('get_sidecar_failure'), __TAURI_INVOKE<SidecarFailureSnapshot>('get_sidecar_failure'),
entryLightweightMode: () => __TAURI_INVOKE<null>('entry_lightweight_mode'), entryLightweightMode: () => __TAURI_INVOKE<null>('entry_lightweight_mode'),
installService: () => installService: () =>
__TAURI_INVOKE<ServiceInstallOutcome>('install_service'), __TAURI_INVOKE<ServiceInstallOutcome>('install_service'),
@@ -52,7 +48,7 @@ export const commands = {
repairService: () => __TAURI_INVOKE<ServiceInstallOutcome>('repair_service'), repairService: () => __TAURI_INVOKE<ServiceInstallOutcome>('repair_service'),
continueWithSidecar: () => __TAURI_INVOKE<null>('continue_with_sidecar'), continueWithSidecar: () => __TAURI_INVOKE<null>('continue_with_sidecar'),
syncRuntimeProviders: () => __TAURI_INVOKE<void>('sync_runtime_providers'), syncRuntimeProviders: () => __TAURI_INVOKE<void>('sync_runtime_providers'),
getClashInfo: () => __TAURI_INVOKE<ClashInfo_Serialize>('get_clash_info'), getClashInfo: () => __TAURI_INVOKE<ClashInfo>('get_clash_info'),
patchClashConfig: (payload: YamlMapping) => patchClashConfig: (payload: YamlMapping) =>
__TAURI_INVOKE<null>('patch_clash_config', { payload }), __TAURI_INVOKE<null>('patch_clash_config', { payload }),
patchClashMode: (payload: string) => patchClashMode: (payload: string) =>
@@ -60,11 +56,16 @@ export const commands = {
/** Reads the saved mode without depending on strict mihomo `/configs` deserialization. */ /** Reads the saved mode without depending on strict mihomo `/configs` deserialization. */
getClashMode: () => __TAURI_INVOKE<string | null>('get_clash_mode'), getClashMode: () => __TAURI_INVOKE<string | null>('get_clash_mode'),
changeClashCore: (clashCore: string) => changeClashCore: (clashCore: string) =>
__TAURI_INVOKE<CommandFailure_Serialize | null>('change_clash_core', { __TAURI_INVOKE<{
clashCore, /** 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: () => 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'), getProxyView: () => __TAURI_INVOKE<ProxyViewV1_Serialize>('get_proxy_view'),
getRuntimeYaml: () => __TAURI_INVOKE<string>('get_runtime_yaml'), getRuntimeYaml: () => __TAURI_INVOKE<string>('get_runtime_yaml'),
getRuntimeLogs: () => __TAURI_INVOKE<RuntimeLogs>('get_runtime_logs'), getRuntimeLogs: () => __TAURI_INVOKE<RuntimeLogs>('get_runtime_logs'),
@@ -72,7 +73,7 @@ export const commands = {
__TAURI_INVOKE<string>('get_runtime_proxy_chain_config', { __TAURI_INVOKE<string>('get_runtime_proxy_chain_config', {
proxyChainExitNode, proxyChainExitNode,
}), }),
updateProxyChainConfigInRuntime: (proxyChainConfig: YamlValue | null) => updateProxyChainConfigInRuntime: (proxyChainConfig: JsonValue | null) =>
__TAURI_INVOKE<ValidationOutcome>('update_proxy_chain_config_in_runtime', { __TAURI_INVOKE<ValidationOutcome>('update_proxy_chain_config_in_runtime', {
proxyChainConfig, proxyChainConfig,
}), }),
@@ -102,11 +103,18 @@ export const commands = {
takeDnsOverrideNotice: () => takeDnsOverrideNotice: () =>
__TAURI_INVOKE<boolean>('take_dns_override_notice'), __TAURI_INVOKE<boolean>('take_dns_override_notice'),
takeServiceFallbackNotice: () => takeServiceFallbackNotice: () =>
__TAURI_INVOKE<ServiceFallbackNotice | null>( __TAURI_INVOKE<
'take_service_fallback_notice', | { kind: 'unavailable' }
), | { kind: 'coreRejected'; reason: string }
| { kind: 'notAutoStarted' }
| null
>('take_service_fallback_notice'),
getCoreStartupError: () => 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: () => takeServiceRepairNotice: () =>
__TAURI_INVOKE<boolean>('take_service_repair_notice'), __TAURI_INVOKE<boolean>('take_service_repair_notice'),
takeServiceOwnerNotice: () => takeServiceOwnerNotice: () =>
@@ -132,12 +140,10 @@ export const commands = {
openDevtools: () => __TAURI_INVOKE<void>('open_devtools'), openDevtools: () => __TAURI_INVOKE<void>('open_devtools'),
exitApp: () => __TAURI_INVOKE<void>('exit_app'), exitApp: () => __TAURI_INVOKE<void>('exit_app'),
getNetworkInterfacesInfo: () => getNetworkInterfacesInfo: () =>
__TAURI_INVOKE<NetworkInterfaceView_Serialize[]>( __TAURI_INVOKE<NetworkInterfaceView[]>('get_network_interfaces_info'),
'get_network_interfaces_info',
),
getProfiles: () => __TAURI_INVOKE<IProfiles_Serialize>('get_profiles'), getProfiles: () => __TAURI_INVOKE<IProfiles_Serialize>('get_profiles'),
enhanceProfiles: () => __TAURI_INVOKE<ValidationOutcome>('enhance_profiles'), enhanceProfiles: () => __TAURI_INVOKE<ValidationOutcome>('enhance_profiles'),
patchProfilesConfig: (profiles: ProfileSelection_Deserialize) => patchProfilesConfig: (profiles: ProfileSelection) =>
__TAURI_INVOKE<ValidationOutcome>('patch_profiles_config', { profiles }), __TAURI_INVOKE<ValidationOutcome>('patch_profiles_config', { profiles }),
viewProfile: (index: string) => viewProfile: (index: string) =>
__TAURI_INVOKE<null>('view_profile', { index }), __TAURI_INVOKE<null>('view_profile', { index }),
@@ -145,12 +151,52 @@ export const commands = {
__TAURI_INVOKE<null>('patch_profile', { index, profile }), __TAURI_INVOKE<null>('patch_profile', { index, profile }),
createProfile: (item: ProfileCreate_Deserialize, fileData: string | null) => createProfile: (item: ProfileCreate_Deserialize, fileData: string | null) =>
__TAURI_INVOKE<null>('create_profile', { item, fileData }), __TAURI_INVOKE<null>('create_profile', { item, fileData }),
importProfile: (url: string, option: PrfOption_Deserialize | null) => importProfile: (
__TAURI_INVOKE<null>('import_profile', { url, option }), 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) => reorderProfile: (activeId: string, overId: string) =>
__TAURI_INVOKE<null>('reorder_profile', { activeId, overId }), __TAURI_INVOKE<null>('reorder_profile', { activeId, overId }),
updateProfile: (index: string, option: PrfOption_Deserialize | null) => updateProfile: (
__TAURI_INVOKE<null>('update_profile', { index, option }), 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) => deleteProfile: (index: string) =>
__TAURI_INVOKE<null>('delete_profile', { index }), __TAURI_INVOKE<null>('delete_profile', { index }),
readProfileFile: (index: string) => readProfileFile: (index: string) =>
@@ -178,14 +224,11 @@ export const commands = {
__TAURI_INVOKE<null>('delete_webdav_backup', { filename }), __TAURI_INVOKE<null>('delete_webdav_backup', { filename }),
restoreWebdavBackup: (filename: string) => restoreWebdavBackup: (filename: string) =>
__TAURI_INVOKE<null>('restore_webdav_backup', { filename }), __TAURI_INVOKE<null>('restore_webdav_backup', { filename }),
getUnlockItems: () => getUnlockItems: () => __TAURI_INVOKE<UnlockItem[]>('get_unlock_items'),
__TAURI_INVOKE<UnlockItem_Serialize[]>('get_unlock_items'), checkMediaUnlock: (onComplete: Channel<UnlockItem>) =>
checkMediaUnlock: (onComplete: Channel<UnlockItem_Deserialize>) => __TAURI_INVOKE<UnlockItem[]>('check_media_unlock', { onComplete }),
__TAURI_INVOKE<UnlockItem_Serialize[]>('check_media_unlock', {
onComplete,
}),
checkMediaUnlockItem: (name: string) => checkMediaUnlockItem: (name: string) =>
__TAURI_INVOKE<UnlockItem_Serialize>('check_media_unlock_item', { name }), __TAURI_INVOKE<UnlockItem>('check_media_unlock_item', { name }),
} }
/* Types */ /* Types */
@@ -194,20 +237,7 @@ export type AutoProxy = {
url: string url: string
} }
export type ClashInfo = ClashInfo_Serialize | ClashInfo_Deserialize export type ClashInfo = {
export type ClashInfo_Deserialize = {
/** clash core port */
mixed_port: number
socks_port: number
port: number
/** same as `external-controller` */
server: string
/** clash secret */
secret?: string | null
}
export type ClashInfo_Serialize = {
/** clash core port */ /** clash core port */
mixed_port: number mixed_port: number
socks_port: number socks_port: number
@@ -226,21 +256,21 @@ export type CommandFailure =
/** Structured command failure returned to the frontend. */ /** Structured command failure returned to the frontend. */
export type CommandFailure_Deserialize = { export type CommandFailure_Deserialize = {
/** Stable frontend identifier, when classified. */ /** Stable frontend identifier, when classified. */
code?: string | null code: string | null
/** Human-readable diagnostic detail. */ /** Human-readable diagnostic detail. */
detail: string detail: string
/** Requested operation when known by the command. */ /** Requested operation when known by the command. */
operation?: FailedOperation | null operation: FailedOperation | null
} }
/** Structured command failure returned to the frontend. */ /** Structured command failure returned to the frontend. */
export type CommandFailure_Serialize = { export type CommandFailure_Serialize = {
/** Stable frontend identifier, when classified. */ /** Stable frontend identifier, when classified. */
code?: string code?: string | null
/** Human-readable diagnostic detail. */ /** Human-readable diagnostic detail. */
detail: string detail: string
/** Requested operation when known by the command. */ /** Requested operation when known by the command. */
operation?: FailedOperation operation?: FailedOperation | null
} }
/** Why the Core is not running, kept until a Core starts again. */ /** Why the Core is not running, kept until a Core starts again. */
@@ -263,26 +293,7 @@ export type DnsOverrideOutcome =
| { status: 'confirmation_required'; source: string } | { status: 'confirmation_required'; source: string }
/** Same wire shape as the updater plugin's JS `DownloadEvent`. */ /** Same wire shape as the updater plugin's JS `DownloadEvent`. */
export type DownloadEvent = DownloadEvent_Serialize | DownloadEvent_Deserialize export type DownloadEvent =
/** Same wire shape as the updater plugin's JS `DownloadEvent`. */
export type DownloadEvent_Deserialize =
| {
event: 'Started'
data: {
contentLength?: number | null
}
}
| {
event: 'Progress'
data: {
chunkLength: number
}
}
| { event: 'Finished' }
/** Same wire shape as the updater plugin's JS `DownloadEvent`. */
export type DownloadEvent_Serialize =
| { | {
event: 'Started' event: 'Started'
data: { data: {
@@ -322,27 +333,14 @@ export type IProfiles_Serialize = {
/** ### `verge.yaml` schema */ /** ### `verge.yaml` schema */
export type IVerge = IVerge_Serialize | IVerge_Deserialize export type IVerge = IVerge_Serialize | IVerge_Deserialize
export type IVergeTestItem = export type IVergeTestItem = {
| IVergeTestItem_Serialize
| IVergeTestItem_Deserialize
export type IVergeTestItem_Deserialize = {
uid?: string | null uid?: string | null
name?: string | null name?: string | null
icon?: string | null icon?: string | null
url?: string | null url?: string | null
} }
export type IVergeTestItem_Serialize = { export type IVergeTheme = {
uid: string | null
name: string | null
icon: string | null
url: string | null
}
export type IVergeTheme = IVergeTheme_Serialize | IVergeTheme_Deserialize
export type IVergeTheme_Deserialize = {
primary_color?: string | null primary_color?: string | null
secondary_color?: string | null secondary_color?: string | null
primary_text?: string | null primary_text?: string | null
@@ -355,19 +353,6 @@ export type IVergeTheme_Deserialize = {
css_injection?: string | null 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 */ /** ### `verge.yaml` schema */
export type IVerge_Deserialize = { export type IVerge_Deserialize = {
/** silent | error | warn | info | debug | trace */ /** silent | error | warn | info | debug | trace */
@@ -410,7 +395,7 @@ export type IVerge_Deserialize = {
proxy_auto_config?: boolean | null proxy_auto_config?: boolean | null
pac_file_content?: string | null pac_file_content?: string | null
proxy_host?: string | null proxy_host?: string | null
theme_setting?: IVergeTheme_Deserialize | null theme_setting?: IVergeTheme | null
web_ui_list?: string[] | null web_ui_list?: string[] | null
clash_core?: string | null clash_core?: string | null
/** format: {func},{key} */ /** format: {func},{key} */
@@ -426,7 +411,7 @@ export type IVerge_Deserialize = {
auto_delay_detection_interval_minutes?: number | null auto_delay_detection_interval_minutes?: number | null
enable_builtin_enhanced?: boolean | null enable_builtin_enhanced?: boolean | null
proxy_layout_column?: number | null proxy_layout_column?: number | null
test_list?: IVergeTestItem_Deserialize[] | null test_list?: IVergeTestItem[] | null
/** 0: 不清理; 1: 1天;2: 7天; 3: 30天; 4: 90天 */ /** 0: 不清理; 1: 1天;2: 7天; 3: 30天; 4: 90天 */
auto_log_clean?: number | null auto_log_clean?: number | null
enable_auto_backup_schedule?: boolean | null enable_auto_backup_schedule?: boolean | null
@@ -475,13 +460,13 @@ export type IVerge_Serialize = {
traffic_graph: boolean | null traffic_graph: boolean | null
/** show memory info (only for Mihomo) */ /** show memory info (only for Mihomo) */
enable_memory_usage: boolean | null enable_memory_usage: boolean | null
enable_group_icon?: boolean enable_group_icon?: boolean | null
pause_render_traffic_stats_on_blur: boolean | null pause_render_traffic_stats_on_blur: boolean | null
common_tray_icon?: boolean common_tray_icon?: boolean | null
menu_icon?: string menu_icon?: string | null
menu_order?: string[] menu_order?: string[] | null
proxy_group_tools_position?: string proxy_group_tools_position?: string | null
notice_position?: string notice_position?: string | null
collapse_navbar: boolean | null collapse_navbar: boolean | null
sysproxy_tray_icon: boolean | null sysproxy_tray_icon: boolean | null
tun_tray_icon: boolean | null tun_tray_icon: boolean | null
@@ -500,11 +485,11 @@ export type IVerge_Serialize = {
proxy_auto_config: boolean | null proxy_auto_config: boolean | null
pac_file_content: string | null pac_file_content: string | null
proxy_host: string | null proxy_host: string | null
theme_setting: IVergeTheme_Serialize | null theme_setting: IVergeTheme | null
web_ui_list: string[] | null web_ui_list: string[] | null
clash_core?: string clash_core?: string | null
/** format: {func},{key} */ /** format: {func},{key} */
hotkeys?: string[] hotkeys?: string[] | null
enable_global_hotkey: boolean | null enable_global_hotkey: boolean | null
home_cards: JsonValue | null home_cards: JsonValue | null
auto_close_connection: boolean | null auto_close_connection: boolean | null
@@ -516,7 +501,7 @@ export type IVerge_Serialize = {
auto_delay_detection_interval_minutes: number | null auto_delay_detection_interval_minutes: number | null
enable_builtin_enhanced: boolean | null enable_builtin_enhanced: boolean | null
proxy_layout_column: number | null proxy_layout_column: number | null
test_list: IVergeTestItem_Serialize[] | null test_list: IVergeTestItem[] | null
/** 0: 不清理; 1: 1天;2: 7天; 3: 30天; 4: 90天 */ /** 0: 不清理; 1: 1天;2: 7天; 3: 30天; 4: 90天 */
auto_log_clean: number | null auto_log_clean: number | null
enable_auto_backup_schedule: boolean | null enable_auto_backup_schedule: boolean | null
@@ -528,10 +513,10 @@ export type IVerge_Serialize = {
verge_socks_enabled: boolean | null verge_socks_enabled: boolean | null
verge_port: number | null verge_port: number | null
verge_http_enabled: boolean | null verge_http_enabled: boolean | null
webdav_url?: string webdav_url?: string | null
webdav_username?: string webdav_username?: string | null
webdav_password?: string webdav_password?: string | null
tray_proxy_groups_display_mode?: string tray_proxy_groups_display_mode?: string | null
tray_inline_outbound_modes: boolean | null tray_inline_outbound_modes: boolean | null
enable_auto_light_weight_mode: boolean | null enable_auto_light_weight_mode: boolean | null
/** 自动进入轻量模式的延迟(分钟) */ /** 自动进入轻量模式的延迟(分钟) */
@@ -593,17 +578,7 @@ export type LocalBackupFile = {
content_length: number content_length: number
} }
export type NetworkInterfaceView = export type NetworkInterfaceView = {
| NetworkInterfaceView_Serialize
| NetworkInterfaceView_Deserialize
export type NetworkInterfaceView_Deserialize = {
name: string
addr: InterfaceAddress[]
mac_addr?: string | null
}
export type NetworkInterfaceView_Serialize = {
name: string name: string
addr: InterfaceAddress[] addr: InterfaceAddress[]
mac_addr: string | null mac_addr: string | null
@@ -662,20 +637,7 @@ export type PendingFailure = {
sequence: number sequence: number
} }
export type PlatformVergeFields = export type PlatformVergeFields = {
| PlatformVergeFields_Serialize
| PlatformVergeFields_Deserialize
export type PlatformVergeFields_Deserialize = {
tray_icon?: string | null
verge_redir_port?: number | null
verge_redir_enabled?: boolean | null
verge_tproxy_port?: number | null
verge_tproxy_enabled?: boolean | null
enable_tray_speed?: boolean | null
}
export type PlatformVergeFields_Serialize = {
tray_icon?: string | null tray_icon?: string | null
verge_redir_port?: number | null verge_redir_port?: number | null
verge_redir_enabled?: boolean | null verge_redir_enabled?: boolean | null
@@ -701,7 +663,7 @@ export type PrfItem_Deserialize = {
file?: string | null file?: string | null
desc?: string | null desc?: string | null
url?: string | null url?: string | null
selected?: PrfSelected_Deserialize[] | null selected?: PrfSelected[] | null
extra?: PrfExtra | null extra?: PrfExtra | null
updated?: number | null updated?: number | null
option?: PrfOption_Deserialize | null option?: PrfOption_Deserialize | null
@@ -714,13 +676,13 @@ export type PrfItem_Serialize = {
type: string | null type: string | null
name: string | null name: string | null
file: string | null file: string | null
desc?: string desc?: string | null
url?: string url?: string | null
selected?: PrfSelected_Serialize[] selected?: PrfSelected[] | null
extra?: PrfExtra extra?: PrfExtra | null
updated: number | null updated: number | null
option?: PrfOption_Serialize option?: PrfOption_Serialize | null
home?: string home?: string | null
} }
export type PrfOption = PrfOption_Serialize | PrfOption_Deserialize export type PrfOption = PrfOption_Serialize | PrfOption_Deserialize
@@ -746,18 +708,18 @@ export type PrfOption_Deserialize = {
} }
export type PrfOption_Serialize = { export type PrfOption_Serialize = {
user_agent?: string user_agent?: string | null
with_proxy?: boolean with_proxy?: boolean | null
self_proxy?: boolean self_proxy?: boolean | null
update_interval?: number update_interval?: number | null
/** /**
* HTTP request timeout in seconds * HTTP request timeout in seconds
* default is 60 seconds * default is 60 seconds
*/ */
timeout_seconds?: number timeout_seconds?: number | null
/** default is `false` */ /** default is `false` */
danger_accept_invalid_certs?: boolean danger_accept_invalid_certs?: boolean | null
allow_auto_update?: boolean allow_auto_update?: boolean | null
merge: string | null merge: string | null
script: string | null script: string | null
rules: string | null rules: string | null
@@ -765,18 +727,11 @@ export type PrfOption_Serialize = {
groups: string | null groups: string | null
} }
export type PrfSelected = PrfSelected_Serialize | PrfSelected_Deserialize export type PrfSelected = {
export type PrfSelected_Deserialize = {
name?: string | null name?: string | null
now?: 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 = ProfileCreate_Serialize | ProfileCreate_Deserialize
export type ProfileCreate_Deserialize = { export type ProfileCreate_Deserialize = {
@@ -806,7 +761,7 @@ export type ProfileDnsSettings_Deserialize = {
export type ProfileDnsSettings_Serialize = { export type ProfileDnsSettings_Serialize = {
enabled: boolean enabled: boolean
confirmation?: string confirmation?: string | null
} }
export type ProfilePatch = ProfilePatch_Serialize | ProfilePatch_Deserialize export type ProfilePatch = ProfilePatch_Serialize | ProfilePatch_Deserialize
@@ -817,7 +772,7 @@ export type ProfilePatch_Deserialize = {
desc?: string | null desc?: string | null
file?: string | null file?: string | null
url?: string | null url?: string | null
selected?: PrfSelected_Deserialize[] | null selected?: PrfSelected[] | null
extra?: PrfExtra | null extra?: PrfExtra | null
updated?: number | null updated?: number | null
option?: PrfOption_Deserialize | null option?: PrfOption_Deserialize | null
@@ -829,24 +784,16 @@ export type ProfilePatch_Serialize = {
desc: string | null desc: string | null
file: string | null file: string | null
url: string | null url: string | null
selected: PrfSelected_Serialize[] | null selected: PrfSelected[] | null
extra: PrfExtra | null extra: PrfExtra | null
updated: number | null updated: number | null
option: PrfOption_Serialize | null option: PrfOption_Serialize | null
} }
export type ProfileSelection = export type ProfileSelection = {
| ProfileSelection_Serialize
| ProfileSelection_Deserialize
export type ProfileSelection_Deserialize = {
current?: string | null current?: string | null
} }
export type ProfileSelection_Serialize = {
current: string | null
}
export type ProfileUpdatePayload = { export type ProfileUpdatePayload = {
uid: string uid: string
} }
@@ -866,11 +813,11 @@ export type ProxyGroupView =
export type ProxyGroupView_Deserialize = { export type ProxyGroupView_Deserialize = {
name: string name: string
type: string type: string
now?: string | null now: string | null
fixed?: string | null fixed: string | null
hidden?: boolean | null hidden: boolean | null
icon?: string | null icon: string | null
testUrl?: string | null testUrl: string | null
history: DelayHistory[] history: DelayHistory[]
members: ProxyMemberRef[] members: ProxyMemberRef[]
} & ProxyCapabilities } & ProxyCapabilities
@@ -878,11 +825,11 @@ export type ProxyGroupView_Deserialize = {
export type ProxyGroupView_Serialize = { export type ProxyGroupView_Serialize = {
name: string name: string
type: string type: string
now?: string now?: string | null
fixed?: string fixed?: string | null
hidden?: boolean hidden?: boolean | null
icon?: string icon?: string | null
testUrl?: string testUrl?: string | null
history: DelayHistory[] history: DelayHistory[]
members: ProxyMemberRef[] members: ProxyMemberRef[]
} & ProxyCapabilities } & ProxyCapabilities
@@ -908,10 +855,10 @@ export type ProxyNodeView_Deserialize = {
name: string name: string
type: string type: string
history: DelayHistory[] history: DelayHistory[]
id?: string | null id: string | null
hidden?: boolean | null hidden: boolean | null
icon?: string | null icon: string | null
testUrl?: string | null testUrl: string | null
source: ProxyNodeSource source: ProxyNodeSource
} & ProxyCapabilities } & ProxyCapabilities
@@ -920,10 +867,10 @@ export type ProxyNodeView_Serialize = {
name: string name: string
type: string type: string
history: DelayHistory[] history: DelayHistory[]
id?: string id?: string | null
hidden?: boolean hidden?: boolean | null
icon?: string icon?: string | null
testUrl?: string testUrl?: string | null
source: ProxyNodeSource source: ProxyNodeSource
} & ProxyCapabilities } & ProxyCapabilities
@@ -944,16 +891,16 @@ export type ProxyProviderView =
export type ProxyProviderView_Deserialize = { export type ProxyProviderView_Deserialize = {
name: string name: string
vehicleType: ProxyProviderVehicleType vehicleType: ProxyProviderVehicleType
updatedAt?: string | null updatedAt: string | null
subscriptionInfo?: ProxySubscriptionInfo | null subscriptionInfo: ProxySubscriptionInfo | null
proxyRecordIds: string[] proxyRecordIds: string[]
} }
export type ProxyProviderView_Serialize = { export type ProxyProviderView_Serialize = {
name: string name: string
vehicleType: ProxyProviderVehicleType vehicleType: ProxyProviderVehicleType
updatedAt?: string updatedAt?: string | null
subscriptionInfo?: ProxySubscriptionInfo subscriptionInfo?: ProxySubscriptionInfo | null
proxyRecordIds: string[] proxyRecordIds: string[]
} }
@@ -974,8 +921,8 @@ export type ProxyViewV1_Deserialize = {
schemaVersion: number schemaVersion: number
orderSource: ProxyViewOrderSource orderSource: ProxyViewOrderSource
providerState: ProxyViewProviderState providerState: ProxyViewProviderState
global?: ProxyGroupView_Deserialize | null global: ProxyGroupView_Deserialize | null
direct?: string | null direct: string | null
groups: ProxyGroupView_Deserialize[] groups: ProxyGroupView_Deserialize[]
records: { [key in string]: ProxyNodeView_Deserialize } records: { [key in string]: ProxyNodeView_Deserialize }
standalone: string[] standalone: string[]
@@ -994,22 +941,7 @@ export type ProxyViewV1_Serialize = {
providers: ProxyProviderView_Serialize[] providers: ProxyProviderView_Serialize[]
} }
export type RunStateView = RunStateView_Serialize | RunStateView_Deserialize export type RunStateView = {
export type RunStateView_Deserialize = {
mode: RunningMode
service: ServiceHealthKind
serviceUnavailableReason?: string | null
pendingAction?: PendingAction | null
sidecarAllowed: boolean
isAdmin: boolean
opInFlight: boolean
serviceUsable: boolean
tunCapable: boolean
serviceNeedsAttention: boolean
}
export type RunStateView_Serialize = {
mode: RunningMode mode: RunningMode
service: ServiceHealthKind service: ServiceHealthKind
serviceUnavailableReason: string | null serviceUnavailableReason: string | null
@@ -1048,18 +980,7 @@ export type ServiceInstallOutcome =
| { status: 'sidecar'; reason: string } | { status: 'sidecar'; reason: string }
/** A removal has a revision too, so a delayed read cannot restore a resolved failure. */ /** A removal has a revision too, so a delayed read cannot restore a resolved failure. */
export type SidecarFailureSnapshot = export type SidecarFailureSnapshot = {
| SidecarFailureSnapshot_Serialize
| SidecarFailureSnapshot_Deserialize
/** A removal has a revision too, so a delayed read cannot restore a resolved failure. */
export type SidecarFailureSnapshot_Deserialize = {
revision: number
detail?: string | null
}
/** A removal has a revision too, so a delayed read cannot restore a resolved failure. */
export type SidecarFailureSnapshot_Serialize = {
revision: number revision: number
detail: string | null detail: string | null
} }
@@ -1080,16 +1001,7 @@ export type ToggleableProxyPort = {
port: number port: number
} }
export type UnlockItem = UnlockItem_Serialize | UnlockItem_Deserialize export type UnlockItem = {
export type UnlockItem_Deserialize = {
name: string
status: string
region?: string | null
check_time?: string | null
}
export type UnlockItem_Serialize = {
name: string name: string
status: string status: string
region: string | null region: string | null
@@ -1115,11 +1027,7 @@ export type ValidationOutcome =
export type ValidationSkipReason = 'exiting' | 'debounced' export type ValidationSkipReason = 'exiting' | 'debounced'
export type VergeEventPayloads = export type VergeEventPayloads = {
| VergeEventPayloads_Serialize
| VergeEventPayloads_Deserialize
export type VergeEventPayloads_Deserialize = {
'verge://refresh-clash-config': null 'verge://refresh-clash-config': null
'verge://refresh-verge-config': null 'verge://refresh-verge-config': null
'verge://refresh-profiles': null 'verge://refresh-profiles': null
@@ -1129,21 +1037,7 @@ export type VergeEventPayloads_Deserialize = {
'verge://timer-updated': string 'verge://timer-updated': string
'profile-update-started': ProfileUpdatePayload 'profile-update-started': ProfileUpdatePayload
'profile-update-completed': ProfileUpdatePayload 'profile-update-completed': ProfileUpdatePayload
'verge://run-state-changed': RunStateView_Deserialize 'verge://run-state-changed': RunStateView
'verge://pending-failures-changed': null
}
export type VergeEventPayloads_Serialize = {
'verge://refresh-clash-config': null
'verge://refresh-verge-config': null
'verge://refresh-profiles': null
'verge://refresh-proxy-config': null
'verge://notice-message': [NoticeStatus, string]
'profile-changed': string
'verge://timer-updated': string
'profile-update-started': ProfileUpdatePayload
'profile-update-completed': ProfileUpdatePayload
'verge://run-state-changed': RunStateView_Serialize
'verge://pending-failures-changed': null 'verge://pending-failures-changed': null
} }
+1 -1
View File
@@ -19,7 +19,7 @@ import type {
export type { export type {
RunningMode, RunningMode,
RunStateView_Serialize as RunState, RunStateView as RunState,
FailedOperation, FailedOperation,
PendingFailure, PendingFailure,
SidecarFailureSnapshot, SidecarFailureSnapshot,
+1 -4
View File
@@ -1,4 +1 @@
export type { export type { NoticeStatus, VergeEventPayloads } from './bindings'
NoticeStatus,
VergeEventPayloads_Serialize as VergeEventPayloads,
} from './bindings'
+4
View File
@@ -5,12 +5,16 @@ import react from '@vitejs/plugin-react'
import { defineConfig } from 'vite' import { defineConfig } from 'vite'
import svgr from 'vite-plugin-svgr' import svgr from 'vite-plugin-svgr'
import { reactDiagnostics } from './scripts/react/vite.mts'
export default defineConfig({ export default defineConfig({
root: 'src', root: 'src',
cacheDir: path.resolve('node_modules/.vite/default'),
server: { port: 3000 }, server: { port: 3000 },
plugins: [ plugins: [
svgr(), svgr(),
react(), react(),
...reactDiagnostics(process.env.CVR_REACT_DIAGNOSTICS),
legacy({ legacy({
modernTargets: ['edge>=109', 'safari>=14'], modernTargets: ['edge>=109', 'safari>=14'],
renderLegacyChunks: false, renderLegacyChunks: false,