From 82271d8c3b69cba11bbdefc273ea383066fda37d Mon Sep 17 00:00:00 2001 From: zhom <2717306+zhom@users.noreply.github.com> Date: Wed, 26 Aug 2026 10:29:45 +0400 Subject: [PATCH] refactor: table style unification --- package.json | 3 +- src-tauri/Cargo.lock | 1400 ++++++++--------- src-tauri/Cargo.toml | 18 +- src-tauri/src/geolocation.rs | 16 +- src/app/page.tsx | 101 +- .../extension-management-dialog.tsx | 16 +- src/components/group-management-dialog.tsx | 5 +- src/components/home-header.tsx | 113 +- src/components/profile-data-table.tsx | 106 +- src/components/proxy-management-dialog.tsx | 16 +- src/i18n/locales/en.json | 46 +- src/i18n/locales/es.json | 46 +- src/i18n/locales/fr.json | 46 +- src/i18n/locales/ja.json | 46 +- src/i18n/locales/ko.json | 46 +- src/i18n/locales/pt.json | 46 +- src/i18n/locales/ru.json | 46 +- src/i18n/locales/tr.json | 46 +- src/i18n/locales/vi.json | 46 +- src/i18n/locales/zh.json | 46 +- src/lib/profile-search.test.mjs | 365 +++++ src/lib/profile-search.ts | 751 +++++++++ 22 files changed, 2518 insertions(+), 852 deletions(-) create mode 100644 src/lib/profile-search.test.mjs create mode 100644 src/lib/profile-search.ts diff --git a/package.json b/package.json index ef35b35..af05456 100644 --- a/package.json +++ b/package.json @@ -10,11 +10,12 @@ "prebuild": "pnpm licenses:generate", "build": "next build", "start": "next start", - "test": "pnpm test:themes && pnpm test:window-decorations && pnpm test:cookie-bot-limits && pnpm test:proxy-string && pnpm test:licenses && pnpm test:xray-packaging && pnpm test:rust:unit && pnpm test:sync-e2e", + "test": "pnpm test:themes && pnpm test:window-decorations && pnpm test:cookie-bot-limits && pnpm test:proxy-string && pnpm test:profile-search && pnpm test:licenses && pnpm test:xray-packaging && pnpm test:rust:unit && pnpm test:sync-e2e", "test:themes": "node --test src/lib/themes.test.mjs", "test:window-decorations": "node --test src/lib/window-decorations.test.mjs", "test:cookie-bot-limits": "node --test src/lib/cookie-bot-limits.test.mjs", "test:proxy-string": "node --test src/lib/proxy-string.test.mjs", + "test:profile-search": "node --test src/lib/profile-search.test.mjs", "test:licenses": "node --test scripts/generate-licenses.test.mjs && node scripts/generate-licenses.mjs --check", "test:xray-packaging": "node --test src-tauri/download-xray.test.mjs", "licenses:generate": "node scripts/generate-licenses.mjs", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index a82b8a4..dd7119f 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -41,9 +41,9 @@ dependencies = [ [[package]] name = "aes" -version = "0.9.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fc76eaeac4c9164506c466d4ffdd8ec9d0c5bf57ee97177c4d8eceb3a0e138" +checksum = "f8eb277bec05f56a0e0591f155a484cbd0f4f07ff2905051a48c72f004f7ed58" dependencies = [ "cipher 0.5.2", "cpubits", @@ -66,34 +66,23 @@ dependencies = [ [[package]] name = "aes-gcm" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdf011db2e21ce0d575593d749db5554b47fed37aff429e4dc50bc91ac93a028" +checksum = "7f2b8006a0c83f52b62ba44a97b58bf76fe2f70a329e588f67f89691d93d498f" dependencies = [ "aead 0.6.1", - "aes 0.9.1", + "aes 0.9.2", "cipher 0.5.2", "ctr 0.10.1", + "ctutils", "ghash 0.6.0", - "subtle", -] - -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom 0.2.17", - "once_cell", - "version_check", ] [[package]] name = "aho-corasick" -version = "1.1.4" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "c982642fa9e8606056828ee9a8505737230110bb1099153c79efe865c59d12ba" dependencies = [ "memchr", ] @@ -150,9 +139,9 @@ dependencies = [ [[package]] name = "android_system_properties" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +checksum = "ae221649c9976a6f6c56ae1facf410f3ddb33cc661c4b7b61020a912d4237fbc" dependencies = [ "libc", ] @@ -209,9 +198,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.103" +version = "1.0.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" +checksum = "330a5ed07fa54e4702c9d6c4174f74427fc0ef6e214bbd677ae50a5099946470" [[package]] name = "arbitrary" @@ -248,7 +237,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -387,7 +376,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -426,13 +415,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.89" +version = "0.1.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +checksum = "82f6aeea286b8eb4dd3431a1be1b59d290ace00f5bfd8e2a159bc2a05e2c1667" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] @@ -485,7 +474,7 @@ dependencies = [ "num-traits", "pastey", "rayon", - "thiserror 2.0.18", + "thiserror 2.0.20", "v_frame", "y4m", ] @@ -539,10 +528,10 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", - "sha1", + "sha1 0.10.7", "sync_wrapper", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.29.0", "tower", "tower-layer", "tower-service", @@ -580,6 +569,12 @@ version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +[[package]] +name = "base64" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5" + [[package]] name = "base64ct" version = "1.8.3" @@ -615,9 +610,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.13.0" +version = "2.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4388bee8683e3d04af747c73422af53102d2bd24d9eadb6cbc100baef4b43f8" +checksum = "b588b76d00fde79687d7646a9b5bdf3cc0f655e0bbd080335a95d7e96f3587da" dependencies = [ "serde_core", ] @@ -631,18 +626,6 @@ dependencies = [ "no_std_io2", ] -[[package]] -name = "bitvec" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddcec3d12c579d40898fe0a9a358a803c23e9c52ca3c425707f81c9436211837" -dependencies = [ - "funty", - "radium", - "tap", - "wyz", -] - [[package]] name = "blake2" version = "0.10.6" @@ -654,11 +637,10 @@ dependencies = [ [[package]] name = "blake3" -version = "1.8.5" +version = "1.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce" +checksum = "6d9e454fc11f76977dc803893aff6304ed33d6a26efae8696573bea74baa27ae" dependencies = [ - "arrayref", "arrayvec", "cc", "cfg-if", @@ -713,9 +695,9 @@ dependencies = [ [[package]] name = "blocking" -version = "1.6.2" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +checksum = "a70e4329df6cb94385eed412ec92375c3cdd8a6e502493d1229b6414e4036dfa" dependencies = [ "async-channel", "async-task", @@ -749,30 +731,6 @@ dependencies = [ "x25519-dalek", ] -[[package]] -name = "borsh" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f3f6da4992df95bbcd9af42a6c7dcb994498fc9048230405f3b36ff7cd3f145" -dependencies = [ - "borsh-derive", - "bytes", - "cfg_aliases", -] - -[[package]] -name = "borsh-derive" -version = "1.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae8fb4fb5740e4b2c4884ff95f5f32f5e8479db1e8fd8eb49ddbe09eb09bb7c" -dependencies = [ - "once_cell", - "proc-macro-crate 3.5.0", - "proc-macro2", - "quote", - "syn 2.0.118", -] - [[package]] name = "brotli" version = "8.0.4" @@ -805,9 +763,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.12.3" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cee35f73844aa3014bb606320a6c1f010249dbdf43342fe54b5a4f6a8ed4b79" +checksum = "6bb31b46c14244e20ee9984b11bf5c992b91fb6939fea616e3512c8baecdbe5f" dependencies = [ "memchr", "serde_core", @@ -825,18 +783,6 @@ version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" -[[package]] -name = "byte-unit" -version = "5.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a813de7f2bbedb7dce265b64f1cf5908ebe4d56281ece8d847e98113788b9b0" -dependencies = [ - "rust_decimal", - "schemars 1.2.1", - "serde", - "utf8-width", -] - [[package]] name = "byte_string" version = "1.0.0" @@ -844,33 +790,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "11aade7a05aa8c3a351cedc44c3fc45806430543382fcc4743a9b757a2a0b4ed" [[package]] -name = "bytecheck" -version = "0.6.12" +name = "bytemuck" +version = "1.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23cdc57ce23ac53c931e88a43d06d070a6fd142f2617be5855eb75efc9beb1c2" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" dependencies = [ - "bytecheck_derive", - "ptr_meta", - "simdutf8", + "bytemuck_derive", ] [[package]] -name = "bytecheck_derive" -version = "0.6.12" +name = "bytemuck_derive" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3db406d29fbcd95542e92559bed4d8ad92636d1ca8b3b72ede10b4bcc010e659" +checksum = "fc0e56a716f1e132ff6bf4bdac1c944a3fcdc1cae65f70a4a2a1ac3b401d2d1f" dependencies = [ "proc-macro2", "quote", - "syn 1.0.109", + "syn 3.0.4", ] -[[package]] -name = "bytemuck" -version = "1.25.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" - [[package]] name = "byteorder" version = "1.5.0" @@ -885,9 +823,9 @@ checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] name = "bytes" -version = "1.12.0" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ae3f5d315924270530207e2a68396c3cc547f6dca3fbdca317cfb1a51edb593" +checksum = "fc652a48c352aef3ea3aed32080501cf3ef6ed5da78602a020c991775b0aff04" dependencies = [ "serde", ] @@ -919,7 +857,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "cairo-sys-rs", "glib", "libc", @@ -940,9 +878,9 @@ dependencies = [ [[package]] name = "camino" -version = "1.2.4" +version = "1.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f2d30e4173c4026932d51d31d6b0613b1fd3014bf3f9f8943d4ba139c437ba0" +checksum = "bb1307f12aa967b5a58416e87b3653360e0fd614a016b6e970db08fecbb1b80d" dependencies = [ "serde_core", ] @@ -967,7 +905,7 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -1000,9 +938,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.66" +version = "1.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5d6cac793997bd970000024b2934968efe83b382de4fdcf4fcb46b6ee4ad996" +checksum = "0ad534f4357a5264cce5019c989cf66a4f0dc4e0d1b1d15f8aacec0ff7360273" dependencies = [ "find-msvc-tools", "jobserver", @@ -1056,9 +994,9 @@ checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "cfg_aliases" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "f079e83a288787bcd14a6aea84cee5c87a67c5a3e660c30f557a3d24761b3527" [[package]] name = "chacha20" @@ -1143,9 +1081,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.1" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +checksum = "473c7e07f409a8d772161724aa8db6a765a2532a70f9667eeb7b49d3d02fbdca" dependencies = [ "clap_builder", "clap_derive", @@ -1153,9 +1091,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.6.0" +version = "4.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +checksum = "7b48fea5a88e9ae728a2dcbedbfc0e730f7d60da42e1cb049a83c9fb8b789889" dependencies = [ "anstream", "anstyle", @@ -1165,14 +1103,14 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.6.1" +version = "4.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2ce8604710f6733aa641a2b3731eaa1e8b3d9973d5e3565da11800813f997a9" +checksum = "d012d2b9d65aca7f18f4d9878a045bc17899bba951561ba5ec3c2ba1eed9a061" dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] @@ -1210,9 +1148,9 @@ checksum = "1d07550c9036bf2ae0c684c4297d503f838287c83c53686d05370d0e139ae570" [[package]] name = "combine" -version = "4.6.7" +version = "4.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +checksum = "cfc320937d09e6de266b31b9afb480f197d7a861be86be7cb2ea7e5d1bfffc5e" dependencies = [ "bytes", "memchr", @@ -1267,9 +1205,9 @@ checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" [[package]] name = "cookie" -version = "0.18.1" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +checksum = "1a373e3602691c3cdea496d2f0ee5935151e6168fe87739483c463db1b2f2f87" dependencies = [ "time", "version_check", @@ -1307,7 +1245,7 @@ version = "0.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "core-foundation 0.10.1", "core-graphics-types", "foreign-types 0.5.0", @@ -1320,20 +1258,11 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "core-foundation 0.10.1", "libc", ] -[[package]] -name = "core_maths" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" -dependencies = [ - "libm", -] - [[package]] name = "cpubits" version = "0.1.1" @@ -1375,9 +1304,9 @@ checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" [[package]] name = "crc32fast" -version = "1.5.0" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" +checksum = "8498c871161e1742aaa9d52551b2d6ebdd4c3d45a3be423e3728f33b955be550" dependencies = [ "cfg-if", ] @@ -1464,7 +1393,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "13b588ba4ac1a99f7f2964d24b3d896ddc6bf847ee3855dbd4366f058cfcd331" dependencies = [ "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1533,7 +1462,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1556,7 +1485,7 @@ dependencies = [ "proc-macro2", "quote", "strsim", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1567,14 +1496,14 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "data-encoding" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" +checksum = "4583a4551df46e2792f82ceeac45e850d2e2d5debba0b91f102385cda5b11f06" [[package]] name = "data-url" @@ -1611,15 +1540,6 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" -[[package]] -name = "defmt" -version = "0.3.100" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0963443817029b2024136fc4dd07a5107eb8f977eaf18fcd1fdeb11306b64ad" -dependencies = [ - "defmt 1.1.1", -] - [[package]] name = "defmt" version = "1.1.1" @@ -1639,7 +1559,7 @@ dependencies = [ "defmt-parser", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1648,7 +1568,7 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10d60334b3b2e7c9d91ef8150abfb6fa4c1c39ebbcf4a81c2e346aad939fee3e" dependencies = [ - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -1688,7 +1608,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1749,7 +1669,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2", "libc", "objc2", @@ -1757,13 +1677,13 @@ dependencies = [ [[package]] name = "displaydoc" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" +checksum = "c6232dd377dcc64799954cbd3a9bb882e9cdc1308ccd87b1c098f1fb2eaf82a8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] @@ -1786,7 +1706,7 @@ checksum = "0fbbb781877580993a8707ec48672673ec7b81eeba04cfd2310bd28c08e47c8f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1817,13 +1737,13 @@ dependencies = [ name = "donutbrowser" version = "0.29.6" dependencies = [ - "aes 0.9.1", - "aes-gcm 0.11.0", + "aes 0.9.2", + "aes-gcm 0.11.1", "argon2", "async-socks5", "async-trait", "axum", - "base64 0.22.1", + "base64 0.23.1", "blake3", "boringtun", "bzip2", @@ -1855,7 +1775,7 @@ dependencies = [ "objc2", "objc2-app-kit", "once_cell", - "quick-xml", + "quick-xml 0.42.0", "rand 0.10.2", "regex-lite", "reqwest", @@ -1888,11 +1808,11 @@ dependencies = [ "tauri-plugin-single-instance", "tauri-plugin-window-state", "tempfile", - "thiserror 2.0.18", + "thiserror 2.0.20", "tokio", - "tokio-tungstenite", + "tokio-tungstenite 0.30.0", "tokio-util", - "toml 1.1.2+spec-1.1.0", + "toml 1.1.4+spec-1.1.0", "tower", "tower-http 0.7.0", "url", @@ -1980,7 +1900,7 @@ checksum = "5ac0893f103ae7bf50181323d748d505f27b6255777c28b502030bdaf8749f4c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1995,9 +1915,9 @@ dependencies = [ [[package]] name = "either" -version = "1.16.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" +checksum = "252afb9ae5eaa683babdc6a068b3f5726eb19e05070c731f9b2a23a7c3e8ed34" [[package]] name = "embed-resource" @@ -2008,7 +1928,7 @@ dependencies = [ "cc", "memchr", "rustc_version", - "toml 1.1.2+spec-1.1.0", + "toml 1.1.4+spec-1.1.0", "vswhom", "winreg 0.55.0", ] @@ -2052,7 +1972,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -2105,7 +2025,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -2137,9 +2057,9 @@ dependencies = [ [[package]] name = "error-code" -version = "3.3.2" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" +checksum = "0b5343afd4a8365a643ac588dab4cf234a190c7f6c88c9f6dd6ffe00837661b7" [[package]] name = "euclid" @@ -2152,11 +2072,10 @@ dependencies = [ [[package]] name = "event-listener" -version = "5.4.1" +version = "5.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +checksum = "5a23add41df1562121a9393cb065eab5146a1242410f23a644851e90cfd669d2" dependencies = [ - "concurrent-queue", "parking", "pin-project-lite", ] @@ -2173,14 +2092,16 @@ dependencies = [ [[package]] name = "exr" -version = "1.74.0" +version = "1.74.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4300e043a56aa2cb633c01af81ca8f699a321879a7854d3896a0ba89056363be" +checksum = "711fe42c9964295e01ee3fba3f9fe0e1d24b98886950d68efe81b1c76e21adf3" dependencies = [ "bit_field", "half", "lebe", "miniz_oxide", + "num-complex", + "pulp", "rayon-core", "smallvec", "zune-inflate", @@ -2200,9 +2121,9 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "fastrand" -version = "2.4.1" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" +checksum = "da7c62ceae207dd37ea5b845da6a0696c799f85e97da1ab5b7910be3c1c80223" [[package]] name = "fax" @@ -2256,9 +2177,9 @@ dependencies = [ [[package]] name = "find-msvc-tools" -version = "0.1.9" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +checksum = "d45db016d36b838f563236e9193d0ee6ce38f3f68b6c94e914b4929c96bbb890" [[package]] name = "fixedbitset" @@ -2300,6 +2221,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" +[[package]] +name = "font-types" +version = "0.12.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e64eb721ca85a34323425f4041adc5d82704d3782d5f8f03793bc012419dce23" +dependencies = [ + "bytemuck", +] + [[package]] name = "fontconfig-parser" version = "0.5.8" @@ -2311,16 +2241,15 @@ dependencies = [ [[package]] name = "fontdb" -version = "0.23.0" +version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" +checksum = "2660c5e9157bf76d2db1294e4a9feba604ef610819a3b591088d0d8392a3290f" dependencies = [ "fontconfig-parser", "log", "memmap2", "slotmap", "tinyvec", - "ttf-parser", ] [[package]] @@ -2344,13 +2273,13 @@ dependencies = [ [[package]] name = "foreign-types-macros" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +checksum = "ea5190182e6915eb873ddbc16e23b711b6eb1f9c00a0d0a3a91b5f6228475225" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] @@ -2374,17 +2303,11 @@ dependencies = [ "percent-encoding", ] -[[package]] -name = "funty" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" - [[package]] name = "futures" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +checksum = "9a31d2a3fbaaeb2af2368bbdd904aa8e812d3c04a1ee10d3171f52d556e5d0a3" dependencies = [ "futures-channel", "futures-core", @@ -2397,9 +2320,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +checksum = "b1f9e3d69d39e4862ffed03ed071a76f9a13ba1d9109d355b0f0aa6b15e393c4" dependencies = [ "futures-core", "futures-sink", @@ -2407,15 +2330,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "92d699e522242e69e3003b94ecc1f960f3a5e015aa7c5d7486e65ad01dd94f5e" [[package]] name = "futures-executor" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +checksum = "031b47cf1a3c6cc8bc2fc76cd437f521619387907d469316e7c0bc278f1f5432" dependencies = [ "futures-core", "futures-task", @@ -2424,9 +2347,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +checksum = "53c0fa8157de1303bfffdaa1cc2a673bfffb60102f76b0ef4441659124373fed" [[package]] name = "futures-lite" @@ -2443,32 +2366,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +checksum = "9fb9654ba8355388abeb8dcb4fc62f511300867002afc858860463bdd9fe0c44" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] name = "futures-sink" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" +checksum = "1944426bf7d03f1d14f708785e4b33efd750b36d48a157b836b3efc15ede8e1d" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "cd417de3d1d015fc3bfd2b1ea46dfc7bab72ef86f1cc7cc9c78e728b34a6d1fd" [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "0d50a92467f8ba5dd6e3ee5d4bd04d73ab2e4e1c44474a0674821dfce14b79bc" dependencies = [ "futures-channel", "futures-core", @@ -2651,7 +2574,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eecf2d5dc9b66b732b97707a0210906b1d30523eb773193ab777c0c84b3e8d5" dependencies = [ - "polyval 0.7.1", + "polyval 0.7.3", ] [[package]] @@ -2702,7 +2625,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "futures-channel", "futures-core", "futures-executor", @@ -2730,7 +2653,7 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -2745,15 +2668,15 @@ dependencies = [ [[package]] name = "glob" -version = "0.3.3" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +checksum = "e4eba85ea1d0a966a983acd07deee566e67395d2d96b6fb39e62b5a833f1eb0b" [[package]] name = "globset" -version = "0.4.18" +version = "0.4.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" +checksum = "07c34a9410465b45bd9787443bc7370f37735bad04b0f0cd57ff1a3186c98988" dependencies = [ "aho-corasick", "bstr", @@ -2822,14 +2745,14 @@ dependencies = [ "proc-macro-error", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "h2" -version = "0.4.18" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "839c0e8a181239723652be9062bb56ca5bf5f64011f73b623f6f4fc59086a228" +checksum = "ef8e5e5a340588f4452631496976cf8636d4a7ecf600239fdc27615d2530bc16" dependencies = [ "atomic-waker", "bytes", @@ -2855,6 +2778,18 @@ dependencies = [ "zerocopy", ] +[[package]] +name = "harfrust" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c03d949a14aa089bbb282f7dd76a498a7f684428e4257202efc119ec010376f9" +dependencies = [ + "bitflags 2.13.1", + "bytemuck", + "read-fonts", + "smallvec", +] + [[package]] name = "hash32" version = "0.3.1" @@ -2869,9 +2804,6 @@ name = "hashbrown" version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash", -] [[package]] name = "hashbrown" @@ -2979,9 +2911,9 @@ dependencies = [ [[package]] name = "http" -version = "1.4.2" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6970f50e31d6fc17d3fa27329444bfa74e196cf62e95052a3f6fee181dba6425" +checksum = "918d3568bebf352712bc2ef3d46a8bcf1a75b373be6539de198e9105cbbf9ce0" dependencies = [ "bytes", "itoa", @@ -2989,9 +2921,9 @@ dependencies = [ [[package]] name = "http-body" -version = "1.0.1" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +checksum = "ca2a8f2913ee65f60facd6a5905613afaa448497a0230cc41ce022d93290bc2c" dependencies = [ "bytes", "http", @@ -2999,9 +2931,9 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.3" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +checksum = "23169fe34a5fbcdd3f3862e78fb9b6fccd5f02a6dc6f732547005d45631ce71c" dependencies = [ "bytes", "futures-core", @@ -3030,18 +2962,18 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hybrid-array" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" +checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b" dependencies = [ "typenum", ] [[package]] name = "hyper" -version = "1.10.1" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55281c53a1894c864990125767da440a4e630446785086f52523b20033b74498" +checksum = "d22053281f852e11534f5198498373cbb59295120a20771d90f7ed1897490a72" dependencies = [ "atomic-waker", "bytes", @@ -3152,9 +3084,9 @@ dependencies = [ [[package]] name = "icu_collections" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" +checksum = "fa68d21081c4a05d5a901a1c62add574c77048b6a1c67be3b50ce0b60d4ca513" dependencies = [ "displaydoc", "potential_utf", @@ -3166,9 +3098,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" +checksum = "d56e28588da92eee5c3201a6eff33fabdd49b62269c8938d4ff050ce4d900deb" dependencies = [ "displaydoc", "litemap", @@ -3179,9 +3111,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" +checksum = "12f9cf5f235641ed274641dd81c3f28d870e276763d0797aeeab72317b1c646f" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -3193,16 +3125,17 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" +checksum = "1563da1ed3e0b3bf3d74c9b85917ac9c56464d2f57242270c09c9e752f8021a0" [[package]] name = "icu_properties" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" +checksum = "7e7ca276ad3145661a65914e6daf131ca5120cd3dcee8f8f3214b8875184a148" dependencies = [ + "displaydoc", "icu_collections", "icu_locale_core", "icu_properties_data", @@ -3213,15 +3146,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" +checksum = "e590f038c1464a96894fd6d10127e90a8be4509f56ff7ecef851b15cee0b7caa" [[package]] name = "icu_provider" -version = "2.2.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" +checksum = "d27bbb9d3abbefac45d55f647c9de1d44aafcd1186eb91879afef17c396c3e73" dependencies = [ "displaydoc", "icu_locale_core", @@ -3295,9 +3228,9 @@ dependencies = [ [[package]] name = "imagesize" -version = "0.14.0" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09e54e57b4c48b40f7aec75635392b12b3421fa26fe8b4332e63138ed278459c" +checksum = "65b27460c2c92b037f3f94c538ed9a3342f3fdf923606781629ccb35f82d042a" [[package]] name = "imgref" @@ -3365,7 +3298,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -3392,9 +3325,9 @@ checksum = "8e537132deb99c0eb4b752f0346b6a836200eaaa3516dd7e5514b63930a09e5d" [[package]] name = "ipnet" -version = "2.12.0" +version = "2.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" +checksum = "6a756c3fac73139e83f14c2d742155dd2b78d3ee56597b419a0579b7bdd6dd78" [[package]] name = "ipnetwork" @@ -3467,27 +3400,55 @@ dependencies = [ [[package]] name = "jiff" -version = "0.2.31" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccfe6121cbe750cf81efa362d85c0bde7ea298ec43092d3a193baca59cdbd634" +checksum = "668b7183bd07af9a4885f5c35b0cc5c83c4607a913c16b7e17291832910d2dcc" dependencies = [ - "defmt 1.1.1", + "defmt", + "jiff-core", "jiff-static", + "jiff-tzdb-platform", "log", "portable-atomic", "portable-atomic-util", "serde_core", + "windows-link 0.2.1", +] + +[[package]] +name = "jiff-core" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09" +dependencies = [ + "defmt", ] [[package]] name = "jiff-static" -version = "0.2.31" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e165e897f662d428f3cd3828a919dbe067c2d42bb1031eede74ef9d27ecdedd2" +checksum = "3a69dcb3a21cfb32ce1cd056169337ca284af0766dd766e7878819b251a49204" dependencies = [ + "jiff-core", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", +] + +[[package]] +name = "jiff-tzdb" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e" + +[[package]] +name = "jiff-tzdb-platform" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "875a5a69ac2bab1a891711cf5eccbec1ce0341ea805560dcd90b7a2e925132e8" +dependencies = [ + "jiff-tzdb", ] [[package]] @@ -3531,7 +3492,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -3546,9 +3507,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.103" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53b44bfcdb3f8d5837a46dae1ca9660a837176eee74a28b229bc626816589102" +checksum = "0e0c1080212aad755ea003d18543e8768dd432c48819efd73a7bf1e39b7a5a3a" dependencies = [ "cfg-if", "futures-util", @@ -3583,7 +3544,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "serde", "unicode-segmentation", ] @@ -3644,9 +3605,9 @@ checksum = "34b357333733e8260735ba5894eb928c02ecc69c78715f01a8019e7fa7f2db4c" [[package]] name = "libc" -version = "0.2.186" +version = "0.2.189" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" +checksum = "3eaf3ede3fee6db1a4c2ee091bf8a8b4dccdc6d17f656fb07896ee72867612f2" [[package]] name = "libdbus-sys" @@ -3685,18 +3646,18 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.18" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" +checksum = "28d0a00925a9f930d679b6789b721e3a7f9ed110f41b86d2497caa780c3a070a" dependencies = [ "libc", ] [[package]] name = "libsqlite3-sys" -version = "0.38.1" +version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6c19a05435c21ac299d71b6a9c13db3e3f47c520517d58990a462a1397a61db" +checksum = "f1d20bef17f513b9b3004532233187769cd072d790971f4e4da0e346eb6401e8" dependencies = [ "cc", "pkg-config", @@ -3711,9 +3672,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" +checksum = "47d9d19d1d6efa0109d2f65ff4c85cddd50bd572e5a00127ab10987290bcefae" [[package]] name = "lock_api" @@ -3726,12 +3687,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.33" +version = "0.4.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ceec5bc11778974d1bcb055b18002eba7f4b3518b6a0081b3af5f21666da9ad" -dependencies = [ - "value-bag", -] +checksum = "f9f8bd3e56ce4dfc153cf470fffbfa98c7620958b312ca5c3a4b8d5181fd13c6" [[package]] name = "loop9" @@ -3793,15 +3751,14 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "maxminddb" -version = "0.29.0" +version = "0.30.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e84ef32bcbf18a95548989e880db4af6fafd563463753afb4b9a149fb2782c" +checksum = "6649a1829998559f076107dfff337f1d05195eec8d01964d96e53570cc8f8d75" dependencies = [ "ipnetwork", - "log", "memchr", "serde", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -3826,9 +3783,9 @@ dependencies = [ [[package]] name = "memchr" -version = "2.8.2" +version = "2.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88904434abc2901f197fe8cc55f0445e7ded921dba5911dad2e2b39b48e663c4" +checksum = "cf8baf1c55e62ffcace7a9f06f4bd9cd3f0c4beb022d3b367256b91b87513d98" [[package]] name = "memmap2" @@ -3876,9 +3833,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" +checksum = "30d65c71f1ce40ab09135ce117d742b9f8a19ff91a41a8b57ed50bc2de59c427" dependencies = [ "libc", "wasi", @@ -3916,7 +3873,7 @@ dependencies = [ "cab", "cfb 0.10.0", "msi", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -3936,7 +3893,7 @@ dependencies = [ "once_cell", "png 0.18.1", "serde", - "thiserror 2.0.18", + "thiserror 2.0.20", "windows-sys 0.61.2", ] @@ -3963,7 +3920,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "jni-sys 0.3.1", "log", "ndk-sys", @@ -3993,7 +3950,7 @@ version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "cfg-if", "cfg_aliases", "libc", @@ -4062,6 +4019,7 @@ version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" dependencies = [ + "bytemuck", "num-traits", ] @@ -4079,14 +4037,14 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "num-integer" -version = "0.1.46" +version = "0.1.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +checksum = "7ce2d95d4b3734dc35aa2f45e1aa22cd416814592a4f9d9205e11affd5b8e10b" dependencies = [ "num-traits", ] @@ -4150,7 +4108,7 @@ dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4178,7 +4136,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2", "libc", "objc2", @@ -4199,7 +4157,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "objc2", "objc2-foundation", ] @@ -4210,7 +4168,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "objc2", "objc2-foundation", ] @@ -4221,7 +4179,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "dispatch2", "objc2", ] @@ -4232,7 +4190,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "dispatch2", "objc2", "objc2-core-foundation", @@ -4265,7 +4223,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "objc2", "objc2-core-foundation", "objc2-core-graphics", @@ -4277,7 +4235,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "objc2", "objc2-core-foundation", "objc2-core-graphics", @@ -4305,7 +4263,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2", "libc", "objc2", @@ -4328,7 +4286,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "objc2", "objc2-core-foundation", ] @@ -4350,7 +4308,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "objc2", "objc2-core-foundation", "objc2-foundation", @@ -4362,7 +4320,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2", "objc2", "objc2-cloud-kit", @@ -4393,7 +4351,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b2e5aaab980c433cf470df9d7af96a7b46a9d892d521a2cbbb2f8a4c16751e7f" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2", "objc2", "objc2-app-kit", @@ -4421,9 +4379,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "open" -version = "5.3.6" +version = "5.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d3b65c44123a56e0133d2cd06ce4361bd3ca99d41198b2f25e3c3db9b8b4a" +checksum = "ade3be4664bc1ef537ce133015f04c176b737815c2ba9fd60edf212d6e90dd55" dependencies = [ "dunce", "is-wsl", @@ -4436,7 +4394,7 @@ version = "0.10.81" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77823a27f0babb03091cb9ed9ef80af3b39dbc82f97e8fa530374b7dafd87a45" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "cfg-if", "foreign-types 0.3.2", "libc", @@ -4452,7 +4410,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4653,7 +4611,7 @@ dependencies = [ "phf_shared 0.13.1", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4697,7 +4655,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4729,9 +4687,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.33" +version = "0.3.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" +checksum = "f6b464fbc74e149a392436b17d523f769e057cb6877f6a5c4618bc6f11800548" [[package]] name = "plist" @@ -4741,7 +4699,7 @@ checksum = "7da1d65da6dd5d1e44199ac0f58712d241c0f439f80adea8924d832384087f85" dependencies = [ "base64 0.22.1", "indexmap 2.14.0", - "quick-xml", + "quick-xml 0.41.0", "serde", "time", ] @@ -4765,7 +4723,7 @@ version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "crc32fast", "fdeflate", "flate2", @@ -4820,9 +4778,9 @@ dependencies = [ [[package]] name = "polyval" -version = "0.7.1" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dfc63250416fea14f5749b90725916a6c903f599d51cb635aa7a52bfd03eede" +checksum = "f0fa31d631f2b2cb2a544d0aa321ce847a94764d701ca2becc411138b93d49cd" dependencies = [ "cpubits", "cpufeatures 0.3.0", @@ -4831,9 +4789,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.13.1" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +checksum = "05c8b63e8d9609db387f0324918f81d68fe27748f084ef092fb35954d0539a85" [[package]] name = "portable-atomic-util" @@ -4846,9 +4804,9 @@ dependencies = [ [[package]] name = "potential_utf" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" +checksum = "d83eb9bc6d8e5cf568e7a1101d60ee05e81ed50ea106026f3d18deeb046d7661" dependencies = [ "zerovec", ] @@ -4900,7 +4858,7 @@ version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" dependencies = [ - "toml_edit 0.25.12+spec-1.1.0", + "toml_edit 0.25.13+spec-1.1.0", ] [[package]] @@ -4929,9 +4887,9 @@ dependencies = [ [[package]] name = "proc-macro2" -version = "1.0.106" +version = "1.0.107" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +checksum = "985e7ec9bb745e6ce6535b544d84d6cd6f7ad8bd711c398938ae983b91a766d9" dependencies = [ "unicode-ident", ] @@ -4952,28 +4910,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4488a4a36b9a4ba6b9334a32a39971f77c1436ec82c38707bce707699cc3bbcb" dependencies = [ "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] -name = "ptr_meta" -version = "0.1.4" +name = "pulp" +version = "0.22.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0738ccf7ea06b608c10564b31debd4f5bc5e197fc8bfe088f68ae5ce81e7a4f1" +checksum = "046aa45b989642ec2e4717c8e72d677b13edd831a4d3b6cf37d9a3e54912496a" dependencies = [ - "ptr_meta_derive", + "bytemuck", + "cfg-if", + "libm", + "num-complex", + "paste", + "pulp-wasm-simd-flag", + "raw-cpuid", + "reborrow", + "version_check", ] [[package]] -name = "ptr_meta_derive" -version = "0.1.4" +name = "pulp-wasm-simd-flag" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16b845dbfca988fa33db069c0e230574d15a3088f147a87b64c7589eb662c9ac" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] +checksum = "1d8f70e07b9c3962945a74e59ca1c511bba65b6419468acc217c457d93f3c740" [[package]] name = "pxfm" @@ -5001,6 +4962,15 @@ name = "quick-xml" version = "0.41.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" +dependencies = [ + "memchr", +] + +[[package]] +name = "quick-xml" +version = "0.42.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41b1177fdf999d2321d3fb46ff47159d9c1fb9ad66a4879f8c50a0b504615e9b" dependencies = [ "memchr", "serde", @@ -5008,9 +4978,9 @@ dependencies = [ [[package]] name = "quote" -version = "1.0.46" +version = "1.0.47" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfbc457d0c7a0759a614551b11a6409e5951f6c7537be1f1b7682b9ae9230368" +checksum = "1fbf4db142a473a8d80c26bbf18454ed458bf8d26c8219c331daecfdbd079001" dependencies = [ "proc-macro2", ] @@ -5027,30 +4997,13 @@ version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" -[[package]] -name = "radium" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09" - [[package]] name = "rand" -version = "0.8.6" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" -dependencies = [ - "rand_chacha 0.9.0", + "rand_chacha", "rand_core 0.9.5", ] @@ -5065,16 +5018,6 @@ dependencies = [ "rand_core 0.10.1", ] -[[package]] -name = "rand_chacha" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", -] - [[package]] name = "rand_chacha" version = "0.9.0" @@ -5136,10 +5079,10 @@ dependencies = [ "num-traits", "paste", "profiling", - "rand 0.9.4", - "rand_chacha 0.9.0", + "rand 0.9.5", + "rand_chacha", "simd_helpers", - "thiserror 2.0.18", + "thiserror 2.0.20", "v_frame", "wasm-bindgen", ] @@ -5159,6 +5102,15 @@ dependencies = [ "rgb", ] +[[package]] +name = "raw-cpuid" +version = "11.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" +dependencies = [ + "bitflags 2.13.1", +] + [[package]] name = "raw-window-handle" version = "0.6.2" @@ -5185,13 +5137,30 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "read-fonts" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "046a7d674daf459825b32f5062056d6882db0d2f5a479fbd76ccfc870ac18709" +dependencies = [ + "bytemuck", + "font-types", + "once_cell", +] + +[[package]] +name = "reborrow" +version = "0.5.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03251193000f4bd3b042892be858ee50e8b3719f2b08e5833ac4353724632430" + [[package]] name = "redox_syscall" version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", ] [[package]] @@ -5202,34 +5171,34 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.17", "libredox", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] name = "ref-cast" -version = "1.0.25" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +checksum = "7e440fb4e4b4147295338efb76001ab9e4efc0e5839df2c47fc5ac2381d365c3" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.25" +version = "1.0.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +checksum = "92ecd8964f8453721699a1ed72037b0db49ce2f5a5138486ee89bed6f67cdf3a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] name = "regex" -version = "1.12.4" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" +checksum = "f020237b6c8eed93db2e2cb53c00c60a8e1bc73da7d073199a1180401450218d" dependencies = [ "aho-corasick", "memchr", @@ -5239,9 +5208,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "ad8553b9b26413251cbf30e620595c7a41b3887f03da04579c0e6b0d6a06b4b2" dependencies = [ "aho-corasick", "memchr", @@ -5260,15 +5229,6 @@ version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" -[[package]] -name = "rend" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fe3824f5629716b1589be05dacd749f6aa084c87e00e016714a8cdfccc997c" -dependencies = [ - "bytecheck", -] - [[package]] name = "reqwest" version = "0.13.4" @@ -5313,10 +5273,11 @@ dependencies = [ [[package]] name = "resvg" -version = "0.47.0" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9be183ad6a216aa96f33e4c8033b0988b8b3ea6fd2359d19af5bac4643fd8e81" +checksum = "67e3803f97b999e80cbf7c6ecdd07a8102204d92e1633cf48783720c521196bd" dependencies = [ + "bytemuck", "gif", "image-webp", "log", @@ -5388,35 +5349,6 @@ dependencies = [ "ring", ] -[[package]] -name = "rkyv" -version = "0.7.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2297bf9c81a3f0dc96bc9521370b88f054168c29826a75e89c55ff196e7ed6a1" -dependencies = [ - "bitvec", - "bytecheck", - "bytes", - "hashbrown 0.12.3", - "ptr_meta", - "rend", - "rkyv_derive", - "seahash", - "tinyvec", - "uuid", -] - -[[package]] -name = "rkyv_derive" -version = "0.7.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84d7b42d4b8d06048d3ac8db0eb31bcb942cbeb709f0b5f2b2ebde398d3038f5" -dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "roxmltree" version = "0.20.0" @@ -5439,16 +5371,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c51c9ae4df8a7fba42103df5c621fa3c37eccf3a3c650879e90fc48b11cc192c" dependencies = [ "hashbrown 0.16.1", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] name = "rusqlite" -version = "0.40.1" +version = "0.40.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11438310b19e3109b6446c33d1ed5e889428cf2e278407bc7896bc4aaea43323" +checksum = "23f2a97da3e3873c73cb2a2e71b35c40ff95e0b1eefa8d72d8499a6928c3b5b3" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -5467,23 +5399,6 @@ dependencies = [ "ordered-multimap", ] -[[package]] -name = "rust_decimal" -version = "1.42.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be2a24f50780bc85f09cc6ac299bdf1424302742d77221106859c9d8b102126a" -dependencies = [ - "arrayvec", - "borsh", - "bytes", - "num-traits", - "rand 0.8.6", - "rkyv", - "serde", - "serde_json", - "wasm-bindgen", -] - [[package]] name = "rustc-hash" version = "2.1.3" @@ -5505,7 +5420,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "errno", "libc", "linux-raw-sys", @@ -5514,9 +5429,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.41" +version = "0.23.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" +checksum = "0283386ce02abc0151e1761d08802dfe86c173b0b494af5cbc086574e453da06" dependencies = [ "once_cell", "rustls-pki-types", @@ -5527,18 +5442,18 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.15.0" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "764899a24af3980067ee14bc143654f297b22eaebfe3c7b6b211920a5a59b046" +checksum = "2f4925028c7eb5d1fcdaf196971378ed9d2c1c4efc7dc5d011256f76c99c0a96" dependencies = [ "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.103.13" +version = "0.103.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" +checksum = "f3c3cf1d8b1e7d4927e2d154c3fcb02979afb9939629c62cd9048d4f07b60ac2" dependencies = [ "ring", "rustls-pki-types", @@ -5551,24 +5466,6 @@ version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" -[[package]] -name = "rustybuzz" -version = "0.20.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" -dependencies = [ - "bitflags 2.13.0", - "bytemuck", - "core_maths", - "log", - "smallvec", - "ttf-parser", - "unicode-bidi-mirroring", - "unicode-ccc", - "unicode-properties", - "unicode-script", -] - [[package]] name = "ryu" version = "1.0.23" @@ -5622,9 +5519,9 @@ dependencies = [ [[package]] name = "schemars" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2b42f36aa1cd011945615b92222f6bf73c599a102a300334cd7f8dbeec726cc" +checksum = "687274d293b6cdc6e73e0fee520bf2049650090d7164f87672d212a3c530cf4a" dependencies = [ "dyn-clone", "ref-cast", @@ -5641,7 +5538,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -5650,12 +5547,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "seahash" -version = "4.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c107b6f4780854c8b126e228ea8869f4d7b71260f962fefb57b996b8959ba6b" - [[package]] name = "sealed" version = "0.6.0" @@ -5664,7 +5555,7 @@ checksum = "22f968c5ea23d555e670b449c1c5e7b2fc399fdaec1d304a17cd48e288abc107" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -5692,7 +5583,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -5715,7 +5606,7 @@ version = "0.36.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5d9c0c92a92d33f08817311cf3f2c29a3538a8240e94a6a3c622ce652d7e00c" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "cssparser", "derive_more", "log", @@ -5750,9 +5641,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +checksum = "4148590afebada386688f18773da617792bf2ef03ffc1e4cbd2b1d45b023e0ba" dependencies = [ "serde_core", "serde_derive", @@ -5772,22 +5663,22 @@ dependencies = [ [[package]] name = "serde_core" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +checksum = "67dca2c9c51e58a4791a4b1ed58308b39c64224d349a935ab5039aa360942a48" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.228" +version = "1.0.229" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +checksum = "e7a5d71263a5a7d47b41f6b3f06ba276f10cc18b0931f1799f710578e2309348" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] @@ -5798,14 +5689,14 @@ checksum = "18d26a20a969b9e3fdf2fc2d9f21eda6c40e2de84c9408bb5d3b05d499aae711" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "serde_json" -version = "1.0.150" +version = "1.0.151" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8014e44b4736ed0538adeecded0fce2a272f22dc9578a7eb6b2d9993c74cfb9" +checksum = "c841b55ecdae098c80dcae9cf767f6f8a0c2cdb3416bbef72181df4d0fe73f14" dependencies = [ "itoa", "memchr", @@ -5827,13 +5718,13 @@ dependencies = [ [[package]] name = "serde_repr" -version = "0.1.20" +version = "0.1.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" +checksum = "8d3b1629de253c70a0508c3899572da79ca359fdab27c7920ff00406df418906" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] @@ -5868,9 +5759,9 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.21.0" +version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" +checksum = "ee78f1fbe43ac4a0e47aadb3dbd357b69eb0d3793e948624cd03dd2750ab1c0a" dependencies = [ "base64 0.22.1", "bs58", @@ -5878,8 +5769,9 @@ dependencies = [ "hex", "indexmap 1.9.3", "indexmap 2.14.0", + "jiff", "schemars 0.9.0", - "schemars 1.2.1", + "schemars 1.2.2", "serde_core", "serde_json", "serde_with_macros", @@ -5888,14 +5780,14 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.21.0" +version = "3.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" +checksum = "8705578779c2b6bd90d84d66eb2e206b708b1a4d7b9f17641b293545bf1c7e46" dependencies = [ "darling", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -5913,9 +5805,9 @@ dependencies = [ [[package]] name = "serial_test" -version = "3.5.0" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "699f4197115b8a7e7ff19c9a315a4bd6fffec26cc4626ef45ecaea389e081c6d" +checksum = "a6df5ed973ad8d834e09f824f9e9f449af6b9a3745f78dec7cc752770bd3bf11" dependencies = [ "futures-executor", "futures-util", @@ -5927,13 +5819,13 @@ dependencies = [ [[package]] name = "serial_test_derive" -version = "3.5.0" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94e153fc76e1c6a068703d6d29c508a0b15c061c4b7e43da59cc097bc342673c" +checksum = "a22144e767da4ddd8416dbf383700542ffd8a5dc493dfecedfe1fe3ad03c98ae" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] @@ -5955,7 +5847,7 @@ checksum = "772ee033c0916d670af7860b6e1ef7d658a4629a6d0b4c8c3e67f09b3765b75d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -5969,15 +5861,26 @@ dependencies = [ [[package]] name = "sha1" -version = "0.10.6" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +checksum = "a978451301f4db1d02937a4ab3ccce137717b81826e79b7d49ffe3244a13c3b8" dependencies = [ "cfg-if", "cpufeatures 0.2.17", "digest 0.10.7", ] +[[package]] +name = "sha1" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", +] + [[package]] name = "sha2" version = "0.10.9" @@ -6025,7 +5928,7 @@ dependencies = [ "shadowsocks-crypto", "socket2", "spin", - "thiserror 2.0.18", + "thiserror 2.0.20", "tokio", "tokio-tfo", "trait-variant", @@ -6044,9 +5947,9 @@ dependencies = [ "chacha20poly1305", "hkdf", "md-5", - "rand 0.9.4", + "rand 0.9.5", "ring-compat", - "sha1", + "sha1 0.10.7", ] [[package]] @@ -6105,9 +6008,9 @@ checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" [[package]] name = "simd-adler32" -version = "0.3.9" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" +checksum = "3a219298ac11a56ea9a6d2120044824d6f01aeb034955e7af7bc16858527deea" [[package]] name = "simd_helpers" @@ -6118,12 +6021,6 @@ dependencies = [ "quote", ] -[[package]] -name = "simdutf8" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" - [[package]] name = "simplecss" version = "0.2.2" @@ -6139,6 +6036,16 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" +[[package]] +name = "skrifa" +version = "0.44.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "819ab7d62b1d3e72d9d9dea5650bac30424f9111364bb94928dbf5ecad1baa68" +dependencies = [ + "bytemuck", + "read-fonts", +] + [[package]] name = "slab" version = "0.4.12" @@ -6162,23 +6069,23 @@ checksum = "8ed6a63f02c8539c91a8685a86f4099661ba3da017932f6ebbea6de3f0fa7c90" [[package]] name = "smoltcp" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f73d40463bba65efc9adc6370b56df76d563cc46e2482bba58351b4afb7535e" +checksum = "b6f8b28ad56c6e35524a37dd492af5d1a47e31e1a4d175cd12f89c075f01980f" dependencies = [ "bitflags 1.3.2", "byteorder", "cfg-if", - "defmt 0.3.100", + "defmt", "heapless", "managed", ] [[package]] name = "socket2" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" +checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4" dependencies = [ "libc", "windows-sys 0.61.2", @@ -6234,9 +6141,9 @@ dependencies = [ [[package]] name = "spin" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5fe4ccb98d9c292d56fec89a5e07da7fc4cf0dc11e156b41793132775d3e591" +checksum = "023a211cb3138dbc438680b32560ad89f699977624c9f8dbb95a47d5b4c07dd3" dependencies = [ "lock_api", ] @@ -6326,9 +6233,9 @@ dependencies = [ [[package]] name = "swift-rs" -version = "1.0.7" +version = "1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4057c98e2e852d51fdcfca832aac7b571f6b351ad159f9eda5db1655f8d0c4d7" +checksum = "e45c444e496845d3f2a351146bff59aae4975b2280238df1dfaa0c7d1846f38e" dependencies = [ "base64 0.21.7", "serde", @@ -6340,6 +6247,16 @@ name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" +dependencies = [ + "proc-macro2", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.119" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" dependencies = [ "proc-macro2", "quote", @@ -6348,9 +6265,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.118" +version = "3.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +checksum = "e6275cddf4610d1775e6d1fe9469b2e77d0f39fd98fb7450901b821e0c53649f" dependencies = [ "proc-macro2", "quote", @@ -6374,7 +6291,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -6388,9 +6305,9 @@ dependencies = [ [[package]] name = "sysinfo" -version = "0.39.5" +version = "0.39.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c8bd2130a9b60bee2581bf82cfe89ee836424d1f37dcfa4ce21509611684673" +checksum = "d2071df9448915b71c4fe6d25deaf1c22f12bd234f01540b77312bb8e41361e6" dependencies = [ "libc", "memchr", @@ -6407,7 +6324,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -6441,7 +6358,7 @@ version = "0.35.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d1c93047acf68669466a34690ac58cca7010bd1b201e1ec86f1fd0a75d3dd4a9" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2", "core-foundation 0.10.1", "core-graphics", @@ -6477,21 +6394,15 @@ dependencies = [ [[package]] name = "tao-macros" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f4e16beb8b2ac17db28eab8bca40e62dbfbb34c0fcdc6d9826b11b7b5d047dfd" +checksum = "5f7eeb6d99155545da6150a1795945f16ac9c178deb2a5f2e74d776107bd5849" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] -[[package]] -name = "tap" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369" - [[package]] name = "tar" version = "0.4.46" @@ -6551,7 +6462,7 @@ dependencies = [ "tauri-runtime", "tauri-runtime-wry", "tauri-utils", - "thiserror 2.0.18", + "thiserror 2.0.20", "tokio", "tray-icon", "url", @@ -6600,9 +6511,9 @@ dependencies = [ "serde", "serde_json", "sha2 0.10.9", - "syn 2.0.118", + "syn 2.0.119", "tauri-utils", - "thiserror 2.0.18", + "thiserror 2.0.20", "time", "url", "uuid", @@ -6618,7 +6529,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", "tauri-codegen", "tauri-utils", ] @@ -6651,7 +6562,7 @@ dependencies = [ "serde_json", "tauri", "tauri-plugin", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -6668,7 +6579,7 @@ dependencies = [ "tauri", "tauri-plugin", "tauri-utils", - "thiserror 2.0.18", + "thiserror 2.0.20", "tracing", "url", "windows-registry 0.5.3", @@ -6677,9 +6588,9 @@ dependencies = [ [[package]] name = "tauri-plugin-dialog" -version = "2.7.1" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65981abb771e74e571a38196c3baa11c459379164791eba0e67abc1a5fac9884" +checksum = "b2d3c1dbe38037e7f590cdf2492594d5ceebe031e7bc7e827509b22a999d2940" dependencies = [ "log", "raw-window-handle", @@ -6689,7 +6600,7 @@ dependencies = [ "tauri", "tauri-plugin", "tauri-plugin-fs", - "thiserror 2.0.18", + "thiserror 2.0.20", "url", ] @@ -6712,19 +6623,18 @@ dependencies = [ "tauri", "tauri-plugin", "tauri-utils", - "thiserror 2.0.18", - "toml 1.1.2+spec-1.1.0", + "thiserror 2.0.20", + "toml 1.1.4+spec-1.1.0", "url", ] [[package]] name = "tauri-plugin-log" -version = "2.8.0" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7545bd67f070a4500432c826e2e0682146a1d6712aee22a2786490156b574d93" +checksum = "6792296e6f389268016c77db21ebae1fc0568f2fccf88b1ec7e2ea71330afb4c" dependencies = [ "android_logger", - "byte-unit", "fern", "log", "objc2", @@ -6735,7 +6645,7 @@ dependencies = [ "swift-rs", "tauri", "tauri-plugin", - "thiserror 2.0.18", + "thiserror 2.0.20", "time", ] @@ -6751,7 +6661,7 @@ dependencies = [ "serde", "tauri", "tauri-plugin", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -6770,7 +6680,7 @@ dependencies = [ "serde_json", "tauri", "tauri-plugin", - "thiserror 2.0.18", + "thiserror 2.0.20", "url", "windows 0.61.3", "zbus", @@ -6793,20 +6703,21 @@ dependencies = [ "shared_child", "tauri", "tauri-plugin", - "thiserror 2.0.18", + "thiserror 2.0.20", "tokio", ] [[package]] name = "tauri-plugin-single-instance" -version = "2.4.2" +version = "2.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c8f29386f5e9fdc699182388a33ee80a56de436d91b67459e86afef426282af" +checksum = "b3214becf9ef5783c0ae99a3bb25adf5353a7a16ebf53e74b909e29205735c6c" dependencies = [ "serde", "serde_json", "tauri", - "thiserror 2.0.18", + "thiserror 2.0.20", + "tokio", "tracing", "windows-sys 0.60.2", "zbus", @@ -6818,13 +6729,13 @@ version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73736611e14142408d15353e21e3cca2f12a3cfb523ad0ce85999b6d2ef1a704" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "log", "serde", "serde_json", "tauri", "tauri-plugin", - "thiserror 2.0.18", + "thiserror 2.0.20", ] [[package]] @@ -6845,7 +6756,7 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "thiserror 2.0.18", + "thiserror 2.0.20", "url", "webkit2gtk", "webview2-com", @@ -6908,8 +6819,8 @@ dependencies = [ "serde_json", "serde_with", "swift-rs", - "thiserror 2.0.18", - "toml 1.1.2+spec-1.1.0", + "thiserror 2.0.20", + "toml 1.1.4+spec-1.1.0", "url", "urlpattern", "uuid", @@ -6924,7 +6835,7 @@ checksum = "cc65d45c68858bfe420dd29e834b5d15dbecf8a07a8a16cf4d532c7b1f69d4b6" dependencies = [ "dunce", "embed-resource", - "toml 1.1.2+spec-1.1.0", + "toml 1.1.4+spec-1.1.0", ] [[package]] @@ -6960,11 +6871,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "ec86235f5fcc2a73650310756d2ac5b138a5780bbbdfae3eeccec992c435ba4f" dependencies = [ - "thiserror-impl 2.0.18", + "thiserror-impl 2.0.20", ] [[package]] @@ -6975,18 +6886,18 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "thiserror-impl" -version = "2.0.18" +version = "2.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "bc04cd3e1236dd4a98afca4569f2deb3f120e5422a4023be2cb683f8486292af" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] @@ -7005,9 +6916,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.53" +version = "0.3.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" +checksum = "cdb87b95ec50ddfa440816d227a17b2ccbdda963a316a727fda0fc4334f7d134" dependencies = [ "deranged", "libc", @@ -7027,9 +6938,9 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109" [[package]] name = "time-macros" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c431b87111666e491a90baa837f914fb45cd5dc3c268591b0220ff5057f2085f" +checksum = "7e689342a48d2ea927c87ea50cabf8594854bf940e9310208848d680d668ed85" dependencies = [ "num-conv", "time-core", @@ -7072,9 +6983,9 @@ dependencies = [ [[package]] name = "tinystr" -version = "0.8.3" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" +checksum = "b1e27c91459209c2986af3dcf603a5a74a4368754ce37414f59acc971167f643" dependencies = [ "displaydoc", "zerovec", @@ -7082,9 +6993,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" +checksum = "bb4ebadaa0af04fab11ae01eb5f9fdb5f9c5b875506e210e71c07873528baa7f" dependencies = [ "tinyvec_macros", ] @@ -7097,9 +7008,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.53.0" +version = "1.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d988bcd52dbe076d3d46903332f58c912b87a2c49b1428419a5845154762ffee" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" dependencies = [ "bytes", "libc", @@ -7115,13 +7026,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.7.0" +version = "2.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +checksum = "78773a2a397f451582ce068015985c33193cf6dea8b74d2a639fe457b2f07b0e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] @@ -7166,24 +7077,37 @@ name = "tokio-tungstenite" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f72a05e828585856dacd553fba484c242c46e391fb0e58917c942ee9202915c" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite 0.29.0", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17a073bfed563fa236697a068031408a93cd9522e08abf9933ead3e73411bd71" dependencies = [ "futures-util", "log", "native-tls", "tokio", "tokio-native-tls", - "tungstenite", + "tungstenite 0.30.0", ] [[package]] name = "tokio-util" -version = "0.7.18" +version = "0.7.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" +checksum = "494815d09bf52b5548659851081238f0ca39ff638363907596da739561c62c52" dependencies = [ "bytes", "futures-core", "futures-sink", + "libc", "pin-project-lite", "tokio", ] @@ -7217,9 +7141,9 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.2+spec-1.1.0" +version = "1.1.4+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +checksum = "3aace63f4bbcdfc2c965b059de67119c89c4017a70d633be6c104910f67056f5" dependencies = [ "indexmap 2.14.0", "serde_core", @@ -7227,7 +7151,7 @@ dependencies = [ "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] @@ -7283,30 +7207,30 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.25.12+spec-1.1.0" +version = "0.25.13+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2153edc6955a6c354fad8f5efd38b6a8769bdccf9fe50f8e1329f81b0baa5d7" +checksum = "6975367e4d2ef766d86af01ffad14b622fecc8d4357a998fbc4deb6e9bacaf9b" dependencies = [ "indexmap 2.14.0", "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] name = "toml_parser" -version = "1.1.2+spec-1.1.0" +version = "1.1.3+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" +checksum = "1d38ac1cf9b95face32296c0a3ede1fdc270627c9d9c02a7274dd6d960dc4d56" dependencies = [ - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] name = "toml_writer" -version = "1.1.1+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" +checksum = "7d56353a2a665ad0f41a421187180aab746c8c325620617ad883a99a1cbe66d2" [[package]] name = "tower" @@ -7330,7 +7254,7 @@ version = "0.6.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "bytes", "futures-util", "http", @@ -7348,7 +7272,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b11f75e912b0c2be01b63d8cf8057b8c3f97cf34abb3d431a3a4c8675498e233" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "bytes", "futures-core", "futures-util", @@ -7400,7 +7324,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -7414,20 +7338,20 @@ dependencies = [ [[package]] name = "trait-variant" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70977707304198400eb4835a78f6a9f928bf41bba420deb8fdb175cd965d77a7" +checksum = "b19a4867a870f6edc4c283f2b455804b1879c0baf0e642f26b03ed8ee262d9d3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "tray-icon" -version = "0.24.1" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65ba1e5f6b9ef9fd87e21b9c6f351554dbd717960089168fcfdef854686961dc" +checksum = "045979e3f037cd18ad1cb2a419dfda133c5c29c9f3453370079f2255d46c257e" dependencies = [ "crossbeam-channel", "dirs", @@ -7441,7 +7365,7 @@ dependencies = [ "once_cell", "png 0.18.1", "serde", - "thiserror 2.0.18", + "thiserror 2.0.20", "windows-sys 0.61.2", ] @@ -7462,20 +7386,27 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "ttf-parser" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" -dependencies = [ - "core_maths", -] - [[package]] name = "tungstenite" version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.5", + "sha1 0.10.7", + "thiserror 2.0.20", +] + +[[package]] +name = "tungstenite" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e48ac77174b19c110a50ab2128b24215ac9cb40e0e12e093fb602d175c569d22" dependencies = [ "bytes", "data-encoding", @@ -7483,9 +7414,9 @@ dependencies = [ "httparse", "log", "native-tls", - "rand 0.9.4", - "sha1", - "thiserror 2.0.18", + "rand 0.10.2", + "sha1 0.11.0", + "thiserror 2.0.20", ] [[package]] @@ -7570,30 +7501,12 @@ version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" -[[package]] -name = "unicode-bidi-mirroring" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfa6e8c60bb66d49db113e0125ee8711b7647b5579dc7f5f19c42357ed039fe" - -[[package]] -name = "unicode-ccc" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce61d488bcdc9bc8b5d1772c404828b17fc481c0a582b5581e95fb233aef503e" - [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" -[[package]] -name = "unicode-properties" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" - [[package]] name = "unicode-script" version = "0.5.8" @@ -7677,38 +7590,32 @@ dependencies = [ [[package]] name = "usvg" -version = "0.47.0" +version = "0.48.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d46cf96c5f498d36b7a9693bc6a7075c0bb9303189d61b2249b0dc3d309c07de" +checksum = "977d0a4abdef933f424a99fe09f95576e089b90aebc6f016a3bc813762493e91" dependencies = [ - "base64 0.22.1", + "base64 0.23.1", "data-url", "flate2", "fontdb", + "harfrust", "imagesize", "kurbo", "log", "pico-args", "roxmltree 0.21.1", - "rustybuzz", "simplecss", "siphasher", + "skrifa", "strict-num", "svgtypes", "tiny-skia-path", - "ttf-parser", "unicode-bidi", "unicode-script", "unicode-vo", "xmlwriter", ] -[[package]] -name = "utf8-width" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "159a7cadce548703edd50d24069bc294c5415ecab0a480e0cd1ca06d112dc94a" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -7755,14 +7662,14 @@ dependencies = [ "proc-macro2", "quote", "regex", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "uuid" -version = "1.24.0" +version = "1.25.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" +checksum = "f053576934f05a761a402421fbbe3d425d9366f75f978806a037b3ca481abecc" dependencies = [ "getrandom 0.4.3", "js-sys", @@ -7781,12 +7688,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "value-bag" -version = "1.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ba6f5989077681266825251a52748b8c1d8a4ad098cc37e440103d0ea717fc0" - [[package]] name = "vcpkg" version = "0.2.15" @@ -7861,9 +7762,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b067c0c11094aef6b7a801c1e34a26affafdf3d051dba08456b868789aaf9a4" +checksum = "1b70935747edd64d89de3efa29d73789b806c15798f8e7dca4d8ac356b50ce70" dependencies = [ "cfg-if", "once_cell", @@ -7874,9 +7775,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.76" +version = "0.4.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c62df1340f32221cb9c54d6a27b030e3dba64361d4a95bed55f9aacb44da291d" +checksum = "6b7777d5cc23d0e91404e53ce2d5e8ec7acae3026b16233dba62cd3246457950" dependencies = [ "js-sys", "wasm-bindgen", @@ -7884,9 +7785,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "167ce5e579f6bcf889c4f7175a8a5a585de84e8ff93976ce393efa5f2837aab1" +checksum = "77775f8f3f7217702089053b94958f8f54061a3f663417df76e19cbdcca29bc1" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -7894,22 +7795,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3997c7839262f4ef12cf90b818d6340c18e80f263f1a94bf157d0ec4420380e" +checksum = "e11d33f857dc2fb11b8bc75aee111aa9cbeb12cd9f25efd3d4c2a3dd4e235284" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.126" +version = "0.2.127" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc1b4cb0cc549fcf58d7dfc081778139b3d283a081644e833e84682ad71cea24" +checksum = "7ef64dbcc55df09c7e5a46182d181c2cfa3e925f3da937ea764728b4bbb9dcbf" dependencies = [ "unicode-ident", ] @@ -7929,9 +7830,9 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.15" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d" +checksum = "38a91b4eaddff87b1cd1074985e3713da4af2c49742d1b356b2c01670a67a078" dependencies = [ "cc", "downcast-rs", @@ -7942,11 +7843,11 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.14" +version = "0.31.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" +checksum = "e3c36a0f861ad76d0901f2800b46321410d9f73f2ea88aac0650d86c32688073" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "rustix", "wayland-backend", "wayland-scanner", @@ -7958,7 +7859,7 @@ version = "0.32.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "23d0c813de3daa2ed6520af85a3bd49b0e722a3078506899aa9686fea58dc4b6" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "wayland-backend", "wayland-client", "wayland-scanner", @@ -7970,7 +7871,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -7984,7 +7885,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "338e30461b3a2b67d70eb30a6d89f8e0c93a833e07d2ae89085cd070c4a00ac0" dependencies = [ "proc-macro2", - "quick-xml", + "quick-xml 0.41.0", "quote", ] @@ -7999,9 +7900,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.103" +version = "0.3.104" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8622dcb61c0bcc9fffa6938bed81210af2da9a7e4a1a834b2e37a59b6dfb6141" +checksum = "c435338968042f4f59a557f690a253676d47ce13ceb55d70100e7facf6620a30" dependencies = [ "js-sys", "wasm-bindgen", @@ -8009,9 +7910,9 @@ dependencies = [ [[package]] name = "web_atoms" -version = "0.2.5" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "075474b12bcb3d2e3d4546580e9de478eeeead668a1761e2a8860c836b7ef297" +checksum = "ba8b815c1b593dc0baf78dd0f4fc8fdb2de53198fb1163738093e9a311c33fb3" dependencies = [ "phf 0.13.1", "phf_codegen", @@ -8085,7 +7986,7 @@ checksum = "67a921c1b6914c367b2b823cd4cde6f96beec77d30a939c8199bb377cf9b9b54" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -8094,7 +7995,7 @@ version = "0.38.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c" dependencies = [ - "thiserror 2.0.18", + "thiserror 2.0.20", "windows 0.61.3", "windows-core 0.61.2", ] @@ -8250,7 +8151,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -8261,7 +8162,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -8629,9 +8530,9 @@ checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" [[package]] name = "winnow" -version = "1.0.3" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" +checksum = "23b97319f7b8343df12cc98938e5c3eb436064524c8d2b4e30a1d3a36eecdf81" dependencies = [ "memchr", ] @@ -8695,7 +8596,7 @@ dependencies = [ "log", "os_pipe", "rustix", - "thiserror 2.0.18", + "thiserror 2.0.20", "tree_magic_mini", "wayland-backend", "wayland-client", @@ -8705,9 +8606,9 @@ dependencies = [ [[package]] name = "writeable" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" +checksum = "3ad82d2a33cdc9674dc7465672f271e096168fcdbe0f799d9e6db8c5892679dc" [[package]] name = "wry" @@ -8742,7 +8643,7 @@ dependencies = [ "sha2 0.10.9", "soup3", "tao-macros", - "thiserror 2.0.18", + "thiserror 2.0.20", "url", "webkit2gtk", "webkit2gtk-sys", @@ -8753,15 +8654,6 @@ dependencies = [ "x11-dl", ] -[[package]] -name = "wyz" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed" -dependencies = [ - "tap", -] - [[package]] name = "x11" version = "2.21.0" @@ -8853,15 +8745,15 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", "synstructure", ] [[package]] name = "zbus" -version = "5.17.0" +version = "5.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28b97f866896a4be7aefd2b5a8e01bb6773d19a775d54ab28b4d094b9a4480e" +checksum = "5db4be7c075cb421e4b7ee645541604239bd243ba7c357511f4ff3a74b555907" dependencies = [ "async-broadcast", "async-executor", @@ -8887,7 +8779,7 @@ dependencies = [ "uds_windows", "uuid", "windows-sys 0.61.2", - "winnow 1.0.3", + "winnow 1.0.4", "zbus_macros", "zbus_names", "zvariant", @@ -8895,14 +8787,14 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.17.0" +version = "5.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e05ad887425eecf5e8384dc2406a4a9313eb73468712fc1cdea362eb4fe0469" +checksum = "2990635d09ade6df1868f72f8cac69a876a90981e8bd3c40b1be413f8dc88f40" dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", "zbus_names", "zvariant", "zvariant_utils", @@ -8910,33 +8802,42 @@ dependencies = [ [[package]] name = "zbus_names" -version = "4.3.3" +version = "4.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1039ca249fee9559680f3a9f05b55e0761fee51af4f6c1e7d8c1f31e549721d2" +checksum = "d8bf88b4a3ff53e883001e0e0115b297a9d53c31b9c1edd2bfdd853e3428624e" dependencies = [ "serde", - "winnow 1.0.3", + "winnow 1.0.4", "zvariant", ] [[package]] -name = "zerocopy" -version = "0.8.53" +name = "zcheapstr" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75726053136156d419e285b9b7eddaaea9e3fea6ce32eed44a89901f0bd98de1" +checksum = "d1afec51604565183aeb5c54c20aeab286120d4e4460f7f76e3e8bb8c0d99473" +dependencies = [ + "serde", +] + +[[package]] +name = "zerocopy" +version = "0.8.56" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "556764e583adb45a9f8d413c2a147fa7e8d821e48e12b14fd560b607998b75eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.53" +version = "0.8.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4714fd92cf900833d49538023a9b3915155210801d1c1169eba513b2addefd71" +checksum = "f2ab42fc20575779bd240faa45f94a74256f755c0fa9e89f0ede20d91d0cdfc1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -8956,7 +8857,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", "synstructure", ] @@ -8977,14 +8878,14 @@ checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "zerotrie" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" +checksum = "4ea269c3bd32f0a32c321907a2ae912ba6f4649bb0fc764a15627e99a7095a3f" dependencies = [ "displaydoc", "yoke", @@ -8993,9 +8894,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.6" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" +checksum = "bb0464e17806c1d976d5cba29399c7f08e516e279e2ba493f63123b5fca67dd8" dependencies = [ "yoke", "zerofrom", @@ -9004,13 +8905,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.3" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" +checksum = "34df6fc39dbd26ddc9c10e6a2984476e13acce22e64e4487636ef494369225da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", ] [[package]] @@ -9028,15 +8929,15 @@ dependencies = [ [[package]] name = "zmij" -version = "1.0.21" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" +checksum = "29666d0abbfad1e3dc4dcf6144730dd3a3ab225bbbdac83319345b1b44ccfc1b" [[package]] name = "zune-core" -version = "0.5.1" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb8a0807f7c01457d0379ba880ba6322660448ddebc890ce29bb64da71fb40f9" +checksum = "d56377fd46368984a170bc5aac5567e52ca5da874caa60bea39fcbca78fb658b" [[package]] name = "zune-inflate" @@ -9058,40 +8959,41 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.13.0" +version = "5.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cf057bb00bf5c9ad77abb6147b0ca4818236a1858416e9d988e40d6322fefa7" +checksum = "c1d34c27cc6cdd1f458427519dd6b8612f7b7e3f7b9a0b2355d041dda9869147" dependencies = [ "endi", "enumflags2", "serde", - "winnow 1.0.3", + "winnow 1.0.4", + "zcheapstr", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.13.0" +version = "5.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8118ca6bda77bfc0ab51d660db0c955f2505eef854c9a449435bccb616933b31" +checksum = "864155e69b4352db0c7f374917bf45d1e0c8d17659c8b3dbf9795f3673f8c497" dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.4", "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "3.5.0" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90cb9383f9b45290407a1258b202d3f8f01db719eb60b4e4055c6375af4fc7c7" +checksum = "bad0294361a320b694a328460dc73add56c306150f5cb6bfafc44446120008a3" dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.118", - "winnow 1.0.3", + "syn 3.0.4", + "winnow 1.0.4", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index edf986b..1d4b72d 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -26,7 +26,7 @@ path = "src/bin/proxy_server.rs" [build-dependencies] tauri-build = { version = "2", features = [] } -resvg = "0.47" +resvg = "0.48" [dependencies] serde_json = "1" @@ -51,7 +51,11 @@ tokio = { version = "1", features = ["full", "sync"] } tokio-util = "0.7" sysinfo = "0.39" lazy_static = "1.5" -base64 = "0.22" +# 0.23 turns on `simd-unsafe` by default, decoding via hand-written unsafe +# AVX2/NEON. This crate decodes attacker-influenced input (proxy CONNECT auth, +# extension payloads, os_crypt key blobs), and none of those paths are hot +# enough to be worth it, so stay on the scalar engine. +base64 = { version = "0.23", default-features = false, features = ["std"] } libc = "0.2" async-trait = "0.1" futures-util = "0.3" @@ -93,19 +97,19 @@ async-socks5 = "0.6" # Wayfern CDP integration -tokio-tungstenite = { version = "0.29", features = ["native-tls"] } +tokio-tungstenite = { version = "0.30", features = ["native-tls"] } rusqlite = { version = "0.40", features = ["bundled"] } serde_yaml = "0.9" toml = "1.1" thiserror = "2.0" regex-lite = "0.1" tempfile = "3" -maxminddb = "0.29" -quick-xml = { version = "0.41", features = ["serialize"] } +maxminddb = "0.30" +quick-xml = { version = "0.42", features = ["serialize"] } # VPN support boringtun = "0.7" -smoltcp = { version = "0.13", default-features = false, features = ["std", "medium-ip", "proto-ipv4", "proto-ipv6", "socket-tcp", "socket-udp", "socket-dns"] } +smoltcp = { version = "0.14", default-features = false, features = ["std", "medium-ip", "proto-ipv4", "proto-ipv6", "socket-tcp", "socket-udp", "socket-dns"] } # Tray icon decoding (main-process system tray) image = "0.25" @@ -158,7 +162,7 @@ http-body-util = "0.1" tower = "0.5" tower-http = { version = "0.7", features = ["fs", "trace"] } futures-util = "0.3" -serial_test = "3" +serial_test = "4" # Integration test configuration [[test]] diff --git a/src-tauri/src/geolocation.rs b/src-tauri/src/geolocation.rs index 64871cb..14bd2bb 100644 --- a/src-tauri/src/geolocation.rs +++ b/src-tauri/src/geolocation.rs @@ -112,7 +112,7 @@ impl LocaleSelector { match reader.read_event_into(&mut buf) { Ok(Event::Start(ref e)) | Ok(Event::Empty(ref e)) => { let name = e.name(); - let name_str = std::str::from_utf8(name.as_ref()).unwrap_or(""); + let name_str = name.as_ref(); if name_str == "territory" { if let Some(code) = current_territory.take() { @@ -122,8 +122,8 @@ impl LocaleSelector { } for attr in e.attributes().flatten() { - if attr.key.as_ref() == b"type" { - current_territory = Some(String::from_utf8_lossy(&attr.value).to_uppercase()); + if attr.key.as_ref() == "type" { + current_territory = Some(attr.value.to_uppercase()); } } } else if name_str == "languagePopulation" && current_territory.is_some() { @@ -132,11 +132,11 @@ impl LocaleSelector { for attr in e.attributes().flatten() { match attr.key.as_ref() { - b"type" => { - lang_type = Some(String::from_utf8_lossy(&attr.value).to_string()); + "type" => { + lang_type = Some(attr.value.to_string()); } - b"populationPercent" => { - pop_percent = String::from_utf8_lossy(&attr.value).parse().unwrap_or(0.0); + "populationPercent" => { + pop_percent = attr.value.parse().unwrap_or(0.0); } _ => {} } @@ -152,7 +152,7 @@ impl LocaleSelector { } Ok(Event::End(ref e)) => { let name_ref = e.name(); - let name = std::str::from_utf8(name_ref.as_ref()).unwrap_or(""); + let name = name_ref.as_ref(); if name == "territory" { if let Some(code) = current_territory.take() { if !current_languages.is_empty() { diff --git a/src/app/page.tsx b/src/app/page.tsx index 50c6168..425e1cf 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -75,6 +75,11 @@ import { ONBOARDING_TOUR_FINISHED_EVENT, setOnboardingActive, } from "@/lib/onboarding-signal"; +import { + matchesProfile, + type ProfileSearchContext, + parseProfileSearch, +} from "@/lib/profile-search"; import { matchesGroupDigit, matchesShortcut, @@ -91,6 +96,7 @@ import { import type { BrowserProfile, ConsistencyResult, + ExtensionGroup, PreLaunchChecks, SyncSettings, WayfernConfig, @@ -266,6 +272,36 @@ export default function Home() { const { vpnConfigs } = useVpnEvents(); + // Extension groups feed both the table's Ext column and the search filter's + // `ext:` lookup, so the list is loaded here and handed down rather than + // fetched twice. Refreshed when the backend emits 'extensions-changed' + // (group rename/create/delete). + const [extensionGroups, setExtensionGroups] = useState([]); + + useEffect(() => { + let mounted = true; + let unlisten: (() => void) | undefined; + const load = async () => { + try { + const data = await invoke("list_extension_groups"); + if (mounted) setExtensionGroups(data); + } catch (e) { + console.error("Failed to load extension groups:", e); + } + }; + void load(); + void listen("extensions-changed", () => { + void load(); + }).then((u) => { + if (mounted) unlisten = u; + else u(); + }); + return () => { + mounted = false; + unlisten?.(); + }; + }, []); + // Synchronizer sessions const { getProfileSyncInfo } = useSyncSessions(); const [syncLeaderProfile, setSyncLeaderProfile] = @@ -1921,41 +1957,39 @@ export default function Home() { void checkSelfHostedSync(); }, [checkSelfHostedSync]); - // Filter data by selected group and search query + // A profile stores ids, and the query asks about names, so the matcher is + // handed the resolution up front. Built off the entity lists rather than off + // `profiles`, because the alternative — a .find() per row per term — is + // O(profiles x entities) on every single keystroke. + const searchContext = useMemo( + () => ({ + groupNames: new Map(groupsData.map((g) => [g.id, g.name])), + proxyNames: new Map(storedProxies.map((p) => [p.id, p.name])), + vpnNames: new Map(vpnConfigs.map((v) => [v.id, v.name])), + extensionGroupNames: new Map(extensionGroups.map((e) => [e.id, e.name])), + runningProfiles, + }), + [groupsData, storedProxies, vpnConfigs, extensionGroups, runningProfiles], + ); + + // Filter data by selected group and search query. The two are independent + // controls and both apply: the rail narrows to a group, the query narrows + // within whatever the rail left. const filteredProfiles = useMemo(() => { - let filtered = profiles; + // "__all__" is a virtual filter that shows every profile (including + // ungrouped ones). Any other value is a real group id; ungrouped profiles + // only show through "All". + const inGroup = + !selectedGroupId || selectedGroupId === "__all__" + ? profiles + : profiles.filter((profile) => profile.group_id === selectedGroupId); - // Filter by group. "__all__" is a virtual filter that shows every - // profile (including ungrouped ones). Any other value is a real - // group id; ungrouped profiles only show through "All". - if (!selectedGroupId || selectedGroupId === "__all__") { - filtered = profiles; - } else { - filtered = profiles.filter( - (profile) => profile.group_id === selectedGroupId, - ); - } - - // Filter by search query - if (searchQuery.trim()) { - const query = searchQuery.toLowerCase().trim(); - filtered = filtered.filter((profile) => { - // Search in profile name - if (profile.name.toLowerCase().includes(query)) return true; - - // Search in note - if (profile.note?.toLowerCase().includes(query)) return true; - - // Search in tags - if (profile.tags?.some((tag) => tag.toLowerCase().includes(query))) - return true; - - return false; - }); - } - - return filtered; - }, [profiles, selectedGroupId, searchQuery]); + const parsed = parseProfileSearch(searchQuery); + if (parsed.isEmpty) return inGroup; + return inGroup.filter((profile) => + matchesProfile(profile, parsed, searchContext), + ); + }, [profiles, selectedGroupId, searchQuery, searchContext]); // Update loading states const isLoading = profilesLoading || groupsLoading || proxiesLoading; @@ -2015,6 +2049,7 @@ export default function Home() { onCopyCookiesToProfile={handleCopyCookiesToProfile} onOpenCookieManagement={handleOpenCookieManagement} runningProfiles={runningProfiles} + extensionGroups={extensionGroups} isUpdating={isUpdating} onDeleteSelectedProfiles={handleDeleteSelectedProfiles} onAssignProfilesToGroup={handleAssignProfilesToGroup} diff --git a/src/components/extension-management-dialog.tsx b/src/components/extension-management-dialog.tsx index 1625728..92ff52f 100644 --- a/src/components/extension-management-dialog.tsx +++ b/src/components/extension-management-dialog.tsx @@ -1492,9 +1492,12 @@ export function ExtensionManagementDialog({ className="w-full table-fixed" containerClassName="overflow-visible" > - + {extTable.getHeaderGroups().map((headerGroup) => ( - + {headerGroup.headers.map((header) => ( {row.getVisibleCells().map((cell) => ( - + {groupTable.getHeaderGroups().map((headerGroup) => ( - + {headerGroup.headers.map((header) => ( {row.getVisibleCells().map((cell) => ( - + {table.getHeaderGroups().map((headerGroup) => ( - + {headerGroup.headers.map((header) => ( {row.getVisibleCells().map((cell) => ( { + const { t } = useTranslation(); + + return ( + + + + + +
+
+

+ {t("search.helpTitle")} +

+

+ {t("search.helpIntro")} +

+
+ +
+

+ {t("search.fieldsTitle")} +

+
+ {PROFILE_SEARCH_FIELDS.map((field) => ( +
+ + {field.key}: + + + {t(field.labelKey)} + {field.values ? ( + + {field.values.join(" ")} + + ) : null} + +
+ ))} +
+
+ +
+

+ {t("search.operatorsTitle")} +

+
+ {PROFILE_SEARCH_OPERATORS.map((operator) => ( +
+ + {operator.token} + + + {t(operator.labelKey)} + +
+ ))} +
+
+ +
+

+ {t("search.examplesTitle")} +

+
+ {PROFILE_SEARCH_EXAMPLES.map((example) => ( +
+ + {example.query} + +

{t(example.labelKey)}

+
+ ))} +
+
+
+
+
+ ); +}; + const HomeHeader = ({ onCreateProfileDialogOpen, searchQuery, @@ -354,6 +463,8 @@ const HomeHeader = ({ )} + {showProfileToolbar && } + {showProfileToolbar && ( diff --git a/src/components/profile-data-table.tsx b/src/components/profile-data-table.tsx index 15784e2..7404cb6 100644 --- a/src/components/profile-data-table.tsx +++ b/src/components/profile-data-table.tsx @@ -70,6 +70,7 @@ import { CommandItem, CommandList, } from "@/components/ui/command"; +import { CopyToClipboard } from "@/components/ui/copy-to-clipboard"; import { DropdownMenu, DropdownMenuContent, @@ -327,6 +328,13 @@ const BOT_LABEL_WIDTH = 880; /** Below this the bot column leaves entirely, like the other low-priority ones. */ const BOT_COLUMN_MIN_WIDTH = 400; +/** + * Above this the table has room for the profile id. Below it the name column, + * which takes whatever the fixed columns leave over, needs those 100px more + * than a value that is already one click away in the info dialog. + */ +const PROFILE_ID_MIN_WIDTH = 1152; + /** Bulk enrolments of this size or larger are confirmed, as run and stop are. */ const BULK_ENROL_CONFIRM_THRESHOLD = 10; @@ -593,6 +601,41 @@ function DnsCell({ ); } +/** + * The first eight characters of the profile's UUID: the whole first group of a + * v4, which is also the prefix `id:` searches on, so what the row shows can be + * pasted straight back into the search box. The clipboard gets the FULL id — + * the only thing it is for is the REST and MCP APIs, which take nothing less. + */ +function ProfileIdCell({ + profile, + meta, +}: { + profile: BrowserProfile; + meta: TableMeta; +}) { + return ( +
+ + + + {profile.id.slice(0, 8)} + + + + {profile.id} + + + +
+ ); +} + const TagsCell = React.memo<{ profile: BrowserProfile; isDisabled: boolean; @@ -1403,6 +1446,11 @@ interface ProfilesDataTableProps { onCopyCookiesToProfile?: (profile: BrowserProfile) => void; onOpenCookieManagement?: (profile: BrowserProfile) => void; runningProfiles: Set; + /** + * Loaded by the page rather than here, because the search filter resolves + * `ext:` to a group name and needs the same list. One invoke, one listener. + */ + extensionGroups: ExtensionGroup[]; isUpdating: (browser: string) => boolean; onDeleteSelectedProfiles: (profileIds: string[]) => Promise; onAssignProfilesToGroup: (profileIds: string[]) => void; @@ -1461,6 +1509,7 @@ export function ProfilesDataTable({ onCopyCookiesToProfile, onOpenCookieManagement, runningProfiles, + extensionGroups, isUpdating, onAssignProfilesToGroup, onAssignProfilesToProxy, @@ -1675,35 +1724,6 @@ export function ProfilesDataTable({ const [countries, setCountries] = React.useState([]); const [countriesLoaded, setCountriesLoaded] = React.useState(false); - // Extension groups for the Ext column lookup. Refreshed when the - // backend emits 'extensions-changed' (group rename/create/delete). - const [extensionGroups, setExtensionGroups] = React.useState< - ExtensionGroup[] - >([]); - - React.useEffect(() => { - let mounted = true; - let unlisten: (() => void) | undefined; - const load = async () => { - try { - const data = await invoke("list_extension_groups"); - if (mounted) setExtensionGroups(data); - } catch (e) { - console.error("Failed to load extension groups:", e); - } - }; - void load(); - void listen("extensions-changed", () => { - void load(); - }).then((u) => { - if (mounted) unlisten = u; - else u(); - }); - return () => { - mounted = false; - unlisten?.(); - }; - }, []); const canCreateLocationProxy = false; const loadCountries = React.useCallback(async () => { @@ -3148,6 +3168,19 @@ export function ProfilesDataTable({ ); }, }, + { + id: "profileId", + size: 100, + enableSorting: false, + header: ({ table }) => { + const meta = table.options.meta as TableMeta; + return meta.t("profiles.table.profileId"); + }, + cell: ({ row, table }) => { + const meta = table.options.meta as TableMeta; + return ; + }, + }, { id: "tags", size: 100, @@ -3562,11 +3595,16 @@ export function ProfilesDataTable({ // Low-priority columns leave the table as the container narrows (most // expendable first); their data stays reachable via the profile info // dialog. Visibility (not CSS hiding) so table-fixed reclaims the width. - // `bot` starts hidden and is switched on by the resize effect below. An - // unentitled account must never see a paid column, not even for the frame - // before the observer's first measurement lands. + // `bot` and `profileId` start hidden and are switched on by the resize effect + // below. An unentitled account must never see a paid column, not even for the + // frame before the observer's first measurement lands, and the id must not + // flash into a narrow table for that same frame. const [columnVisibility, setColumnVisibility] = - React.useState({ created_at: false, bot: false }); + React.useState({ + created_at: false, + bot: false, + profileId: false, + }); const table = useReactTable({ data: profiles, @@ -3630,6 +3668,8 @@ export function ProfilesDataTable({ const next: VisibilityState = { // Always hidden — sort-only column. created_at: false, + // First to leave: pure metadata, and the info dialog still has it. + profileId: w >= PROFILE_ID_MIN_WIDTH, dns: w >= 768, ext: w >= 672, note: w >= 576, diff --git a/src/components/proxy-management-dialog.tsx b/src/components/proxy-management-dialog.tsx index 41166b5..639009b 100644 --- a/src/components/proxy-management-dialog.tsx +++ b/src/components/proxy-management-dialog.tsx @@ -1263,9 +1263,12 @@ export function ProxyManagementDialog({ className="w-full table-fixed" containerClassName="overflow-visible" > - + {proxiesTable.getHeaderGroups().map((headerGroup) => ( - + {headerGroup.headers.map((header) => ( {row.getVisibleCells().map((cell) => ( - + {vpnsTable.getHeaderGroups().map((headerGroup) => ( - + {headerGroup.headers.map((header) => ( {row.getVisibleCells().map((cell) => ( matchesProfile(p, parsed, ctx)) + .map((p) => p.name); +} + +test("an empty query matches everything", () => { + for (const raw of ["", " ", '""', "\t\n"]) { + const parsed = parseProfileSearch(raw); + assert.equal(parsed.isEmpty, true, `expected ${JSON.stringify(raw)} empty`); + assert.equal(hit(raw, profile()), true); + } +}); + +test("plain text still searches name, note and tags", () => { + assert.equal(hit("shopify", profile()), true); + assert.equal(hit("SHOPIFY", profile()), true); + assert.equal(hit("amazon", profile()), false); + assert.equal(hit("renew", profile({ note: "Renew the card" })), true); + assert.equal(hit("ads", profile({ tags: ["paid-ads", "eu"] })), true); +}); + +test("plain text also matches the id by prefix, so the table's short id works", () => { + assert.equal(hit("a1b2c3d4", profile()), true); + assert.equal(hit("A1B2C3D4", profile()), true); + assert.equal(hit("a1b2c3d4-1111-2222-3333-444455556666", profile()), true); + // A slice from the middle is not a prefix and must not match. + assert.equal(hit("2222", profile()), false); +}); + +test("a colon inside an ordinary word stays free text", () => { + const noted = profile({ + note: "check https://shop.example.com:8080 at 12:30", + }); + assert.equal(hit("https://shop.example.com:8080", noted), true); + assert.equal(hit("12:30", noted), true); + // An unknown field name is free text too, never a filter that matches nothing. + assert.equal(hit("warmup:done", profile({ note: "warmup:done" })), true); + assert.equal(hit("warmup:done", profile()), false); +}); + +test("field terms match on the resolved name, not the stored id", () => { + const target = profile({ + group_id: "g1", + proxy_id: "p1", + vpn_id: "v1", + extension_group_id: "e1", + }); + assert.equal(hit("group:client", target), true); + assert.equal(hit('group:"Client A"', target), true); + assert.equal(hit("group:g1", target), false); + assert.equal(hit("proxy:frankfurt", target), true); + assert.equal(hit("vpn:office", target), true); + assert.equal(hit("ext:blockers", target), true); + assert.equal(hit("folder:client", target), true, "alias"); + assert.equal(hit("extension:blockers", target), true, "alias"); +}); + +test("name, note, tag, id, browser, version and email fields", () => { + const target = profile({ + note: "Renew the card", + tags: ["prod", "eu"], + created_by_email: "ops@example.com", + }); + assert.equal(hit("name:shop", target), true); + assert.equal(hit("name:renew", target), false, "name must not read the note"); + assert.equal(hit("note:card", target), true); + assert.equal(hit("notes:card", target), true, "alias"); + assert.equal(hit("tag:prod", target), true); + assert.equal(hit("tags:eu", target), true, "alias"); + assert.equal(hit("id:a1b2c3d4", target), true); + assert.equal(hit("id:b2c3", target), false, "id matches by prefix only"); + assert.equal(hit("browser:wayfern", target), true); + assert.equal(hit("version:140", target), true); + assert.equal(hit("email:ops@example.com", target), true); + assert.equal(hit("owner:ops", target), true, "alias"); +}); + +test("enum fields match a slug by prefix, so a half-typed value narrows", () => { + const running = profile({ id: "shop", name: "Live" }); + assert.equal(hit("status:running", running), true); + assert.equal(hit("status:run", running), true); + assert.equal(hit("status:stopped", running), false); + assert.equal(hit("status:stopped", profile()), true); + assert.equal(hit("os:macos", profile({ host_os: "macos" })), true); + assert.equal( + hit("os:windows", profile({ wayfern_config: { os: "windows" } })), + true, + "falls back to the fingerprint OS", + ); + assert.equal( + hit("dns:pro_plus", profile({ dns_blocklist: "pro_plus" })), + true, + ); + assert.equal( + hit("sync:encrypted", profile({ sync_mode: "Encrypted" })), + true, + ); + assert.equal( + hit("sync:disabled", profile()), + true, + "unset reads as disabled", + ); +}); + +test("boolean fields take yes and no", () => { + assert.equal(hit("locked:yes", profile({ password_protected: true })), true); + assert.equal(hit("locked:no", profile({ password_protected: true })), false); + assert.equal(hit("password:no", profile()), true, "alias, unset is false"); + assert.equal(hit("ephemeral:yes", profile({ ephemeral: true })), true); + assert.equal( + hit("locked:maybe", profile({ password_protected: true })), + false, + "an unusable value matches nothing rather than everything", + ); +}); + +test("none and any answer the empty question on every relation", () => { + const bare = profile(); + const wired = profile({ proxy_id: "p1", group_id: "g1", tags: ["eu"] }); + assert.equal(hit("proxy:none", bare), true); + assert.equal(hit("proxy:none", wired), false); + assert.equal(hit("proxy:any", wired), true); + assert.equal(hit("group:none", bare), true); + assert.equal(hit("tag:none", bare), true); + assert.equal(hit("tag:any", wired), true); + assert.equal(hit("note:any", profile({ note: "x" })), true); + // A quoted value is the literal word, so a tag really called "none" is findable. + assert.equal(hit('tag:"none"', profile({ tags: ["none"] })), true); + assert.equal(hit('tag:"none"', bare), false); + // A proxy whose stored name no longer resolves still counts as having one. + assert.equal(hit("proxy:none", profile({ proxy_id: "gone" })), false); + assert.equal(hit("proxy:any", profile({ proxy_id: "gone" })), true); +}); + +test("negation inverts a term, on both kinds", () => { + const tagged = profile({ tags: ["banned"] }); + assert.equal(hit("-tag:banned", tagged), false); + assert.equal(hit("-tag:banned", profile()), true); + assert.equal(hit("!tag:banned", tagged), false, "! is an alias for -"); + assert.equal(hit("tag!=banned", tagged), false); + assert.equal(hit("tag!=banned", profile()), true); + assert.equal(hit("-shopify", profile()), false); + assert.equal(hit("-amazon", profile()), true); +}); + +test("quotes hold a value together and keep separators literal", () => { + const spaced = profile({ tags: ["black friday"], note: "a, b" }); + assert.equal(hit('tag:"black friday"', spaced), true); + assert.equal( + hit("tag:black friday", profile({ tags: ["black"] })), + false, + "unquoted is two terms, and nothing here matches the second", + ); + assert.equal(hit('note:"a, b"', spaced), true, "a quoted comma is literal"); + assert.equal(hit('"-lead"', profile({ name: "-lead" })), true); +}); + +test("several terms are ANDed", () => { + const target = profile({ tags: ["prod"], group_id: "g1", note: "vat" }); + assert.equal(hit("tag:prod group:client", target), true); + assert.equal(hit("tag:prod group:other", target), false); + assert.equal(hit("shopify tag:prod note:vat status:stopped", target), true); + assert.equal(hit("shopify tag:prod -note:vat", target), false); +}); + +test("or joins two terms, and binds tighter than the implicit and", () => { + const rows = [ + profile({ name: "A", tags: ["ads"], id: "shop" }), + profile({ name: "B", tags: ["seo"] }), + profile({ name: "C", tags: ["other"] }), + ]; + assert.deepEqual(names("tag:ads or tag:seo", rows), ["A", "B"]); + assert.deepEqual(names("tag:ads or tag:seo status:running", rows), ["A"]); + assert.deepEqual(names("tag:ads,seo", rows), ["A", "B"], "comma is or"); + assert.deepEqual( + names("OR tag:ads or", rows), + ["A"], + "a dangling or is ignored", + ); +}); + +test("an = prefix forces a whole-value match", () => { + const long = profile({ tags: ["production"] }); + assert.equal(hit("tag:prod", long), true); + assert.equal(hit("tag:=prod", long), false); + assert.equal(hit("tag:=production", long), true); + assert.equal(hit('group:="Client A"', profile({ group_id: "g1" })), true); + assert.equal( + hit("name:=shopify", profile()), + false, + "the real name is longer", + ); +}); + +test("dates take relative durations, read the way the question is asked", () => { + const fresh = profile({ last_launch: NOW / 1000 - 2 * DAY }); + const cold = profile({ last_launch: NOW / 1000 - 90 * DAY }); + assert.equal(hit("launched:<7d", fresh), true); + assert.equal(hit("launched:<7d", cold), false); + assert.equal( + hit("launched:>30d", cold), + true, + "not launched for over 30 days", + ); + assert.equal(hit("launched:>30d", fresh), false); + assert.equal(hit("launched:7d", fresh), true, "bare means within"); + assert.equal( + hit("launched:<12h", profile({ last_launch: NOW / 1000 - HOUR })), + true, + ); + assert.equal(hit("launched:never", profile()), true); + assert.equal(hit("launched:never", fresh), false); + assert.equal(hit("launched:any", fresh), true); + assert.equal(hit("lastlaunch:<7d", fresh), true, "alias"); +}); + +test("dates take absolute days, months and years", () => { + const made = profile({ + created_at: Date.parse("2026-03-04T10:00:00") / 1000, + }); + assert.equal(hit("created:2026-03-04", made), true); + assert.equal(hit("created:2026-03-05", made), false); + assert.equal(hit("created:2026-03", made), true); + assert.equal(hit("created:2026", made), true); + assert.equal(hit("created:>=2026-01-01", made), true); + assert.equal(hit("created:<2026-01-01", made), false); + assert.equal( + hit("created:>2026-03", made), + false, + "March is not after March", + ); + assert.equal( + hit("created:none", profile()), + true, + "legacy profiles have none", + ); +}); + +test("version comparisons run segment by segment", () => { + assert.equal(hit("version:>140", profile()), true); + assert.equal(hit("version:>=140.0", profile()), true); + assert.equal(hit("version:<140", profile()), false); + assert.equal(hit("version:>141", profile()), false); + assert.equal( + hit("version:>9", profile({ version: "10.0.1" })), + true, + "not lexical", + ); +}); + +test("a query being typed never throws and never blanks the list", () => { + const target = profile({ tags: ["prod"], note: 'say "hello"' }); + const halves = [ + 'name:"unclosed', + "name:", + "tag:", + "-", + "!", + ":", + '"', + '""', + "or", + "or or or", + "tag:,,,", + "created:>", + "created:>notadate", + "launched:=<:", + "name:>shop", + ]; + for (const raw of halves) { + assert.doesNotThrow(() => parseProfileSearch(raw), raw); + assert.doesNotThrow(() => hit(raw, target), raw); + } + assert.equal(hit('name:"unclosed', profile({ name: "unclosed" })), true); + assert.equal(hit("tag:", target), true, "a bare field filters nothing"); + assert.equal( + hit("tag:prod created:notadate", target), + true, + "bad date drops itself", + ); + assert.equal( + hit("name:>shop", profile()), + false, + "a bad operator falls to text", + ); + assert.equal(hit("name:>shop", profile({ note: "name:>shop" })), true); +}); + +test("unicode values compare case-insensitively", () => { + const cyrillic = profile({ name: "Профиль Магазин", tags: ["Реклама"] }); + assert.equal(hit("магазин", cyrillic), true); + assert.equal(hit("МАГАЗИН", cyrillic), true); + assert.equal(hit("tag:реклама", cyrillic), true); + assert.equal(hit("name:профиль", cyrillic), true); + + const cjk = profile({ name: "東京プロファイル", note: "測試" }); + assert.equal(hit("東京", cjk), true); + assert.equal(hit("note:測試", cjk), true); + + const emoji = profile({ name: "Store 🛒 EU", tags: ["🔥 hot"] }); + assert.equal(hit("🛒", emoji), true); + assert.equal(hit('tag:"🔥 hot"', emoji), true); + assert.equal(hit("straße", profile({ name: "Straße Berlin" })), true); +}); + +test("every field carries a translation key and unique tokens", () => { + const seen = new Set(); + for (const field of PROFILE_SEARCH_FIELDS) { + assert.match(field.labelKey, /^search\.fields\./, field.key); + for (const token of [field.key, ...field.aliases]) { + assert.equal(seen.has(token), false, `duplicate token ${token}`); + seen.add(token); + } + } +}); diff --git a/src/lib/profile-search.ts b/src/lib/profile-search.ts new file mode 100644 index 0000000..b23a618 --- /dev/null +++ b/src/lib/profile-search.ts @@ -0,0 +1,751 @@ +/** + * The profile search grammar. + * + * One text box carries the whole filter, so the grammar has to survive whatever + * is in it halfway through a keystroke: a bare word still means what it always + * meant, and anything the parser does not recognise degrades to that bare-word + * search instead of to an error or an empty table. `foo:bar` is free text + * because `foo` is not a field, which is what keeps a pasted `https://x:8080` + * or a `12:30` in a note searchable. Nothing here throws, and nothing here may + * answer "no rows" because of syntax. + * + * Field names and values are ASCII slugs and are never translated, so a query + * means the same thing in every locale; only the help panel's prose goes + * through `t()`, keyed off the `labelKey` each field carries. The React layer + * calls `parseProfileSearch` and `matchesProfile` and nothing else — every + * lookup the matcher needs (a group's name for its id, which profiles are + * running) arrives in the `ProfileSearchContext` the caller builds. + * + * Kept free of runtime imports so `profile-search.test.mjs` can load it + * directly, the way `proxy-string.ts` is. + */ + +import type { BrowserProfile } from "@/types"; + +export type ProfileSearchFieldKind = + | "text" + | "tags" + | "id" + | "lookup" + | "enum" + | "boolean" + | "date" + | "version"; + +export interface ProfileSearchField { + /** Canonical token; the one the help panel teaches. */ + readonly key: string; + readonly aliases: readonly string[]; + readonly kind: ProfileSearchFieldKind; + /** Translation key describing the field to a human. */ + readonly labelKey: string; + /** Accepted slugs, where the set is closed. Shown in the help panel. */ + readonly values?: readonly string[]; +} + +/** + * The closed field vocabulary. Closed on purpose: a token only becomes a field + * when it is in here, so adding a short everyday word (`ip`, `url`) would + * silently turn someone's plain-text search into a filter. + */ +export const PROFILE_SEARCH_FIELDS: readonly ProfileSearchField[] = [ + { key: "name", aliases: [], kind: "text", labelKey: "search.fields.name" }, + { + key: "tag", + aliases: ["tags"], + kind: "tags", + labelKey: "search.fields.tag", + }, + { + key: "note", + aliases: ["notes"], + kind: "text", + labelKey: "search.fields.note", + }, + { key: "id", aliases: [], kind: "id", labelKey: "search.fields.id" }, + { + key: "group", + aliases: ["folder"], + kind: "lookup", + labelKey: "search.fields.group", + }, + { + key: "proxy", + aliases: [], + kind: "lookup", + labelKey: "search.fields.proxy", + }, + { key: "vpn", aliases: [], kind: "lookup", labelKey: "search.fields.vpn" }, + { + key: "ext", + aliases: ["extension"], + kind: "lookup", + labelKey: "search.fields.ext", + }, + { + key: "dns", + aliases: [], + kind: "enum", + labelKey: "search.fields.dns", + values: ["light", "normal", "pro", "pro_plus", "ultimate", "custom"], + }, + { + key: "os", + aliases: [], + kind: "enum", + labelKey: "search.fields.os", + values: ["macos", "windows", "linux"], + }, + { + key: "browser", + aliases: [], + kind: "text", + labelKey: "search.fields.browser", + }, + { + key: "status", + aliases: [], + kind: "enum", + labelKey: "search.fields.status", + values: ["running", "stopped"], + }, + { + key: "sync", + aliases: [], + kind: "enum", + labelKey: "search.fields.sync", + values: ["disabled", "regular", "encrypted"], + }, + { + key: "email", + aliases: ["owner"], + kind: "text", + labelKey: "search.fields.email", + }, + { + key: "version", + aliases: [], + kind: "version", + labelKey: "search.fields.version", + }, + { + key: "locked", + aliases: ["password"], + kind: "boolean", + labelKey: "search.fields.locked", + values: ["yes", "no"], + }, + { + key: "ephemeral", + aliases: [], + kind: "boolean", + labelKey: "search.fields.ephemeral", + values: ["yes", "no"], + }, + { + key: "created", + aliases: [], + kind: "date", + labelKey: "search.fields.created", + }, + { + key: "launched", + aliases: ["lastlaunch"], + kind: "date", + labelKey: "search.fields.launched", + }, +]; + +/** Operator vocabulary, for the help panel. The token is the syntax itself. */ +export const PROFILE_SEARCH_OPERATORS: readonly { + readonly token: string; + readonly labelKey: string; +}[] = [ + { token: "-tag:ads", labelKey: "search.operators.negate" }, + { token: 'group:"Client A"', labelKey: "search.operators.quote" }, + { token: "tag:ads or tag:seo", labelKey: "search.operators.or" }, + { token: "tag:ads,seo", labelKey: "search.operators.comma" }, + { token: "tag:=prod", labelKey: "search.operators.exact" }, + { token: "proxy:none", labelKey: "search.operators.none" }, + { token: "created:>=2026-01-01", labelKey: "search.operators.compare" }, +]; + +/** Whole queries worth copying, for the help panel. */ +export const PROFILE_SEARCH_EXAMPLES: readonly { + readonly query: string; + readonly labelKey: string; +}[] = [ + { query: 'status:running group:"Client A"', labelKey: "search.examples.a" }, + { query: "tag:none proxy:any", labelKey: "search.examples.b" }, + { query: "launched:>30d -tag:archived", labelKey: "search.examples.c" }, +]; + +export type ProfileSearchOperator = "match" | "lt" | "lte" | "gt" | "gte"; + +interface FreeTextTerm { + readonly type: "text"; + /** Already lowercased. */ + readonly value: string; + readonly negated: boolean; +} + +interface FieldTerm { + readonly type: "field"; + readonly field: ProfileSearchField; + readonly operator: ProfileSearchOperator; + /** Alternatives from the comma shorthand; any one matching matches. */ + readonly values: readonly string[]; + readonly negated: boolean; + /** `=value`: whole-value match rather than substring. */ + readonly exact: boolean; + /** The value was quoted, so `none` and `any` are literal text. */ + readonly quoted: boolean; +} + +export type ProfileSearchTerm = FreeTextTerm | FieldTerm; + +export interface ParsedProfileSearch { + /** AND across the groups, OR inside each one. */ + readonly groups: readonly (readonly ProfileSearchTerm[])[]; + /** Nothing left to filter on, so every profile matches. */ + readonly isEmpty: boolean; +} + +export interface ProfileSearchContext { + /** Group id to the name the table shows for it. Same for the three below. */ + readonly groupNames: ReadonlyMap; + readonly proxyNames: ReadonlyMap; + readonly vpnNames: ReadonlyMap; + readonly extensionGroupNames: ReadonlyMap; + readonly runningProfiles: ReadonlySet; + /** Epoch ms the relative dates count back from. Defaults to the wall clock. */ + readonly now?: number; +} + +const FIELD_BY_TOKEN: ReadonlyMap = new Map( + PROFILE_SEARCH_FIELDS.flatMap((field) => + [field.key, ...field.aliases].map( + (token) => [token, field] as [string, ProfileSearchField], + ), + ), +); + +const RESERVED_NONE = "none"; +const RESERVED_ANY = "any"; +const RESERVED_NEVER = "never"; + +const DAY_MS = 86_400_000; +const DURATION_UNITS: Readonly> = { + h: 3_600_000, + d: DAY_MS, + w: 7 * DAY_MS, + m: 30 * DAY_MS, + y: 365 * DAY_MS, +}; + +interface QueryChar { + readonly c: string; + readonly quoted: boolean; +} + +/** + * Splits on whitespace outside double quotes. An unclosed quote runs to the end + * of the input instead of being rejected: the query is re-parsed on every + * keystroke, so `name:"unclosed` is a query being typed, not a mistake. Each + * character remembers whether it was quoted, which is what keeps a separator + * inside quotes (`group:"Acme, Inc"`) literal. + */ +function tokenize(raw: string): QueryChar[][] { + const tokens: QueryChar[][] = []; + let current: QueryChar[] = []; + let quoted = false; + for (const c of raw) { + if (c === '"') { + quoted = !quoted; + continue; + } + if (!quoted && /\s/.test(c)) { + if (current.length > 0) { + tokens.push(current); + current = []; + } + continue; + } + current.push({ c, quoted }); + } + if (current.length > 0) tokens.push(current); + return tokens; +} + +function textOf(chars: readonly QueryChar[]): string { + let out = ""; + for (const ch of chars) out += ch.c; + return out; +} + +function hasQuoted(chars: readonly QueryChar[]): boolean { + return chars.some((ch) => ch.quoted); +} + +/** Splits on an unquoted separator, dropping the empty pieces. */ +function splitUnquoted(chars: readonly QueryChar[], sep: string): string[] { + const parts: string[] = []; + let current = ""; + for (const ch of chars) { + if (ch.c === sep && !ch.quoted) { + if (current.length > 0) parts.push(current); + current = ""; + continue; + } + current += ch.c; + } + if (current.length > 0) parts.push(current); + return parts; +} + +interface SeparatorToken { + readonly token: string; + readonly operator: ProfileSearchOperator; + readonly negates: boolean; +} + +/** Longest first, so `>=` is never read as `>` followed by a stray `=`. */ +const SEPARATORS: readonly SeparatorToken[] = [ + { token: ">=", operator: "gte", negates: false }, + { token: "<=", operator: "lte", negates: false }, + { token: "!=", operator: "match", negates: true }, + { token: ":", operator: "match", negates: false }, + { token: ">", operator: "gt", negates: false }, + { token: "<", operator: "lt", negates: false }, +]; + +function separatorAt( + chars: readonly QueryChar[], + index: number, +): SeparatorToken | null { + for (const candidate of SEPARATORS) { + let hit = true; + for (let i = 0; i < candidate.token.length; i++) { + const ch = chars[index + i]; + if (!ch || ch.quoted || ch.c !== candidate.token[i]) { + hit = false; + break; + } + } + if (hit) return candidate; + } + return null; +} + +/** Comparisons only mean something where the values are ordered. */ +function acceptsComparison(field: ProfileSearchField): boolean { + return field.kind === "date" || field.kind === "version"; +} + +function freeText(value: string, negated: boolean): FreeTextTerm | null { + const trimmed = value.trim(); + if (trimmed.length === 0) return null; + return { type: "text", value: trimmed.toLowerCase(), negated }; +} + +function buildTerm(chars: readonly QueryChar[]): ProfileSearchTerm | null { + let negated = false; + let body = chars; + const first = body[0]; + if ( + body.length > 1 && + first && + !first.quoted && + (first.c === "-" || first.c === "!") + ) { + negated = true; + body = body.slice(1); + } + + let found: { at: number; token: SeparatorToken } | null = null; + for (let i = 0; i < body.length && !found; i++) { + if (body[i].quoted) continue; + const token = separatorAt(body, i); + if (token) found = { at: i, token }; + } + if (!found || found.at === 0) return freeText(textOf(body), negated); + + const name = textOf(body.slice(0, found.at)).toLowerCase(); + const field = FIELD_BY_TOKEN.get(name); + // An unrecognised name is never an error: it is somebody's note holding a + // URL, and returning zero rows for it would be the worst possible answer. + if (!field) return freeText(textOf(body), negated); + + let operator = found.token.operator; + let value = body.slice(found.at + found.token.token.length); + // `created:>=2026-01-01` writes the comparison after the colon; `created>=...` + // writes it instead of one. Both reach the same term. + if (found.token.token === ":") { + const inner = separatorAt(value, 0); + if (inner && inner.operator !== "match") { + operator = inner.operator; + value = value.slice(inner.token.length); + } + } + if (operator !== "match" && !acceptsComparison(field)) { + return freeText(textOf(body), negated); + } + if (value.length === 0) return null; + if (found.token.negates) negated = !negated; + + let exact = false; + const lead = value[0]; + if (lead && !lead.quoted && lead.c === "=") { + exact = true; + value = value.slice(1); + if (value.length === 0) return null; + } + + const quoted = hasQuoted(value); + const values = (quoted ? [textOf(value)] : splitUnquoted(value, ",")).map( + (v) => v.toLowerCase(), + ); + if (values.length === 0) return null; + + if (field.kind === "date") { + const usable = values.filter((v) => parseDateValue(v) !== null); + // A date that does not parse drops its own term and leaves the rest of the + // query running, rather than filtering everything away. + if (usable.length === 0) return null; + return { + type: "field", + field, + operator, + values: usable, + negated, + exact, + quoted, + }; + } + + return { + type: "field", + field, + operator, + values, + negated, + exact, + quoted, + }; +} + +/** + * Turns raw input into AND-ed groups of OR-ed terms. Total: every input, valid + * or not, produces a result, and an input with nothing usable in it produces an + * empty one that matches every profile. + */ +export function parseProfileSearch(raw: string): ParsedProfileSearch { + const groups: ProfileSearchTerm[][] = []; + let pendingOr = false; + + for (const chars of tokenize(raw)) { + if (!hasQuoted(chars) && textOf(chars).toLowerCase() === "or") { + // A dangling `or` at either end simply has nothing to join. + pendingOr = groups.length > 0; + continue; + } + const term = buildTerm(chars); + if (!term) continue; + const last = groups[groups.length - 1]; + if (pendingOr && last) { + last.push(term); + } else { + groups.push([term]); + } + pendingOr = false; + } + + return { groups, isEmpty: groups.length === 0 }; +} + +type DateValue = + | { readonly kind: "relative"; readonly durationMs: number } + | { + readonly kind: "absolute"; + readonly startMs: number; + readonly endMs: number; + } + | { readonly kind: "never" } + | { readonly kind: "any" }; + +const RELATIVE_PATTERN = /^(\d+)([hdwmy])$/; +const ABSOLUTE_PATTERN = /^(\d{4})(?:-(\d{2})(?:-(\d{2}))?)?$/; + +/** `null` for anything that is not a date, which is how a term gets dropped. */ +function parseDateValue(value: string): DateValue | null { + if (value === RESERVED_NEVER || value === RESERVED_NONE) { + return { kind: "never" }; + } + if (value === RESERVED_ANY) return { kind: "any" }; + + const relative = RELATIVE_PATTERN.exec(value); + if (relative) { + const amount = Number.parseInt(relative[1], 10); + const unit = DURATION_UNITS[relative[2]]; + if (unit === undefined) return null; + return { kind: "relative", durationMs: amount * unit }; + } + + const absolute = ABSOLUTE_PATTERN.exec(value); + if (!absolute) return null; + const year = Number.parseInt(absolute[1], 10); + if (absolute[2] === undefined) { + return { + kind: "absolute", + startMs: new Date(year, 0, 1).getTime(), + endMs: new Date(year + 1, 0, 1).getTime(), + }; + } + const month = Number.parseInt(absolute[2], 10); + if (month < 1 || month > 12) return null; + if (absolute[3] === undefined) { + return { + kind: "absolute", + startMs: new Date(year, month - 1, 1).getTime(), + endMs: new Date(year, month, 1).getTime(), + }; + } + const day = Number.parseInt(absolute[3], 10); + const start = new Date(year, month - 1, day); + // February 31st parses as March 3rd unless the roll-over is caught here. + if (start.getMonth() !== month - 1 || start.getDate() !== day) return null; + return { + kind: "absolute", + startMs: start.getTime(), + endMs: new Date(year, month - 1, day + 1).getTime(), + }; +} + +function matchesDate( + seconds: number | undefined, + operator: ProfileSearchOperator, + value: string, + now: number, +): boolean { + const parsed = parseDateValue(value); + if (!parsed) return false; + if (parsed.kind === "never") return !seconds; + if (parsed.kind === "any") return Boolean(seconds); + if (!seconds) return false; + const ts = seconds * 1000; + + if (parsed.kind === "relative") { + // Read the way the question is asked, not the way the timestamps compare: + // `launched:<7d` is "inside the last 7 days" and `launched:>30d` is "not + // launched for over 30 days", which is the query an operator hunting cold + // profiles actually wants. + const threshold = now - parsed.durationMs; + switch (operator) { + case "gt": + return ts < threshold; + case "gte": + return ts <= threshold; + default: + return ts >= threshold; + } + } + + switch (operator) { + case "lt": + return ts < parsed.startMs; + case "lte": + return ts < parsed.endMs; + case "gt": + return ts >= parsed.endMs; + case "gte": + return ts >= parsed.startMs; + default: + return ts >= parsed.startMs && ts < parsed.endMs; + } +} + +function compareVersions(a: string, b: string): number { + const left = a.split("."); + const right = b.split("."); + const length = Math.max(left.length, right.length); + for (let i = 0; i < length; i++) { + const l = Number.parseInt(left[i] ?? "0", 10); + const r = Number.parseInt(right[i] ?? "0", 10); + const ln = Number.isNaN(l) ? 0 : l; + const rn = Number.isNaN(r) ? 0 : r; + if (ln !== rn) return ln < rn ? -1 : 1; + } + return 0; +} + +function parseBoolean(value: string): boolean | null { + if (value === "yes" || value === "true" || value === "1") return true; + if (value === "no" || value === "false" || value === "0") return false; + return null; +} + +interface FieldValue { + /** The profile has something here, even if its name cannot be resolved. */ + readonly present: boolean; + /** Lowercased text to compare against. */ + readonly candidates: readonly string[]; +} + +function lookupValue( + id: string | undefined, + names: ReadonlyMap, +): FieldValue { + if (!id) return { present: false, candidates: [] }; + const name = names.get(id); + return { + present: true, + candidates: name ? [name.toLowerCase()] : [], + }; +} + +function fieldValue( + profile: BrowserProfile, + field: ProfileSearchField, + ctx: ProfileSearchContext, +): FieldValue { + const one = (value: string | undefined | null): FieldValue => + value + ? { present: true, candidates: [value.toLowerCase()] } + : { present: false, candidates: [] }; + + switch (field.key) { + case "name": + return one(profile.name); + case "note": + return one(profile.note); + case "browser": + return one(profile.browser); + case "version": + return one(profile.version); + case "email": + return one(profile.created_by_email); + case "id": + return { present: true, candidates: [profile.id.toLowerCase()] }; + case "tag": { + const tags = profile.tags ?? []; + return { + present: tags.length > 0, + candidates: tags.map((tag) => tag.toLowerCase()), + }; + } + case "group": + return lookupValue(profile.group_id, ctx.groupNames); + case "proxy": + return lookupValue(profile.proxy_id, ctx.proxyNames); + case "vpn": + return lookupValue(profile.vpn_id, ctx.vpnNames); + case "ext": + return lookupValue(profile.extension_group_id, ctx.extensionGroupNames); + case "dns": + return one(profile.dns_blocklist); + case "os": + return one(profile.host_os ?? profile.wayfern_config?.os); + case "sync": + return one(profile.sync_mode ?? "Disabled"); + case "status": + return one(ctx.runningProfiles.has(profile.id) ? "running" : "stopped"); + default: + return { present: false, candidates: [] }; + } +} + +function matchesFieldValue(term: FieldTerm, value: string, actual: FieldValue) { + if (!term.quoted && !term.exact) { + if (value === RESERVED_NONE) return !actual.present; + if (value === RESERVED_ANY) return actual.present; + } + if (term.field.kind === "id") { + return actual.candidates.some((candidate) => + term.exact ? candidate === value : candidate.startsWith(value), + ); + } + if (term.field.kind === "enum") { + // A prefix is enough, so `status:run` works while the user is still typing. + return actual.candidates.some((candidate) => + term.exact ? candidate === value : candidate.startsWith(value), + ); + } + return actual.candidates.some((candidate) => + term.exact ? candidate === value : candidate.includes(value), + ); +} + +function matchesFieldTerm( + profile: BrowserProfile, + term: FieldTerm, + ctx: ProfileSearchContext, +): boolean { + const field = term.field; + + if (field.kind === "boolean") { + const actual = + field.key === "locked" + ? profile.password_protected === true + : profile.ephemeral === true; + return term.values.some((value) => parseBoolean(value) === actual); + } + + if (field.kind === "date") { + const now = ctx.now ?? Date.now(); + const seconds = + field.key === "created" ? profile.created_at : profile.last_launch; + return term.values.some((value) => + matchesDate(seconds, term.operator, value, now), + ); + } + + if (field.kind === "version" && term.operator !== "match") { + return term.values.some((value) => { + const order = compareVersions(profile.version, value); + switch (term.operator) { + case "lt": + return order < 0; + case "lte": + return order <= 0; + case "gt": + return order > 0; + default: + return order >= 0; + } + }); + } + + const actual = fieldValue(profile, field, ctx); + return term.values.some((value) => matchesFieldValue(term, value, actual)); +} + +/** + * What a bare word searches: the same three fields the box has always covered, + * plus the id, so the trimmed id the table shows can be pasted straight back in. + */ +function matchesFreeText(profile: BrowserProfile, value: string): boolean { + if (profile.name.toLowerCase().includes(value)) return true; + if (profile.note?.toLowerCase().includes(value)) return true; + if (profile.tags?.some((tag) => tag.toLowerCase().includes(value))) { + return true; + } + return profile.id.toLowerCase().startsWith(value); +} + +export function matchesProfile( + profile: BrowserProfile, + parsed: ParsedProfileSearch, + ctx: ProfileSearchContext, +): boolean { + for (const group of parsed.groups) { + const hit = group.some((term) => { + const matched = + term.type === "text" + ? matchesFreeText(profile, term.value) + : matchesFieldTerm(profile, term, ctx); + return term.negated ? !matched : matched; + }); + if (!hit) return false; + } + return true; +}