diff --git a/Cargo.lock b/Cargo.lock index 621f9131..6a76c872 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -24,7 +24,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f7b0a21988c1bf877cf4759ef5ddaac04c1c9fe808c9142ecb78ba97d97a28a" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "bytes", "futures-core", "futures-sink", @@ -45,7 +45,7 @@ dependencies = [ "actix-rt", "actix-service", "actix-utils", - "bitflags 2.13.0", + "bitflags 2.13.1", "bytes", "bytestring", "derive_more 2.1.1", @@ -160,7 +160,7 @@ dependencies = [ "serde_json", "serde_urlencoded", "smallvec", - "socket2 0.6.4", + "socket2 0.6.5", "time", "tracing", "url", @@ -348,7 +348,7 @@ dependencies = [ "persistence", "priority-queue", "prost-types", - "rand 0.8.6", + "rand 0.8.7", "rayon", "regex", "repo_metadata", @@ -367,7 +367,7 @@ dependencies = [ "strum_macros 0.27.2", "syntax_tree", "tempfile", - "thiserror 2.0.18", + "thiserror 2.0.19", "tokio", "typed-path 0.10.0", "unicode-width 0.1.14", @@ -435,7 +435,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812947049edcd670a82cd5c73c3661d2e58468577ba8489de58e1a73c04cbd5d" dependencies = [ "alsa-sys", - "bitflags 2.13.0", + "bitflags 2.13.1", "cfg-if", "libc", ] @@ -457,7 +457,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f2a1bb052857d5dd49572219344a7332b31b76405648eabac5bc68978251bcd" dependencies = [ "android-properties", - "bitflags 2.13.0", + "bitflags 2.13.1", "cc", "jni 0.22.4", "libc", @@ -466,7 +466,7 @@ dependencies = [ "ndk-context", "ndk-sys", "num_enum", - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] @@ -542,9 +542,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 = "app-installation-detection" @@ -1103,7 +1103,7 @@ checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1165,7 +1165,7 @@ dependencies = [ "enumflags2", "futures-channel", "futures-util", - "rand 0.9.4", + "rand 0.9.5", "serde", "serde_repr", "url", @@ -1207,7 +1207,7 @@ dependencies = [ "proc-macro2", "quote", "sha2 0.10.9", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1267,7 +1267,7 @@ checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" dependencies = [ "async-task", "concurrent-queue", - "fastrand 2.4.1", + "fastrand 2.5.0", "futures-lite 2.6.1", "pin-project-lite", "slab", @@ -1350,7 +1350,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1390,7 +1390,7 @@ checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1401,13 +1401,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.89" +version = "0.1.91" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" +checksum = "ae36dc4177970ef04fde5178d3e2429882def40e57a451f919c098f72baa6cec" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.3", ] [[package]] @@ -1464,7 +1464,7 @@ dependencies = [ "attribute-derive-macro", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1480,7 +1480,7 @@ dependencies = [ "proc-macro2", "quote", "quote-use", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -1504,7 +1504,7 @@ dependencies = [ "num-traits", "pastey 0.1.1", "rayon", - "thiserror 2.0.18", + "thiserror 2.0.19", "v_frame", "y4m", ] @@ -1534,9 +1534,9 @@ dependencies = [ [[package]] name = "aws-config" -version = "1.8.18" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e33f815b73a3899c03b380d543532e5865f230dce9678d108dc10732a8682275" +checksum = "701418aa459dac33e50a0f8e818e5662a16bc018a6ac7423659b70f3799d67a8" dependencies = [ "aws-credential-types", "aws-runtime", @@ -1554,11 +1554,11 @@ dependencies = [ "aws-types", "base64-simd", "bytes", - "fastrand 2.4.1", + "fastrand 2.5.0", "hex", "http 1.4.2", "p256", - "rand 0.8.6", + "rand 0.8.7", "sha1", "sha2 0.10.9", "time", @@ -1571,9 +1571,9 @@ dependencies = [ [[package]] name = "aws-credential-types" -version = "1.2.14" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f20799b373a1be121fe3005fba0c2090af9411573878f224df44b42727fcaf7" +checksum = "e93964ffdaf57857f544be3666a5f57570bb699e934700f11b49708f61bb556e" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -1583,9 +1583,9 @@ dependencies = [ [[package]] name = "aws-lc-rs" -version = "1.17.1" +version = "1.17.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4342d8937fc7e5dd9b1c60292261c0670c882a2cd1719cfc11b1af41731e32ad" +checksum = "00bdb5da18dac48ca2cc7cd4a98e533e8635a58e2361d13a1a4ee3888e0d72f1" dependencies = [ "aws-lc-sys", "zeroize", @@ -1593,9 +1593,9 @@ dependencies = [ [[package]] name = "aws-lc-sys" -version = "0.42.0" +version = "0.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9ceb1da931507a12f4fccea479dccd00da1943e1b4ae72d8e502d707361444" +checksum = "43103168cc76fe62678a375e722fc9cb3a0146159ac5828bc4f0dfd755c2224c" dependencies = [ "cc", "cmake", @@ -1606,9 +1606,9 @@ dependencies = [ [[package]] name = "aws-runtime" -version = "1.7.5" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c9b9de216a988dd54b754a82a7660cfe14cee4f6782ae4524470972fa0ccb39" +checksum = "a6b50a43f3ccdf331521c6d6c68b7cc9668b6e09d439ebda9569df5722324d76" dependencies = [ "aws-credential-types", "aws-sigv4", @@ -1621,9 +1621,9 @@ dependencies = [ "aws-types", "bytes", "bytes-utils", - "fastrand 2.4.1", + "fastrand 2.5.0", "http 1.4.2", - "http-body 1.0.1", + "http-body 1.1.0", "percent-encoding", "pin-project-lite", "tracing", @@ -1632,9 +1632,9 @@ dependencies = [ [[package]] name = "aws-sdk-bedrockruntime" -version = "1.135.0" +version = "1.137.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e74b780f2f36912bae71b4f4f8ed9a0a88832b4681a1add3caf5ca25dbc8ab2d" +checksum = "4d264d7d6412325fc6c77449f98363920da408fb426ce2d8b9b7c95324497c2c" dependencies = [ "arc-swap", "aws-credential-types", @@ -1647,10 +1647,11 @@ dependencies = [ "aws-smithy-observability", "aws-smithy-runtime", "aws-smithy-runtime-api", + "aws-smithy-schema", "aws-smithy-types", "aws-types", "bytes", - "fastrand 2.4.1", + "fastrand 2.5.0", "http 0.2.12", "http 1.4.2", "http-body-util", @@ -1660,9 +1661,9 @@ dependencies = [ [[package]] name = "aws-sdk-signin" -version = "1.14.0" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3295648005395c0611b1e4705f8cad062aa2a71789806d9d9717020b744a869f" +checksum = "4cb1aef0872a7ab9e035a8c14d9ee1c3bd7f86f741b3620df8e8ecfe7b0d14dd" dependencies = [ "arc-swap", "aws-credential-types", @@ -1673,10 +1674,11 @@ dependencies = [ "aws-smithy-observability", "aws-smithy-runtime", "aws-smithy-runtime-api", + "aws-smithy-schema", "aws-smithy-types", "aws-types", "bytes", - "fastrand 2.4.1", + "fastrand 2.5.0", "http 0.2.12", "http 1.4.2", "regex-lite", @@ -1685,9 +1687,9 @@ dependencies = [ [[package]] name = "aws-sdk-sso" -version = "1.102.0" +version = "1.104.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c82b3ac19f1431854f7ace3a7531674633e286bfdde21976893bfee36fd493b" +checksum = "b53416d16c278234845392e38d93bd4481d2f09daa0f005a2277f0aa91f59c22" dependencies = [ "arc-swap", "aws-credential-types", @@ -1698,10 +1700,11 @@ dependencies = [ "aws-smithy-observability", "aws-smithy-runtime", "aws-smithy-runtime-api", + "aws-smithy-schema", "aws-smithy-types", "aws-types", "bytes", - "fastrand 2.4.1", + "fastrand 2.5.0", "http 0.2.12", "http 1.4.2", "regex-lite", @@ -1710,9 +1713,9 @@ dependencies = [ [[package]] name = "aws-sdk-ssooidc" -version = "1.104.0" +version = "1.106.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "321000d2b4c5519ee573f73167f612efd7329322d9b26969ad1979f0427f1913" +checksum = "cc9b706c3305ed0285d5b1b696c747aa34950f830fb03e3e6c76890f99b9f188" dependencies = [ "arc-swap", "aws-credential-types", @@ -1723,10 +1726,11 @@ dependencies = [ "aws-smithy-observability", "aws-smithy-runtime", "aws-smithy-runtime-api", + "aws-smithy-schema", "aws-smithy-types", "aws-types", "bytes", - "fastrand 2.4.1", + "fastrand 2.5.0", "http 0.2.12", "http 1.4.2", "regex-lite", @@ -1735,9 +1739,9 @@ dependencies = [ [[package]] name = "aws-sdk-sts" -version = "1.107.0" +version = "1.109.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0d328ba962af23ecfa3c9f23b98d3d35e325fa218d7f13d17a6bf522f8a560" +checksum = "32d214cdfa5bbe17f117e76a7643fadf32a5234fb597322ef8b1fb4b2f17dbbd" dependencies = [ "arc-swap", "aws-credential-types", @@ -1749,10 +1753,11 @@ dependencies = [ "aws-smithy-query", "aws-smithy-runtime", "aws-smithy-runtime-api", + "aws-smithy-schema", "aws-smithy-types", "aws-smithy-xml", "aws-types", - "fastrand 2.4.1", + "fastrand 2.5.0", "http 0.2.12", "http 1.4.2", "regex-lite", @@ -1761,9 +1766,9 @@ dependencies = [ [[package]] name = "aws-sigv4" -version = "1.4.5" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bae38512beae0ffee7010fc24e7a8a123c53efdfef42a61e80fda4882418dc71" +checksum = "723c2234ad7511ceef63eab016b7ba6ff7c55590fefb96fa8467af014a07309f" dependencies = [ "aws-credential-types", "aws-smithy-eventstream", @@ -1784,9 +1789,9 @@ dependencies = [ [[package]] name = "aws-smithy-async" -version = "1.2.14" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ffcaf626bdda484571968400c326a244598634dc75fd451325a54ad1a59acfc" +checksum = "f02e407fb3b54891734224b9ffac8a71fdd35f542500fa1af95754a6b2beb316" dependencies = [ "futures-util", "pin-project-lite", @@ -1795,9 +1800,9 @@ dependencies = [ [[package]] name = "aws-smithy-eventstream" -version = "0.60.21" +version = "0.61.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78d8391e65fcea47c586a22e1a41f173b38615b112b2c6b7a44e80cec3e6b706" +checksum = "5a9381123ab62d20c13082b151f30f962a3b112b727345394536dfa39a482944" dependencies = [ "aws-smithy-types", "bytes", @@ -1806,9 +1811,9 @@ dependencies = [ [[package]] name = "aws-smithy-http" -version = "0.63.6" +version = "0.64.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1ab2dc1c2c3749ead27180d333c42f11be8b0e934058fb4b2258ee8dbe5231" +checksum = "37843d9add67c3aff5856f409c6dc315d3cdff60f9c0cb5b670dab1e9920306d" dependencies = [ "aws-smithy-eventstream", "aws-smithy-runtime-api", @@ -1818,7 +1823,7 @@ dependencies = [ "futures-core", "futures-util", "http 1.4.2", - "http-body 1.0.1", + "http-body 1.1.0", "http-body-util", "percent-encoding", "pin-project-lite", @@ -1828,9 +1833,9 @@ dependencies = [ [[package]] name = "aws-smithy-http-client" -version = "1.1.13" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c3ef8931ad1c98aa6a55b4256f847f3116090819844e0dd41ea682cac5dd2d3" +checksum = "635d23afda0a6ab48d666c4d447c4873e8d1e83518a2be2093122397e50b838e" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api", @@ -1852,9 +1857,9 @@ dependencies = [ [[package]] name = "aws-smithy-json" -version = "0.62.7" +version = "0.63.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "701a947f4797e52a911e114a898667c746c39feea467bbd1abd7b3721f702ffa" +checksum = "3dc65a121adb4b33729919fcfa14fa36fb33c1555a8f06bb0e2188dbfdc1d9ef" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-schema", @@ -1863,28 +1868,31 @@ dependencies = [ [[package]] name = "aws-smithy-observability" -version = "0.2.6" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06c2315d173edbf1920da8ba3a7189695827002e4c0fc961973ab1c54abca9c" +checksum = "8e86338c869539a581bf161247762a6e87f92c5c075060057b5ed6d06632ed0c" dependencies = [ "aws-smithy-runtime-api", ] [[package]] name = "aws-smithy-query" -version = "0.60.15" +version = "0.62.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a56d79744fb3edb5d722ef79d86081e121d3b9422cb209eb03aea6aa4f21ebd" +checksum = "512346c7212ab7436df2d77a16d976a468ae44a418835511d2a69269810aaf62" dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-schema", "aws-smithy-types", + "aws-smithy-xml", "urlencoding", ] [[package]] name = "aws-smithy-runtime" -version = "1.11.3" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e6f5caf6fea86f8c2206541ab5857cfcda9013426cdbe8fa0098b9e2d32182" +checksum = "bea94a9ff8464016338c851e24b472d7131c388c88898a502e781815b2ee6045" dependencies = [ "aws-smithy-async", "aws-smithy-http", @@ -1894,11 +1902,11 @@ dependencies = [ "aws-smithy-schema", "aws-smithy-types", "bytes", - "fastrand 2.4.1", + "fastrand 2.5.0", "http 0.2.12", "http 1.4.2", "http-body 0.4.6", - "http-body 1.0.1", + "http-body 1.1.0", "http-body-util", "pin-project-lite", "pin-utils", @@ -1908,9 +1916,9 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api" -version = "1.12.3" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9db177daa6ba8afb9ee1aefcf548c907abcf52065e394ee11a92780057fe0e8c" +checksum = "22ed1ebe6e0a95ea84570225f5a8208dec4b8f77e61a9b0d6f51773fcb4612f0" dependencies = [ "aws-smithy-async", "aws-smithy-runtime-api-macros", @@ -1926,20 +1934,20 @@ dependencies = [ [[package]] name = "aws-smithy-runtime-api-macros" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d7396fd9500589e62e460e987ecb671bad374934e55ec3b5f498cc7a8a8a7b7" +checksum = "221eaa237ddf1ca79b60d1372aad77e47f9c0ea5b3ce5099da8c61d027dc77b3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "aws-smithy-schema" -version = "0.1.0" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7442cb268338f0eb8278140a107c046756aa01093d8ef5e99628d34ae09c94f5" +checksum = "7d56e0a4e53127a632224e43633b0fe045fa9e1e3cfc68b9830f1115e103f910" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", @@ -1948,9 +1956,9 @@ dependencies = [ [[package]] name = "aws-smithy-types" -version = "1.5.0" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32b42fcf341259d85ca10fac9a2f6448a8ec691c6955a18e45bc3b71a85fab85" +checksum = "d6dc683efb34b9e755675b37fedbe0103141e5b6df7bdc9eb6967756a8c167d8" dependencies = [ "base64-simd", "bytes", @@ -1959,7 +1967,7 @@ dependencies = [ "http 0.2.12", "http 1.4.2", "http-body 0.4.6", - "http-body 1.0.1", + "http-body 1.1.0", "http-body-util", "itoa", "num-integer", @@ -1974,18 +1982,21 @@ dependencies = [ [[package]] name = "aws-smithy-xml" -version = "0.60.15" +version = "0.62.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ce02add1aa3677d022f8adf81dcbe3046a95f17a1b1e8979c145cd21d3d22b3" +checksum = "ce84f71c72fee2cbbadde6e7d082f5fb466e3a84733855295fa7aafd1b31b7d8" dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-schema", + "aws-smithy-types", "xmlparser", ] [[package]] name = "aws-types" -version = "1.3.16" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16bf10b03a3c01e6b3b7d47cd964e873ffe9e7d4e80fad16bd4c077cb068531" +checksum = "eec1cd5469f328c782dc3e33d4153cf118a54e33cbb3356d60d16f89883e1f94" dependencies = [ "aws-credential-types", "aws-smithy-async", @@ -2007,7 +2018,7 @@ dependencies = [ "form_urlencoded", "futures-util", "http 1.4.2", - "http-body 1.0.1", + "http-body 1.1.0", "http-body-util", "hyper", "hyper-util", @@ -2038,7 +2049,7 @@ dependencies = [ "bytes", "futures-core", "http 1.4.2", - "http-body 1.0.1", + "http-body 1.1.0", "http-body-util", "mime", "pin-project-lite", @@ -2059,7 +2070,7 @@ dependencies = [ "bytes", "futures-util", "http 1.4.2", - "http-body 1.0.1", + "http-body 1.1.0", "http-body-util", "mime", "pin-project-lite", @@ -2146,7 +2157,7 @@ version = "0.69.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "271383c67ccabffb7381723dea0672a673f292304fcb45c01cc648c7a8d58088" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "cexpr", "clang-sys", "itertools 0.12.1", @@ -2159,7 +2170,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex 1.3.0", - "syn 2.0.118", + "syn 2.0.119", "which", ] @@ -2169,7 +2180,7 @@ version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "cexpr", "clang-sys", "itertools 0.13.0", @@ -2180,7 +2191,7 @@ dependencies = [ "regex", "rustc-hash 1.1.0", "shlex 1.3.0", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -2253,9 +2264,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", ] @@ -2266,7 +2277,7 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b64e60c28b6d25ad92e8b367801ff9aa12b41d05fc8798055d296bace4a60cc" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "serde", ] @@ -2386,7 +2397,7 @@ dependencies = [ "proc-macro2", "quote", "rustversion", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -2397,26 +2408,26 @@ checksum = "dc0b364ead1874514c8c2855ab558056ebfeb775653e7ae45ff72f28f8f3166c" [[package]] name = "borsh" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f3f6da4992df95bbcd9af42a6c7dcb994498fc9048230405f3b36ff7cd3f145" +checksum = "a88b7ea17d208c4193f2c1e6de3c35fe71f98c96982d5ced308bdcc749ff6e1f" dependencies = [ "borsh-derive", "bytes", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", ] [[package]] name = "borsh-derive" -version = "1.7.0" +version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ae8fb4fb5740e4b2c4884ff95f5f32f5e8479db1e8fd8eb49ddbe09eb09bb7c" +checksum = "d8f347189c62a579b8cd5f80714efa178f52e461dc2e6d701d264f5ff22e566c" dependencies = [ "once_cell", "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -2448,9 +2459,9 @@ dependencies = [ [[package]] name = "bstr" -version = "1.12.3" +version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cee35f73844aa3014bb606320a6c1f010249dbdf43342fe54b5a4f6a8ed4b79" +checksum = "1f7dc094d718f2e1c1559ad110e27eeaae14a5465d3d56dd6dbd793079fbd530" dependencies = [ "memchr", "regex-automata", @@ -2529,7 +2540,7 @@ checksum = "89385e82b5d1821d2219e0b095efa2cc1f246cbf99080f3be46a1a85c0d392d9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -2540,22 +2551,22 @@ checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" [[package]] name = "bytemuck" -version = "1.25.0" +version = "1.25.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +checksum = "95832e849adfb21180ccb6826a99da14e5d266ae5c2e668e1602cf234f153797" dependencies = [ "bytemuck_derive", ] [[package]] name = "bytemuck_derive" -version = "1.10.2" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +checksum = "f65693059b6b9c588b9f62fed1cedbf0a8b805631457ea162d68f0de186f3de5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -2572,9 +2583,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", ] @@ -2623,7 +2634,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "log", "polling", "rustix 0.38.44", @@ -2663,11 +2674,11 @@ dependencies = [ "num_cpus", "objc2-foundation 0.3.2", "objc2-metal 0.3.2", - "rand 0.9.4", + "rand 0.9.5", "rand_distr", "rayon", "safetensors 0.7.0", - "thiserror 2.0.18", + "thiserror 2.0.19", "yoke 0.8.3", "zip 7.2.0", ] @@ -2692,7 +2703,7 @@ dependencies = [ "objc2-foundation 0.3.2", "objc2-metal 0.3.2", "once_cell", - "thiserror 2.0.18", + "thiserror 2.0.19", "tracing", ] @@ -2711,7 +2722,7 @@ dependencies = [ "rayon", "safetensors 0.7.0", "serde", - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] @@ -2737,7 +2748,7 @@ dependencies = [ "candle-nn", "fancy-regex 0.17.0", "num-traits", - "rand 0.9.4", + "rand 0.9.5", "rayon", "serde", "serde_json", @@ -2782,9 +2793,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.65" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e228eec9be7c17ccb640b59b36a5cd805ea2a564a4c5e162c2f659fea30d3b96" +checksum = "c89588d05638b5b4594a3348a2d6c20277e43a7f5c5202b05cc56888475a47b8" dependencies = [ "find-msvc-tools", "jobserver", @@ -2838,9 +2849,9 @@ checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" [[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" @@ -2853,6 +2864,17 @@ dependencies = [ "cpufeatures 0.2.17", ] +[[package]] +name = "chacha20" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", +] + [[package]] name = "chacha20poly1305" version = "0.10.1" @@ -2860,7 +2882,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ "aead", - "chacha20", + "chacha20 0.9.1", "cipher", "poly1305", "zeroize", @@ -2945,9 +2967,9 @@ dependencies = [ [[package]] name = "clap" -version = "4.6.1" +version = "4.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ddb117e43bbf7dacf0a4190fef4d345b9bad68dfc649cb349e7d17d28428e51" +checksum = "d91e0c145792ef73a6ad36d27c75ac09f1832222a3c209689d90f534685ee5b7" dependencies = [ "clap_builder", "clap_derive", @@ -2955,9 +2977,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.6.0" +version = "4.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714a53001bf66416adb0e2ef5ac857140e7dc3a0c48fb28b2f10762fc4b5069f" +checksum = "f09628afdcc538b57f3c6341e9c8e9970f18e4a481690a64974d7023bd33548b" dependencies = [ "anstream", "anstyle", @@ -2976,14 +2998,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.3", ] [[package]] @@ -3086,7 +3108,7 @@ dependencies = [ "serde", "session-sharing-protocol", "settings_value", - "thiserror 2.0.18", + "thiserror 2.0.19", "uuid", "warp_server_auth", ] @@ -3139,7 +3161,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f79398230a6e2c08f5c9760610eb6924b52aa9e7950a619602baba59dcbbdbb2" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block", "cocoa-foundation 0.2.1", "core-foundation 0.10.1", @@ -3169,7 +3191,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "81411967c50ee9a1fc11365f8c585f863a22a9697c89239c452292c40ba79b0d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block", "core-foundation 0.10.1", "core-graphics-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -3215,7 +3237,7 @@ dependencies = [ "nom 7.1.3", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -3271,7 +3293,7 @@ dependencies = [ "libc", "log", "tempfile", - "thiserror 2.0.18", + "thiserror 2.0.19", "win32job", "windows 0.62.2", ] @@ -3295,7 +3317,7 @@ version = "0.1.0" dependencies = [ "anyhow", "command", - "rust-embed 8.11.0", + "rust-embed 8.12.0", ] [[package]] @@ -3338,7 +3360,7 @@ dependencies = [ "ashpd", "async-trait", "cfg-if", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "clap", "command", "computer_use", @@ -3370,7 +3392,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f76990911f2267d837d9d0ad060aa63aaad170af40904b29461734c339030d4d" dependencies = [ "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -3499,7 +3521,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "core-foundation 0.10.1", "core-graphics-types 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.5.0", @@ -3511,7 +3533,7 @@ name = "core-graphics" version = "0.25.0" source = "git+https://github.com/servo/core-foundation-rs?rev=6f844cf1a1a18e25b70fcdf1bcdc458555bd2eff#6f844cf1a1a18e25b70fcdf1bcdc458555bd2eff" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "core-foundation 0.10.1", "core-graphics-types 0.2.0 (git+https://github.com/servo/core-foundation-rs?rev=6f844cf1a1a18e25b70fcdf1bcdc458555bd2eff)", "foreign-types 0.5.0", @@ -3535,7 +3557,7 @@ 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", ] @@ -3573,7 +3595,7 @@ version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7d5d7dca3ebcf65a035582c9ad4385371a9d9ee6537474d2a278f4e1e475bb58" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "libc", "objc2-audio-toolbox", "objc2-core-audio", @@ -3586,7 +3608,7 @@ name = "cosmic-text" version = "0.12.0" source = "git+https://github.com/warpdotdev/cosmic-text.git?rev=15198beba692162201c0ea8b15222cf5643ea068#15198beba692162201c0ea8b15222cf5643ea068" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "fontdb", "log", "rangemap", @@ -3761,18 +3783,18 @@ checksum = "790eea4361631c5e7d22598ecd5723ff611904e3344ce8720784c93e3d83d40b" [[package]] name = "crossbeam-channel" -version = "0.5.15" +version = "0.5.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +checksum = "d85363c37faeca707aef026efa9f3b34d077bce547e48f770770625c6013679e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +checksum = "5181e0de7b61eb03a81e347d6dd8797bae9da5146707b51077e2d71a54ec0ceb" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -3780,18 +3802,18 @@ dependencies = [ [[package]] name = "crossbeam-epoch" -version = "0.9.18" +version = "0.9.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +checksum = "2d6914041f254d6e9176c01941b21115dcfb7089e55135a35411081bd106ef3f" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" -version = "0.8.21" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "61803da095bee82a81bb1a452ecc25d3b2f1416d1897eb86430c6159ef717c17" [[package]] name = "crossterm" @@ -3799,7 +3821,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "crossterm_winapi", "derive_more 2.1.1", "document-features", @@ -3987,7 +4009,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4001,9 +4023,9 @@ dependencies = [ [[package]] name = "cynic" -version = "3.13.2" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2e722e1560ed4e4260cf35ac5fafc422aa7682c919db5a14209e4afad4446c2" +checksum = "6260314e64d59ff1a962d465573ac6775fbf4624aa67613e220eb61f4a3d9a5c" dependencies = [ "cynic-proc-macros", "ref-cast", @@ -4014,9 +4036,9 @@ dependencies = [ [[package]] name = "cynic-codegen" -version = "3.13.2" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fde92ad70b36e9794eab14cbe915ae33e0ee8013844b7dcacad686830e107a5f" +checksum = "04e68e191fc65c3b8a5b467368ef8e3d5f610ef770df664d5cca51de573c3010" dependencies = [ "cynic-parser", "darling 0.20.11", @@ -4026,15 +4048,15 @@ dependencies = [ "quote", "rkyv 0.8.17", "strsim 0.10.0", - "syn 2.0.118", + "syn 2.0.119", "thiserror 1.0.69", ] [[package]] name = "cynic-parser" -version = "0.11.1" +version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4dfae2e0e25c72720197bde791ef9b840562ed6418eab6d8505f1d8fed27e86" +checksum = "7793e157a59a0bd2142a5723860aeaef3aa459feae1467b597351e5f58251904" dependencies = [ "indexmap 2.14.0", "lalrpop-util", @@ -4043,14 +4065,14 @@ dependencies = [ [[package]] name = "cynic-proc-macros" -version = "3.13.2" +version = "3.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64118c37832e4ff3e2b8ffaf2f3a08d1b646fa049e90ff54826abac01005ae11" +checksum = "8ca74b7363c9c675b6539aaf151c695cac83d3e7abc80dfbfbfdbfef27e4ec20" dependencies = [ "cynic-codegen", "darling 0.20.11", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4103,7 +4125,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4117,7 +4139,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4130,7 +4152,7 @@ dependencies = [ "proc-macro2", "quote", "strsim 0.11.1", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4141,7 +4163,7 @@ checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" dependencies = [ "darling_core 0.20.11", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4152,7 +4174,7 @@ checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" dependencies = [ "darling_core 0.21.3", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4163,7 +4185,7 @@ checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ "darling_core 0.23.0", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4240,9 +4262,9 @@ checksum = "ac6b926516df9c60bfa16e107b21086399f8285a44ca9711344b9e553c5146e2" [[package]] name = "defmt" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6e524506490a1953d237cb87b1cfc1e46f88c18f10a22dfe0f507dc6bfc7f7f" +checksum = "e2953bfe4f93bbd20cc71198842756f77d161884c99ebbabc41d80231ded88d1" dependencies = [ "bitflags 1.3.2", "defmt-macros", @@ -4250,15 +4272,14 @@ dependencies = [ [[package]] name = "defmt-macros" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0a27770e9c8f719a79d8b638281f4d828f77d8fd61e0bd94451b9b85e576a0b" +checksum = "bad9c72e7ca2137e0dc3813245a0d282fd6daad32fd800af018306a9169b5fe8" dependencies = [ "defmt-parser", - "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4267,7 +4288,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.19", ] [[package]] @@ -4289,9 +4310,9 @@ dependencies = [ [[package]] name = "der" -version = "0.8.0" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71fd89660b2dc699704064e59e9dba0147b903e85319429e131620d022be411b" +checksum = "a69dedd701da44b0536442edf09c81a64b0ab97a7a4a5e3d1971f00027cbc63d" dependencies = [ "pem-rfc7468 1.0.0", "zeroize", @@ -4336,7 +4357,7 @@ checksum = "d08b3a0bcc0d079199cd476b2cae8435016ec11d1c0986c6901c5ac223041534" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4347,7 +4368,7 @@ checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4368,7 +4389,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4378,7 +4399,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" dependencies = [ "derive_builder_core", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4391,7 +4412,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4413,7 +4434,7 @@ dependencies = [ "proc-macro2", "quote", "rustc_version", - "syn 2.0.118", + "syn 2.0.119", "unicode-xid", ] @@ -4435,9 +4456,9 @@ dependencies = [ [[package]] name = "diesel" -version = "2.3.10" +version = "2.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29fe29a87fb84c631ffb3ba21798c4b1f3a964701ba78f0dce4bf8668562ec88" +checksum = "e54d1f576cd3a3460f212a4615fd12ce1b6303c095b79a44449ffbe627753dc1" dependencies = [ "chrono", "diesel_derives", @@ -4457,7 +4478,7 @@ dependencies = [ "dsl_auto_type", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4477,7 +4498,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fe2444076b48641147115697648dc743c2c00b61adade0f01ce67133c7babe8c" dependencies = [ - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4601,7 +4622,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 0.6.2", "libc", "objc2 0.6.4", @@ -4615,7 +4636,7 @@ checksum = "1ac70aa55017e108007fbaf5aa0f54b021c98f92ff8af59d42eda9da96e3dd4f" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4681,7 +4702,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4790,7 +4811,7 @@ dependencies = [ "cc", "memchr", "rustc_version", - "toml 1.1.2+spec-1.1.0", + "toml 1.1.3+spec-1.1.0", "vswhom", "winreg", ] @@ -4846,7 +4867,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4866,7 +4887,7 @@ checksum = "685adfa4d6f3d765a26bc5dbc936577de9abf756c1feeb3089b01dd395034842" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4887,7 +4908,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -4920,7 +4941,7 @@ checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -5008,14 +5029,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 0.22.3", "rayon-core", "smallvec", "zune-inflate", @@ -5076,9 +5099,9 @@ dependencies = [ [[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" @@ -5120,7 +5143,7 @@ dependencies = [ "prost", "prost-reflect", "prost-types", - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] @@ -5239,7 +5262,7 @@ dependencies = [ "cudarc 0.19.8", "half", "num-traits", - "rand 0.9.4", + "rand 0.9.5", "rand_distr", ] @@ -5295,7 +5318,7 @@ name = "font-kit" version = "0.12.0" source = "git+https://github.com/warpdotdev/font-kit.git?rev=a04b225ecb639bb850f21d05212cfdc7465000f3#a04b225ecb639bb850f21d05212cfdc7465000f3" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "byteorder", "core-foundation 0.10.1", "core-graphics 0.25.0", @@ -5382,7 +5405,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -5502,9 +5525,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" +checksum = "a88cf1f829d945f548cf8fec32c61b1f202b6d93b45848602fc02af4b12ad218" dependencies = [ "futures-channel", "futures-core", @@ -5517,9 +5540,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" +checksum = "262590f4fe6afeb0bc83be1daa64e52657fe185690a958af7f3ad0e92085c5ae" dependencies = [ "futures-core", "futures-sink", @@ -5527,15 +5550,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" +checksum = "2cd50c473c80f6d7c3670a752354b8e569b1a7cbfdc0419ec88e5edad85e0dc7" [[package]] name = "futures-executor" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" +checksum = "6754879cc9f2c66f88c6e5c35344bb0bdb0708b0352b1201815667c7eabc7458" dependencies = [ "futures-core", "futures-task", @@ -5544,9 +5567,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" +checksum = "4577ecaa3c4f96589d473f679a71b596316f6641bc350038b962a5daf0085d7a" [[package]] name = "futures-lite" @@ -5569,7 +5592,7 @@ version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" dependencies = [ - "fastrand 2.4.1", + "fastrand 2.5.0", "futures-core", "futures-io", "parking", @@ -5578,26 +5601,26 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" +checksum = "2d6d3cde68c518367be28956066ddfef33813991b77a55005a69dae04bf3b10b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "futures-sink" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" +checksum = "e34418ac499d6305c2fb5ad0ed2f6ac998c5f8ca209b4510f7f94242c647e307" [[package]] name = "futures-task" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" +checksum = "b231ed28831efb4a61a08580c4bc233ec56bc009f4cd8f52da2c3cb97df0c109" [[package]] name = "futures-test-sink" @@ -5620,9 +5643,9 @@ dependencies = [ [[package]] name = "futures-util" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" +checksum = "a77a90a256fce34da66415271e30f94ee91c57b04b8a2c042d9cf3220179deaa" dependencies = [ "futures-channel", "futures-core", @@ -5685,7 +5708,7 @@ dependencies = [ "base64 0.22.1", "bimap", "bincode", - "bitflags 2.13.0", + "bitflags 2.13.1", "bitflags-serde-legacy", "block", "blocking", @@ -5696,7 +5719,7 @@ dependencies = [ "bytes", "cc", "cfg-if", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "channel_versions", "chrono", "clap", @@ -5831,10 +5854,6 @@ dependencies = [ "objc2-foundation 0.3.2", "onboarding", "once_cell", - "opentelemetry", - "opentelemetry-http", - "opentelemetry-otlp", - "opentelemetry_sdk", "ordered-float 3.9.2", "os_info", "palette 0.6.1", @@ -5850,7 +5869,7 @@ dependencies = [ "prost-build", "prost-types", "qrcode", - "rand 0.8.6", + "rand 0.8.7", "rangemap", "rayon", "regex", @@ -5860,7 +5879,7 @@ dependencies = [ "reqwest 0.13.4", "rmcp", "rquickjs", - "rust-embed 8.11.0", + "rust-embed 8.12.0", "rustls", "schemars", "security-framework-sys", @@ -5900,16 +5919,15 @@ dependencies = [ "sysinfo", "tabwriter", "tempfile", - "thiserror 2.0.18", + "thiserror 2.0.19", "thousands", "tikv-jemallocator", "tokio", "tokio-util", "toml 0.8.23", - "toml_edit 0.25.12+spec-1.1.0", + "toml_edit 0.25.13+spec-1.1.0", "tracing", "tracing-futures", - "tracing-opentelemetry", "tracing-subscriber", "typed-path 0.10.0", "ui_components", @@ -5958,7 +5976,7 @@ version = "0.0.0" dependencies = [ "anyhow", "cfg-if", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "chrono", "clap", "clap_complete", @@ -6010,7 +6028,7 @@ dependencies = [ "shellexpand", "smol_str", "string-offset", - "thiserror 2.0.18", + "thiserror 2.0.19", "typed-path 0.10.0", "warp-command-signatures", ] @@ -6047,7 +6065,7 @@ dependencies = [ "objc2-foundation 0.3.2", "parking_lot", "pin-project", - "rand 0.8.6", + "rand 0.8.7", "regex", "reqwest 0.13.4", "schemars", @@ -6065,7 +6083,7 @@ dependencies = [ "strum_macros 0.27.2", "sysinfo", "tempfile", - "thiserror 2.0.18", + "thiserror 2.0.19", "tokio", "tracing", "url", @@ -6107,17 +6125,17 @@ dependencies = [ "ordered-float 3.9.2", "parking_lot", "pathfinder_color", - "rand 0.8.6", + "rand 0.8.7", "rangemap", "rayon", "regex-automata", "regex-syntax", - "rust-embed 8.11.0", + "rust-embed 8.12.0", "serde", "serde_yaml 0.8.26", "string-offset", "sum_tree", - "thiserror 2.0.18", + "thiserror 2.0.19", "unicode-width 0.1.14", "urlocator", "vec1", @@ -6147,7 +6165,7 @@ dependencies = [ "remote_server", "repo_metadata", "tempfile", - "thiserror 2.0.18", + "thiserror 2.0.19", "watcher", ] @@ -6172,7 +6190,7 @@ dependencies = [ "persistence", "reqwest 0.13.4", "serde", - "thiserror 2.0.18", + "thiserror 2.0.19", "websocket", "ws_stream_wasm", ] @@ -6198,7 +6216,7 @@ dependencies = [ "log", "serde", "serde_json", - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] @@ -6209,7 +6227,7 @@ dependencies = [ "cfg-if", "rquickjs", "serde", - "thiserror 2.0.18", + "thiserror 2.0.19", "uuid", ] @@ -6251,11 +6269,11 @@ dependencies = [ "getrandom 0.3.4", "hpke", "log", - "rand 0.9.4", + "rand 0.9.5", "serde", "serde_json", "tempfile", - "thiserror 2.0.18", + "thiserror 2.0.19", "tink-core", "tink-hybrid", "tink-proto", @@ -6322,14 +6340,14 @@ dependencies = [ "oauth2", "parking_lot", "pathfinder_geometry", - "rand 0.8.6", + "rand 0.8.7", "reqwest 0.13.4", "schemars", "serde", "serde_json", "session-sharing-protocol", "settings_value", - "thiserror 2.0.18", + "thiserror 2.0.19", "url", "uuid", "warp_server_auth", @@ -6341,7 +6359,7 @@ name = "galaxy_terminal" version = "0.0.0" dependencies = [ "anyhow", - "bitflags 2.13.0", + "bitflags 2.13.1", "bitflags-serde-legacy", "cfg-if", "channel_versions", @@ -6362,7 +6380,7 @@ dependencies = [ "smol_str", "static_assertions", "string-offset", - "thiserror 2.0.18", + "thiserror 2.0.19", "typed-path 0.10.0", "unicode-segmentation", "unicode-width 0.1.14", @@ -6391,11 +6409,11 @@ dependencies = [ "mime_guess", "pathdiff", "pin-project", - "rand 0.8.6", + "rand 0.8.7", "regex", "serde", "serde_json", - "thiserror 2.0.18", + "thiserror 2.0.19", "typed-path 0.10.0", "windows 0.62.2", ] @@ -6427,7 +6445,7 @@ dependencies = [ "bytemuck", "cc", "cfg-if", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "chrono", "cocoa 0.26.0", "command", @@ -6481,14 +6499,14 @@ dependencies = [ "parking_lot", "pathfinder_color", "pathfinder_geometry", - "rand 0.8.6", + "rand 0.8.7", "resvg", - "rust-embed 8.11.0", + "rust-embed 8.12.0", "serde", "sum_tree", "takecell", "tauri-winrt-notification", - "thiserror 2.0.18", + "thiserror 2.0.19", "tini", "url", "urlencoding", @@ -6524,7 +6542,7 @@ dependencies = [ "bounded-vec-deque", "bytes", "cfg-if", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "chrono", "command", "concat-idents", @@ -6559,13 +6577,13 @@ dependencies = [ "parking_lot", "pathfinder_color", "pathfinder_geometry", - "rand 0.8.6", + "rand 0.8.7", "rangemap", "ratatui", "raw-window-handle 0.6.2", "resvg", "rstar", - "rust-embed 8.11.0", + "rust-embed 8.12.0", "rustc-hash 2.1.3", "schemars", "serde", @@ -6579,7 +6597,7 @@ dependencies = [ "strum_macros 0.27.2", "sum_tree", "tempfile", - "thiserror 2.0.18", + "thiserror 2.0.19", "titlecase", "tokio", "tracing", @@ -6602,7 +6620,7 @@ dependencies = [ "objc2 0.6.4", "objc2-foundation 0.3.2", "ouroboros", - "rand 0.8.6", + "rand 0.8.7", "ring", "secret-service", "security-framework 2.11.1", @@ -6610,8 +6628,8 @@ dependencies = [ "serde_json", "sha2 0.10.9", "tempfile", - "thiserror 2.0.18", - "toml_edit 0.25.12+spec-1.1.0", + "thiserror 2.0.19", + "toml_edit 0.25.13+spec-1.1.0", "windows 0.62.2", "windows-registry 0.2.0", "windows-result 0.2.0", @@ -6883,7 +6901,7 @@ checksum = "13a1bcfb855c1f340d5913ab542e36f25a1c56f57de79022928297632435dec2" dependencies = [ "attribute-derive", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -6930,8 +6948,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" dependencies = [ "cfg-if", + "js-sys", "libc", "r-efi 6.0.0", + "rand_core 0.10.1", + "wasm-bindgen", ] [[package]] @@ -6942,7 +6963,7 @@ checksum = "6cf442baaabe4213ce7d1239afc26c039180b6456da2cededa316ae2c8a77a77" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -6997,7 +7018,7 @@ version = "0.20.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b88256088d75a56f8ecfa070513a775dd9107f6530ef14919dac831af9cfe2b" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "libc", "libgit2-sys", "log", @@ -7017,9 +7038,9 @@ 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 = "global-hotkey" @@ -7032,7 +7053,7 @@ dependencies = [ "objc2 0.6.4", "objc2-app-kit 0.3.2", "once_cell", - "thiserror 2.0.18", + "thiserror 2.0.19", "windows-sys 0.59.0", "x11rb", "xkeysym", @@ -7040,9 +7061,9 @@ dependencies = [ [[package]] name = "globset" -version = "0.4.18" +version = "0.4.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" +checksum = "e47d37d2ae4464254884b60ab7071be2b876a9c35b696bd018ddcc76847309cd" dependencies = [ "aho-corasick", "bstr", @@ -7167,7 +7188,7 @@ dependencies = [ "proc-macro-crate 1.3.1", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -7212,7 +7233,7 @@ dependencies = [ "hashbrown 0.16.1", "log", "presser", - "thiserror 2.0.18", + "thiserror 2.0.19", "windows 0.62.2", ] @@ -7222,7 +7243,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "gpu-descriptor-types", "hashbrown 0.15.5", ] @@ -7233,7 +7254,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", ] [[package]] @@ -7294,9 +7315,9 @@ dependencies = [ [[package]] name = "grep-matcher" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "36d7b71093325ab22d780b40d7df3066ae4aebb518ba719d38c697a8228a8023" +checksum = "f9417543f4870fc8f1c8e1af870afae2431007626d9e703fce6471c468d33847" dependencies = [ "memchr", ] @@ -7331,9 +7352,9 @@ dependencies = [ [[package]] name = "grep-searcher" -version = "0.1.16" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac63295322dc48ebb20a25348147905d816318888e64f531bfc2a2bc0577dc34" +checksum = "72348823a0eafc4bc2e9051064f28b5b42cc100b571b3a35d67918d711efcbc6" dependencies = [ "bstr", "encoding_rs", @@ -7384,7 +7405,7 @@ dependencies = [ "cfg-if", "crunchy", "num-traits", - "rand 0.9.4", + "rand 0.9.5", "rand_distr", "zerocopy", ] @@ -7517,11 +7538,11 @@ dependencies = [ "log", "native-tls", "num_cpus", - "rand 0.9.4", + "rand 0.9.5", "reqwest 0.12.28", "serde", "serde_json", - "thiserror 2.0.18", + "thiserror 2.0.19", "tokio", "ureq 2.12.1", "windows-sys 0.60.2", @@ -7614,12 +7635,9 @@ dependencies = [ [[package]] name = "html-escape" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d1ad449764d627e22bfd7cd5e8868264fc9236e07c752972b4080cd351cb476" -dependencies = [ - "utf8-width", -] +checksum = "46c1ff2d1cbf39efe5af0900ced8a069b5e61557a17544eb0c4a50239937389e" [[package]] name = "html5ever" @@ -7672,9 +7690,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 1.4.2", @@ -7682,14 +7700,14 @@ dependencies = [ [[package]] name = "http-body-util" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +checksum = "e9f41fd6a08e4d4ec69df65976da761afd5ad5e58a9d4acb46bd1c953a9e3ff2" dependencies = [ "bytes", "futures-core", "http 1.4.2", - "http-body 1.0.1", + "http-body 1.1.0", "pin-project-lite", ] @@ -7766,9 +7784,9 @@ dependencies = [ [[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", @@ -7776,7 +7794,7 @@ dependencies = [ "futures-core", "h2", "http 1.4.2", - "http-body 1.0.1", + "http-body 1.1.0", "httparse", "httpdate", "itoa", @@ -7829,13 +7847,13 @@ dependencies = [ "futures-channel", "futures-util", "http 1.4.2", - "http-body 1.0.1", + "http-body 1.1.0", "hyper", "ipnet", "libc", "percent-encoding", "pin-project-lite", - "socket2 0.6.4", + "socket2 0.6.5", "system-configuration", "tokio", "tower-service", @@ -7978,9 +7996,9 @@ dependencies = [ [[package]] name = "ignore" -version = "0.4.27" +version = "0.4.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe112b004901c62c2faa11f4f75e9864e0cc5af8da71c9115d184a3aa888749f" +checksum = "7f8a7b8211e695a1d0cd91cace480d4d0bd57667ab10277cc412c5f7f4884f83" dependencies = [ "crossbeam-deque", "globset", @@ -8048,9 +8066,9 @@ checksum = "89194689a993ab15268672e99e7b0e19da2da3268ac682e8f02d29d4d1434cd7" [[package]] name = "impl-more" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35a84fd5aa25fae5c0f4a33d9cac2ca017fc622cbd089be2229993514990f870" +checksum = "134d2c4324d61664107020b79019cf6a6aec153f0b79bc9619ee9e794a5fb021" [[package]] name = "indexmap" @@ -8108,20 +8126,20 @@ dependencies = [ [[package]] name = "inotify" -version = "0.11.2" +version = "0.11.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "533e68a5842e734946fe159fb03fc9bbbb254f590dd0d8ad321ae5ff7beca2c1" +checksum = "153be1941a183ec9ccd095ddbe17a8b8d435ef6c76e9e02451b933c3999af2c8" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "inotify-sys", "libc", ] [[package]] name = "inotify-sys" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea94e891b3606826e9c998be69ddca42247dad8ad50b1649a5cb7e1c9ae06fd" +checksum = "c033f80b2c113cdf91ab7a33faa9cbc014726dcad99880c8609af2a370edf37d" dependencies = [ "libc", ] @@ -8160,7 +8178,7 @@ dependencies = [ "ort", "parking_lot", "prost", - "rust-embed 8.11.0", + "rust-embed 8.12.0", "serde", "smol_str", "tokenizers", @@ -8172,7 +8190,7 @@ version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6654738b8024300cf062d04a1c13c10c8e2cea598ec1c47dc9b6641159429756" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "crossterm", "dyn-clone", "fuzzy-matcher", @@ -8190,7 +8208,7 @@ dependencies = [ "indoc", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -8233,9 +8251,9 @@ dependencies = [ "nix 0.26.4", "parking_lot", "pathfinder_geometry", - "rand 0.8.6", + "rand 0.8.7", "regex", - "rust-embed 8.11.0", + "rust-embed 8.12.0", "serde", "serde_json", "settings", @@ -8259,7 +8277,7 @@ checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -8318,9 +8336,9 @@ dependencies = [ "itertools 0.14.0", "log", "parking_lot", - "rand 0.8.6", + "rand 0.8.7", "serde", - "thiserror 2.0.18", + "thiserror 2.0.19", "uuid", ] @@ -8337,7 +8355,7 @@ dependencies = [ "markdown_parser", "serde", "serde_json", - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] @@ -8550,11 +8568,12 @@ dependencies = [ [[package]] name = "jiff" -version = "0.2.31" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccfe6121cbe750cf81efa362d85c0bde7ea298ec43092d3a193baca59cdbd634" +checksum = "e184d09547b80eb7e20d141ba2fb1fbac843ca53f4cf1b31210adc4c1adc6e16" dependencies = [ "defmt", + "jiff-core", "jiff-static", "jiff-tzdb-platform", "log", @@ -8565,21 +8584,31 @@ dependencies = [ ] [[package]] -name = "jiff-static" -version = "0.2.31" +name = "jiff-core" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e165e897f662d428f3cd3828a919dbe067c2d42bb1031eede74ef9d27ecdedd2" +checksum = "7feca88439efe53da3754500c1851dedf3cb36c524dd5cf8225cc0794de95d09" dependencies = [ + "defmt", +] + +[[package]] +name = "jiff-static" +version = "0.2.34" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "323da076b7a6faf914dc677cb05a4b907742ff7375c8322c9e7f5061e5e0e9de" +dependencies = [ + "jiff-core", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "jiff-tzdb" -version = "0.1.7" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6142247df1a93c2b3587402a19710be3e6e942f1581a1702e76408f2c21d6590" +checksum = "142bd39932ad231f10513df9ab62661fead8719872150b7ad02a2df79f4e141e" [[package]] name = "jiff-tzdb-platform" @@ -8618,7 +8647,7 @@ dependencies = [ "jni-sys 0.4.1", "log", "simd_cesu8", - "thiserror 2.0.18", + "thiserror 2.0.19", "walkdir", "windows-link 0.2.1", ] @@ -8633,7 +8662,7 @@ dependencies = [ "quote", "rustc_version", "simd_cesu8", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -8661,16 +8690,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" dependencies = [ "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "jobserver" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" +checksum = "1c00acbd29eabad4a2392fa0e921c874934dbbf4194312ad20f04a0ed67a3cb3" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.3", "libc", ] @@ -8734,7 +8763,7 @@ checksum = "bde5057d6143cc94e861d90f591b9303d6716c6b9602309150bd068853c10899" dependencies = [ "hashbrown 0.16.1", "portable-atomic", - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] @@ -8743,7 +8772,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", ] @@ -8772,7 +8801,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1d9f1da1000eb32cea537203e5ea121eb06e66c5e5f3420a1e9f11a57676f55b" dependencies = [ "palette 0.6.1", - "rand 0.8.6", + "rand 0.8.7", "rand_chacha 0.3.1", ] @@ -8792,7 +8821,7 @@ version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07293a4e297ac234359b510362495713f75ea345d5307140414f20c69ffeb087" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "libc", ] @@ -8837,7 +8866,7 @@ dependencies = [ "galaxy_editor", "galaxy_util", "lazy_static", - "rust-embed 8.11.0", + "rust-embed 8.12.0", "serde", "serde_yaml 0.8.26", ] @@ -8878,9 +8907,9 @@ checksum = "0c2cdeb66e45e9f36bfad5bbdb4d2384e70936afbee843c6f6543f0c551ebb25" [[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 = "libfuzzer-sys" @@ -8936,7 +8965,7 @@ version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c943259e342f1e06ff2da7a83eabdfe7f92ce10262688dbf1895ff0b3e6e4652" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "libc", "plain", "redox_syscall 0.9.0", @@ -8971,7 +9000,7 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f50e8f47623268b5407192d26876c4d7f89d686ca130fdc53bced4814cd29f8" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", ] [[package]] @@ -9030,12 +9059,12 @@ dependencies = [ "chrono", "command", "libc", - "rand 0.8.6", + "rand 0.8.7", "reqwest 0.13.4", "serde", "serde_json", "tempfile", - "thiserror 2.0.18", + "thiserror 2.0.19", "uuid", ] @@ -9122,7 +9151,7 @@ dependencies = [ "proc-macro2", "quote", "regex-syntax", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -9154,9 +9183,9 @@ dependencies = [ [[package]] name = "lru" -version = "0.18.0" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a860605968fce16869fd239cf4237a82f3ac470723415db603b0e8b6c8d4fb9" +checksum = "0b6180140927ee907000b0aa540091f6ea512ead4447c92b8fc35bc72788a5a6" dependencies = [ "hashbrown 0.17.1", ] @@ -9365,7 +9394,7 @@ dependencies = [ "markup5ever_rcdom", "nom 7.1.3", "serde_yaml 0.8.26", - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] @@ -9399,7 +9428,7 @@ checksum = "ac84fd3f360fcc43dc5f5d186f02a94192761a080e8bc58621ad4d12296a58cf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -9419,9 +9448,9 @@ checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" [[package]] name = "matrixmultiply" -version = "0.3.10" +version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" +checksum = "3f607c237553f086e7043417a51df26b2eb899d3caff94e6a67592ff992fedc7" dependencies = [ "autocfg", "rawpointer", @@ -9460,7 +9489,7 @@ dependencies = [ "serde_json", "simple_logger", "sse-stream", - "thiserror 2.0.18", + "thiserror 2.0.19", "tokio", "tracing", "url", @@ -9479,9 +9508,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 = "memmap" @@ -9553,7 +9582,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block", "core-graphics-types 0.1.3", "foreign-types 0.5.0", @@ -9605,7 +9634,7 @@ version = "0.21.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c4d14bcca0fd3ed165a03000480aaa364c6860c34e900cb2dafdf3b95340e77" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "debugid", "num-derive", "num-traits", @@ -9620,7 +9649,7 @@ version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e2abcd9c8a1e6e1e9d56ce3627851f39a17ea83e17c96bc510f29d7e43d78a7d" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "byteorder", "cfg-if", "crash-context", @@ -9700,9 +9729,9 @@ checksum = "c505b3e17ed6b70a7ed2e67fbb2c560ee327353556120d6e72f5232b6880d536" [[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", "log", @@ -9733,7 +9762,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -9747,13 +9776,13 @@ dependencies = [ "colored", "futures-core", "http 1.4.2", - "http-body 1.0.1", + "http-body 1.1.0", "http-body-util", "hyper", "hyper-util", "log", "pin-project-lite", - "rand 0.9.4", + "rand 0.9.5", "regex", "serde_json", "serde_urlencoded", @@ -9780,7 +9809,7 @@ checksum = "e4db6d5580af57bf992f59068d4ea26fd518574ff48d7639b255a36f9de6e7e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -9816,7 +9845,7 @@ checksum = "4568f25ccbd45ab5d5603dc34318c1ec56b117531781260002151b8530a9f931" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -9833,9 +9862,9 @@ checksum = "b2bf919621e7975acb27d881bae2fb993e0d45c8e0446e85e6272971e00dc8df" dependencies = [ "arrayvec", "bit-set 0.9.1", - "bitflags 2.13.0", + "bitflags 2.13.1", "cfg-if", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "codespan-reporting", "half", "hashbrown 0.16.1", @@ -9847,7 +9876,7 @@ dependencies = [ "once_cell", "rustc-hash 1.1.0", "spirv", - "thiserror 2.0.18", + "thiserror 2.0.19", "unicode-ident", ] @@ -9945,7 +9974,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", @@ -9993,7 +10022,7 @@ version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "cfg-if", "cfg_aliases 0.1.1", "libc", @@ -10005,9 +10034,9 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "cfg-if", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "libc", "memoffset 0.9.1", ] @@ -10018,9 +10047,9 @@ 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 0.2.1", + "cfg_aliases 0.2.2", "libc", ] @@ -10085,7 +10114,7 @@ name = "notify" version = "8.0.0" source = "git+https://github.com/warpdotdev/notify?rev=91b719849bc04ef251bcb2cc61076b099204e970#91b719849bc04ef251bcb2cc61076b099204e970" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "filetime", "fsevent-sys", "inotify", @@ -10163,9 +10192,9 @@ dependencies = [ [[package]] name = "num-bigint" -version = "0.4.7" +version = "0.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c863e9ab5e7bf9c99ba75e1050f1e4d624ae87ed3532d6238ffbdc7b585dbbe6" +checksum = "c89e69e7e0f03bea5ef08013795c25018e101932225a656383bd384495ecc367" dependencies = [ "num-integer", "num-traits", @@ -10201,7 +10230,7 @@ checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -10215,11 +10244,10 @@ dependencies = [ [[package]] name = "num-iter" -version = "0.1.45" +version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +checksum = "c92800bd69a1eac91786bcfe9da64a897eb72911b8dc3095decbd07429e8048b" dependencies = [ - "autocfg", "num-integer", "num-traits", ] @@ -10274,7 +10302,7 @@ dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -10302,7 +10330,7 @@ dependencies = [ "chrono", "getrandom 0.2.17", "http 1.4.2", - "rand 0.8.6", + "rand 0.8.7", "serde", "serde_json", "serde_path_to_error", @@ -10361,7 +10389,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2 0.5.1", "libc", "objc2 0.5.2", @@ -10377,7 +10405,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", "objc2 0.6.4", "objc2-core-foundation", "objc2-core-graphics", @@ -10391,7 +10419,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6948501a91121d6399b79abaa33a8aa4ea7857fe019f341b8c23ad6e81b79b08" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "libc", "objc2 0.6.4", "objc2-core-audio", @@ -10406,7 +10434,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "478ae33fcac9df0a18db8302387c666b8ef08a3e2d62b510ca4fc278a384b6c0" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "objc2 0.6.4", "objc2-foundation 0.3.2", ] @@ -10427,7 +10455,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2 0.5.1", "objc2 0.5.2", "objc2-core-location 0.2.2", @@ -10440,7 +10468,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 0.6.4", "objc2-foundation 0.3.2", ] @@ -10475,7 +10503,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5a89f2ec274a0cf4a32642b2991e8b351a404d290da87bb6a9a9d8632490bd1c" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "objc2 0.6.4", ] @@ -10485,7 +10513,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -10507,7 +10535,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", "block2 0.6.2", "dispatch2", "libc", @@ -10520,7 +10548,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", "block2 0.6.2", "dispatch2", "libc", @@ -10580,7 +10608,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 0.6.4", "objc2-core-foundation", "objc2-core-graphics", @@ -10598,7 +10626,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2 0.5.1", "dispatch", "libc", @@ -10611,7 +10639,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 0.6.2", "libc", "objc2 0.6.4", @@ -10634,7 +10662,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 0.6.4", "objc2-core-foundation", ] @@ -10657,7 +10685,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -10669,7 +10697,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2 0.6.2", "dispatch2", "objc2 0.6.4", @@ -10683,7 +10711,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -10696,7 +10724,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 0.6.4", "objc2-core-foundation", "objc2-foundation 0.3.2", @@ -10719,7 +10747,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2 0.5.1", "objc2 0.5.2", "objc2-cloud-kit 0.2.2", @@ -10740,7 +10768,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 0.6.2", "objc2 0.6.4", "objc2-cloud-kit 0.3.2", @@ -10772,7 +10800,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "block2 0.5.1", "objc2 0.5.2", "objc2-core-location 0.2.2", @@ -10822,7 +10850,7 @@ dependencies = [ "log", "pathfinder_color", "pathfinder_geometry", - "rust-embed 8.11.0", + "rust-embed 8.12.0", "serde", "serde_json", "strum 0.27.2", @@ -10855,7 +10883,7 @@ version = "6.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc3cbf698f9438986c11a880c90a6d04b9de27575afd28bbf45b154b6c709e2" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "libc", "once_cell", "onig_sys", @@ -10885,9 +10913,9 @@ checksum = "c08d65885ee38876c4f86fa503fb49d7b507c2b62552df7c70b2fce627e06381" [[package]] name = "open" -version = "5.3.6" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd8d3b65c44123a56e0133d2cd06ce4361bd3ca99d41198b2f25e3c3db9b8b4a" +checksum = "a0b3d059e795d52b8a72fef45658620edd4d9c359b338564aa14391ffa511ed5" dependencies = [ "is-wsl", "libc", @@ -10920,7 +10948,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", @@ -10936,7 +10964,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -10957,76 +10985,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "opentelemetry" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0142c63252a9e054e68a4c61a5778f7b14f576274d593f8ce883d191a099682" -dependencies = [ - "futures-core", - "futures-sink", - "js-sys", - "pin-project-lite", - "thiserror 2.0.18", - "tracing", -] - -[[package]] -name = "opentelemetry-http" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5683015d09e2df236ef005b17f6f196f0d5f6313c4fa43a7b6a53b52776e4331" -dependencies = [ - "async-trait", - "bytes", - "http 1.4.2", - "opentelemetry", -] - -[[package]] -name = "opentelemetry-otlp" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9966929966d17620d7c316c643ba62631826e10021409357772d5eea84f62c35" -dependencies = [ - "flate2", - "http 1.4.2", - "opentelemetry", - "opentelemetry-http", - "opentelemetry-proto", - "opentelemetry_sdk", - "prost", - "thiserror 2.0.18", - "zstd", -] - -[[package]] -name = "opentelemetry-proto" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56d658ba1faf63f7b9c492cfbe6e0ec365440a16132d3270c1065f7b33f1b638" -dependencies = [ - "opentelemetry", - "opentelemetry_sdk", - "prost", -] - -[[package]] -name = "opentelemetry_sdk" -version = "0.32.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b59f80e1ac4d5ff7a2db8fb6c80badb7f0f3f858211fba08dd9aaec750894f9" -dependencies = [ - "futures-channel", - "futures-executor", - "futures-util", - "opentelemetry", - "percent-encoding", - "portable-atomic", - "rand 0.9.4", - "thiserror 2.0.18", -] - [[package]] name = "option-ext" version = "0.2.0" @@ -11050,7 +11008,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc" dependencies = [ "num-traits", - "rand 0.8.6", + "rand 0.8.7", "serde", ] @@ -11159,7 +11117,7 @@ dependencies = [ "proc-macro2", "proc-macro2-diagnostics", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -11242,7 +11200,7 @@ dependencies = [ "by_address", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -11382,9 +11340,9 @@ dependencies = [ [[package]] name = "pest" -version = "2.8.6" +version = "2.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0848c601009d37dfa3430c4666e147e49cdcf1b92ecd3e63657d8a5f19da662" +checksum = "7df728be843c7070fab6ab7c328c4e9e9d78e23bf749c0669c86ee7ebfa050a2" dependencies = [ "memchr", "ucd-trie", @@ -11392,9 +11350,9 @@ dependencies = [ [[package]] name = "pest_derive" -version = "2.8.6" +version = "2.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11f486f1ea21e6c10ed15d5a7c77165d0ee443402f0780849d1768e7d9d6fe77" +checksum = "9e2dd6fc3b26b3462ee188aac870f5a41d398f1cd5e2408d16531bd71c9591fd" dependencies = [ "pest", "pest_generator", @@ -11402,25 +11360,24 @@ dependencies = [ [[package]] name = "pest_generator" -version = "2.8.6" +version = "2.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8040c4647b13b210a963c1ed407c1ff4fdfa01c31d6d2a098218702e6664f94f" +checksum = "6a7a9205cfb6f596a9e8b689c0a15f9ceb7a1aafae7aaf788150ac65b29975b6" dependencies = [ "pest", "pest_meta", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "pest_meta" -version = "2.8.6" +version = "2.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220" +checksum = "85abd351c0de1e8384fc791a0737111a350394937e92b956b743dac12429f57c" dependencies = [ "pest", - "sha2 0.10.9", ] [[package]] @@ -11472,7 +11429,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared", - "rand 0.8.6", + "rand 0.8.7", ] [[package]] @@ -11485,7 +11442,7 @@ dependencies = [ "phf_shared", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -11520,7 +11477,7 @@ checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -11553,7 +11510,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" dependencies = [ "atomic-waker", - "fastrand 2.4.1", + "fastrand 2.5.0", "futures-io", ] @@ -11581,13 +11538,13 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "plist" -version = "1.9.0" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "092791278e026273c1b65bbdcfbba3a300f2994c896bd01ab01da613c29c46f1" +checksum = "7da1d65da6dd5d1e44199ac0f58712d241c0f439f80adea8924d832384087f85" dependencies = [ "base64 0.22.1", "indexmap 2.14.0", - "quick-xml", + "quick-xml 0.41.0", "serde", "time", ] @@ -11626,7 +11583,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", @@ -11681,9 +11638,9 @@ dependencies = [ [[package]] name = "portable-atomic" -version = "1.13.1" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" +checksum = "3d20d5497ef88037a52ff98267d066e7f11fcc5e99bbfbd58a42336193aacec3" [[package]] name = "portable-atomic-util" @@ -11729,7 +11686,7 @@ dependencies = [ "spin", "symbolic-demangle", "tempfile", - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] @@ -11799,7 +11756,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -11807,7 +11764,7 @@ name = "prevent_sleep" version = "0.0.0" dependencies = [ "cfg-if", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "futures", "itertools 0.14.0", "log", @@ -11863,7 +11820,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]] @@ -11890,28 +11847,6 @@ dependencies = [ "version_check", ] -[[package]] -name = "proc-macro-error-attr2" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" -dependencies = [ - "proc-macro2", - "quote", -] - -[[package]] -name = "proc-macro-error2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "11ec05c52be0a07b08061f7dd003e7d7092e0472bc731b4af7bb1ef876109802" -dependencies = [ - "proc-macro-error-attr2", - "proc-macro2", - "quote", - "syn 2.0.118", -] - [[package]] name = "proc-macro-utils" version = "0.8.0" @@ -11925,9 +11860,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", ] @@ -11940,7 +11875,7 @@ checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", "version_check", "yansi", ] @@ -11965,7 +11900,7 @@ version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2d3554923a69f4ce04c4a754260c338f505ce22642d3830e049a399fc2059a29" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "hex", ] @@ -11985,7 +11920,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4488a4a36b9a4ba6b9334a32a39971f77c1436ec82c38707bce707699cc3bbcb" dependencies = [ "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -12013,7 +11948,7 @@ dependencies = [ "prost", "prost-types", "regex", - "syn 2.0.118", + "syn 2.0.119", "tempfile", ] @@ -12027,14 +11962,14 @@ dependencies = [ "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] name = "prost-reflect" -version = "0.16.4" +version = "0.16.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "590aa145fee8f7a26b5a6055365e7c5e89a5c1caae9869de76ec0ee73181a2f9" +checksum = "01b80ea363c31af2de2b92e3c07ed1156628f7838c4afb4df75ee78a37fedbd1" dependencies = [ "prost", "prost-reflect-derive", @@ -12043,9 +11978,9 @@ dependencies = [ [[package]] name = "prost-reflect-build" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8214ae2c30bbac390db0134d08300e770ef89b6d4e5abf855e8d300eded87e28" +checksum = "95a9e8261adf6617d5dc2a5a9e75cce5ab9d546a007f6f870f809a1ad25386b6" dependencies = [ "prost-build", "prost-reflect", @@ -12053,13 +11988,13 @@ dependencies = [ [[package]] name = "prost-reflect-derive" -version = "0.16.0" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b6d90e29fa6c0d13c2c19ba5e4b3fb0efbf5975d27bcf4e260b7b15455bcabe" +checksum = "30320eb03b43b7dfcaf9b361f808a4f1adad1e718ad219df1d7e4283e34e73f5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -12124,9 +12059,9 @@ dependencies = [ [[package]] name = "psl" -version = "2.1.216" +version = "2.1.220" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9826c2fe4dade07e9da1af2e18427257877bc8bc27aa810f6fbb52d907a480cc" +checksum = "de177021aa731f88221b3ae411cb89d70f66e51487c26ef9c9f65fc7250c22e8" dependencies = [ "psl-types", ] @@ -12174,7 +12109,7 @@ checksum = "7347867d0a7e1208d93b46767be83e2b8f978c3dad35f775ac8d8847551d6fe1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -12216,9 +12151,9 @@ checksum = "1d8f70e07b9c3962945a74e59ca1c511bba65b6419468acc217c457d93f3c740" [[package]] name = "pxfm" -version = "0.1.29" +version = "0.1.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" +checksum = "d55d956fa96f5ec02be2e13af0e20391a5aa83d6a074e3ad368959d0fab299ea" [[package]] name = "qrcode" @@ -12241,6 +12176,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "quick-xml" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e660451e55124f798a69a5af3f49ccfbefbd41910eefd25caf2393e1f3473ec1" +dependencies = [ + "memchr", +] + [[package]] name = "quinn" version = "0.11.11" @@ -12248,14 +12192,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c1a41e437b6bbd489372cd4971de128e85c855f56c57f283d20ff016cf7c0a8" dependencies = [ "bytes", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "pin-project-lite", "quinn-proto", "quinn-udp", "rustc-hash 2.1.3", "rustls", - "socket2 0.6.4", - "thiserror 2.0.18", + "socket2 0.6.5", + "thiserror 2.0.19", "tokio", "tracing", "web-time", @@ -12263,21 +12207,22 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.15" +version = "0.11.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fcb935c5bec503c2f0e306bdd3e58bb9029dcb14fa8d9ac76e3a5256ac0763e" +checksum = "2f4bfc015262b9df63c8845072ce59068853ff5872180c2ce2f13038b970e560" dependencies = [ "aws-lc-rs", "bytes", - "getrandom 0.3.4", + "getrandom 0.4.3", "lru-slab", - "rand 0.9.4", + "rand 0.10.2", + "rand_pcg", "ring", "rustc-hash 2.1.3", "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.18", + "thiserror 2.0.19", "tinyvec", "tracing", "web-time", @@ -12285,23 +12230,23 @@ dependencies = [ [[package]] name = "quinn-udp" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" +checksum = "35a133f956daabe89a61a685c2649f13d82d5aa4bd5d12d1277e1072a21c0694" dependencies = [ - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "libc", "once_cell", - "socket2 0.6.4", + "socket2 0.6.5", "tracing", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[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", ] @@ -12314,7 +12259,7 @@ checksum = "a7b5abe3fe82fdeeb93f44d66a7b444dedf2e4827defb0a8e69c437b2de2ef94" dependencies = [ "quote", "quote-use-macros", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -12326,7 +12271,7 @@ dependencies = [ "derive-where", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -12358,9 +12303,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" +checksum = "22f6172bdec972074665ed81ed53b71da00bfc44b65a753cfde883ec4c702a1a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -12370,14 +12315,25 @@ dependencies = [ [[package]] name = "rand" -version = "0.9.4" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" +checksum = "b9ef1d0d795eb7d84685bca4f72f3649f064e6641543d3a8c415898726a57b41" dependencies = [ "rand_chacha 0.9.0", "rand_core 0.9.5", ] +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20 0.10.1", + "getrandom 0.4.3", + "rand_core 0.10.1", +] + [[package]] name = "rand_chacha" version = "0.3.1" @@ -12417,6 +12373,12 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "rand_distr" version = "0.5.1" @@ -12424,7 +12386,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6a8615d50dcf34fa31f7ab52692afec947c4dd0ab803cc87cb3b0b4570ff7463" dependencies = [ "num-traits", - "rand 0.9.4", + "rand 0.9.5", +] + +[[package]] +name = "rand_pcg" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "caa0f4137e1c0a72f4c651489402276c8e8e1cf081f3b0ba156d2cbeef09e86a" +dependencies = [ + "rand_core 0.10.1", ] [[package]] @@ -12470,17 +12441,17 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cbb175c433c8e28a809d1f5773a2ae96e68c0ce40db865cbab1020bf33ae479c" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "compact_str", "critical-section", "hashbrown 0.17.1", "itertools 0.14.0", "kasuari", - "lru 0.18.0", + "lru 0.18.1", "palette 0.7.6", "serde", "strum 0.28.0", - "thiserror 2.0.18", + "thiserror 2.0.19", "unicode-segmentation", "unicode-truncate", "unicode-width 0.2.2", @@ -12535,7 +12506,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "66e3d19bcc9130ca376277d93b60767ff121ace3be06f5f95f81dd68956407d1" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "hashbrown 0.17.1", "indoc", "instability", @@ -12576,10 +12547,10 @@ dependencies = [ "num-traits", "paste", "profiling", - "rand 0.9.4", + "rand 0.9.5", "rand_chacha 0.9.0", "simd_helpers", - "thiserror 2.0.18", + "thiserror 2.0.19", "v_frame", "wasm-bindgen", ] @@ -12605,7 +12576,7 @@ version = "11.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", ] [[package]] @@ -12709,7 +12680,7 @@ 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]] @@ -12718,7 +12689,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c5102a6aaa05aa011a238e178e6bca86d2cb56fc9f586d37cb80f5bca6e07759" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", ] [[package]] @@ -12740,27 +12711,27 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.17", "libredox", - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] name = "ref-cast" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" +checksum = "216e8f773d7923bcba9ceb86a86c93cabb3903a11872fc3f138c49630e50b96d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" -version = "1.0.25" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" +checksum = "2c9283685feec7d69af75fb0e858d5e7378f33fe4fc699383b2916ab9273e03c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.3", ] [[package]] @@ -12780,9 +12751,9 @@ dependencies = [ [[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", @@ -12792,9 +12763,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.14" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" +checksum = "8fcfdb36bda0c880c5931cdc7a2bcdc8ba4556847b9d912bca70bc94708711ad" dependencies = [ "aho-corasick", "memchr", @@ -12841,7 +12812,7 @@ dependencies = [ "prost-build", "repo_metadata", "serde", - "thiserror 2.0.18", + "thiserror 2.0.19", "tokio", "tokio-util", "uuid", @@ -12897,7 +12868,7 @@ dependencies = [ "strum 0.27.2", "strum_macros 0.27.2", "tempfile", - "thiserror 2.0.18", + "thiserror 2.0.19", "virtual-fs", "walkdir", "watcher", @@ -12916,7 +12887,7 @@ dependencies = [ "futures-util", "h2", "http 1.4.2", - "http-body 1.0.1", + "http-body 1.1.0", "http-body-util", "hyper", "hyper-rustls", @@ -12960,7 +12931,7 @@ dependencies = [ "futures-util", "h2", "http 1.4.2", - "http-body 1.0.1", + "http-body 1.1.0", "http-body-util", "hyper", "hyper-rustls", @@ -13098,7 +13069,7 @@ checksum = "c0ed1a78a1b19d184b0daa629dd9a024573173ec7d485b287cb369fb3607cc1c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -13122,7 +13093,7 @@ dependencies = [ "serde", "serde_json", "sse-stream", - "thiserror 2.0.18", + "thiserror 2.0.19", "tokio", "tokio-stream", "tokio-util", @@ -13140,7 +13111,7 @@ dependencies = [ "proc-macro2", "quote", "serde_json", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -13192,7 +13163,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c51c9ae4df8a7fba42103df5c621fa3c37eccf3a3c650879e90fc48b11cc192c" dependencies = [ "hashbrown 0.16.1", - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] @@ -13231,12 +13202,12 @@ dependencies = [ [[package]] name = "rust-embed" -version = "8.11.0" +version = "8.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04113cb9355a377d83f06ef1f0a45b8ab8cd7d8b1288160717d66df5c7988d27" +checksum = "e9e7760e252aaba7b09f4be00e36476cf585bdb68a53552ac954cdf504ab4bc9" dependencies = [ - "rust-embed-impl 8.11.0", - "rust-embed-utils 8.11.0", + "rust-embed-impl 8.12.0", + "rust-embed-utils 8.12.0", "walkdir", ] @@ -13249,20 +13220,21 @@ dependencies = [ "proc-macro2", "quote", "rust-embed-utils 7.8.1", - "syn 2.0.118", + "syn 2.0.119", "walkdir", ] [[package]] name = "rust-embed-impl" -version = "8.11.0" +version = "8.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0902e4c7c8e997159ab384e6d0fc91c221375f6894346ae107f47dd0f3ccaa" +checksum = "3bcfc4d6f53af43755f7a723e4b6b8794fcce052a178dd8c6c1dadc5f5343097" dependencies = [ + "mime_guess", "proc-macro2", "quote", - "rust-embed-utils 8.11.0", - "syn 2.0.118", + "rust-embed-utils 8.12.0", + "syn 2.0.119", "walkdir", ] @@ -13279,12 +13251,12 @@ dependencies = [ [[package]] name = "rust-embed-utils" -version = "8.11.0" +version = "8.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bcdef0be6fe7f6fa333b1073c949729274b05f123a0ad7efcb8efd878e5c3b1" +checksum = "42ffa149f6aa81b58a5b3011d01a857c4ed12c7a732d2c51947a4c7c692185f0" dependencies = [ "globset", - "sha2 0.10.9", + "sha2 0.11.0", "walkdir", ] @@ -13308,7 +13280,7 @@ dependencies = [ "borsh", "bytes", "num-traits", - "rand 0.8.6", + "rand 0.8.7", "rkyv 0.7.46", "serde", "serde_json", @@ -13317,9 +13289,9 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" +checksum = "b74b56ffa8bb2830709a538c2cbcae9aa062db0d2a42563bfb09bdaae44020eb" [[package]] name = "rustc-hash" @@ -13362,7 +13334,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "errno", "libc", "linux-raw-sys 0.4.15", @@ -13375,7 +13347,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 0.12.1", @@ -13384,9 +13356,9 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.41" +version = "0.23.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b92b125634d9b795e7beca796cc790df15a7fb38323bf3196fda83292d06b1f" +checksum = "3c54fcab019b409d04215d3a17cb438fd7fbf192ee61461f20f4fe18704bc138" dependencies = [ "aws-lc-rs", "log", @@ -13482,9 +13454,9 @@ dependencies = [ [[package]] name = "rustversion" -version = "1.0.22" +version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "cf54715a573b99ac80df0bc206da022bcd442c974952c7b9720069370852e21f" [[package]] name = "rustybuzz" @@ -13492,7 +13464,7 @@ version = "0.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fd3c7c96f8a08ee34eff8857b11b49b07d71d1c3f4e88f8a88d4c9e9f90b1702" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "bytemuck", "core_maths", "log", @@ -13592,7 +13564,7 @@ dependencies = [ "proc-macro2", "quote", "serde_derive_internals", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -13624,7 +13596,7 @@ checksum = "1783eabc414609e28a5ba76aee5ddd52199f7107a0b24c2e9746a1ecc34a683d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -13685,7 +13657,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -13698,7 +13670,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", @@ -13717,9 +13689,9 @@ dependencies = [ [[package]] name = "self_cell" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" +checksum = "2ab42ca02749e120097e328d91d415325bdf43b1c72c4c8badf37375fe40a813" [[package]] name = "semver" @@ -13739,7 +13711,7 @@ version = "0.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eb25f439f97d26fea01d717fa626167ceffcd981addaa670001e70505b72acbb" dependencies = [ - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "httpdate", "reqwest 0.13.4", "rustls", @@ -13810,7 +13782,7 @@ version = "0.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ac170a5bba8bec6e3339c90432569d89641fa7a3d3e4f44987d24f0762e6adf" dependencies = [ - "rand 0.9.4", + "rand 0.9.5", "sentry-types", "serde", "serde_json", @@ -13833,7 +13805,7 @@ version = "0.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "235865e639f1d72414fa5d35374e105c292e2ee3a2f90919d961bbb486626ee6" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "log", "sentry-core", ] @@ -13854,7 +13826,7 @@ version = "0.47.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27701acc51e68db5281802b709010395bfcbcb128b1d0a4e5873680d3b47ff0c" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "sentry-backtrace", "sentry-core", "tracing-core", @@ -13869,10 +13841,10 @@ checksum = "56780cb5597d676bf22e6c11d1f062eb4def46390ea3bfb047bcbcf7dfd19bdb" dependencies = [ "debugid", "hex", - "rand 0.9.4", + "rand 0.9.5", "serde", "serde_json", - "thiserror 2.0.18", + "thiserror 2.0.19", "time", "url", "uuid", @@ -13886,9 +13858,9 @@ checksum = "1bc711410fbe7399f390ca1c3b60ad0f53f80e95c5eb935e52268a0e2cd49acc" [[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", @@ -13928,22 +13900,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.3", ] [[package]] @@ -13954,14 +13926,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", @@ -14002,13 +13974,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.3", ] [[package]] @@ -14066,7 +14038,7 @@ dependencies = [ "darling 0.20.11", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -14182,7 +14154,7 @@ dependencies = [ "convert_case 0.7.1", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -14198,9 +14170,9 @@ 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", @@ -14308,15 +14280,15 @@ dependencies = [ [[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_cesu8" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +checksum = "11031e251abf8611c80f460e19dbdeb54a66db918e49c65a7065b46ac7aec520" dependencies = [ "rustc_version", "simdutf8", @@ -14355,7 +14327,7 @@ dependencies = [ "galaxyui_core", "instant", "log", - "thiserror 2.0.18", + "thiserror 2.0.19", "tokio", ] @@ -14439,7 +14411,7 @@ checksum = "0eb01866308440fc64d6c44d9e86c5cc17adfe33c4d6eed55da9145044d0ffc1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -14448,7 +14420,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "calloop", "calloop-wayland-source", "cursor-icon", @@ -14488,9 +14460,9 @@ dependencies = [ [[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", @@ -14509,9 +14481,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", ] @@ -14531,7 +14503,7 @@ version = "0.4.0+sdk-1.4.341.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9571ea910ebd84c86af4b3ed27f9dbdc6ad06f17c5f96146b2b671e2976744f" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", ] [[package]] @@ -14570,13 +14542,13 @@ dependencies = [ [[package]] name = "sse-stream" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3962b63f038885f15bce2c6e02c0e7925c072f1ac86bb60fd44c5c6b762fb72" +checksum = "c123f296ade4ec4b8b0f6162116e6629f5146922ca5ab40ca9d3c2e73ab4761e" dependencies = [ "bytes", "futures-util", - "http-body 1.0.1", + "http-body 1.1.0", "http-body-util", "pin-project-lite", ] @@ -14684,7 +14656,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -14696,7 +14668,7 @@ dependencies = [ "heck 0.5.0", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -14711,7 +14683,7 @@ version = "0.1.0" dependencies = [ "arrayvec", "log", - "rand 0.8.6", + "rand 0.8.7", "sum_tree", ] @@ -14772,9 +14744,20 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.118" +version = "2.0.119" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b9ae57f904213ebb649ce6895b8a66c66f0203b9319718f69a5612a065b1422" +checksum = "872831b642d1a07999a962a351ed35b955ea2cfc8f3862091e2a240a84f17297" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "3.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53e9bae58849f64dfa4f5d5ae372c8341f7305f82a3868709269343628b659a3" dependencies = [ "proc-macro2", "quote", @@ -14798,7 +14781,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -14837,7 +14820,7 @@ dependencies = [ "serde", "serde_derive", "serde_json", - "thiserror 2.0.18", + "thiserror 2.0.19", "walkdir", "yaml-rust", ] @@ -14857,7 +14840,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01198a2debb237c62b6826ec7081082d951f46dbb64b0e8c7649a452230d1dfc" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "byteorder", "enum-as-inner", "libc", @@ -14886,7 +14869,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", ] @@ -14962,7 +14945,7 @@ dependencies = [ "tantivy-stacker", "tantivy-tokenizer-api", "tempfile", - "thiserror 2.0.18", + "thiserror 2.0.19", "time", "typetag", "uuid", @@ -15087,7 +15070,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed071c670382e85fc2f48ae706492d8c338f4f89bf72520d32f8abfe880aade" dependencies = [ - "thiserror 2.0.18", + "thiserror 2.0.19", "windows 0.61.3", "windows-version", ] @@ -15104,7 +15087,7 @@ version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ - "fastrand 2.4.1", + "fastrand 2.5.0", "getrandom 0.4.3", "once_cell", "rustix 1.1.4", @@ -15137,7 +15120,7 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9048a889effe34a5cddee0af7f53285198b16dca3be510858d38dfdb3e62a04e" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "parking_lot", "rustix 1.1.4", "signal-hook", @@ -15179,7 +15162,7 @@ checksum = "4676b37242ccbd1aabf56edb093a4827dc49086c0ffd764a5705899e0f35f8f7" dependencies = [ "anyhow", "base64 0.22.1", - "bitflags 2.13.0", + "bitflags 2.13.1", "fancy-regex 0.11.0", "filedescriptor", "finl_unicode", @@ -15224,11 +15207,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.18" +version = "2.0.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +checksum = "09a43598840e33d5b0331f38c5e30d13bb11c11210a4b58f0d9b18a5a5eefcd9" dependencies = [ - "thiserror-impl 2.0.18", + "thiserror-impl 2.0.19", ] [[package]] @@ -15239,18 +15222,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.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" +checksum = "43cbfe0cf76104d42a574802844187e84a305e531ed54455f11fbde0f10541cd" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.3", ] [[package]] @@ -15261,9 +15244,9 @@ checksum = "3bf63baf9f5039dadc247375c29eb13706706cfde997d0330d05aa63a77d8820" [[package]] name = "thread_local" -version = "1.1.9" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" +checksum = "1ad99c4c6d32803332c548b1af0540b357b3f5fc0be8f6c6bfe8b2e6ae784070" dependencies = [ "cfg-if", ] @@ -15313,9 +15296,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.53" +version = "0.3.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18dfaaeddcb932337b5e7866ee7d0ce9b76d2fd092997146f187ec09b4558a50" +checksum = "3e1d5e639ff6bab73cb6885cc7e7b1de96c3f32c68ec55f3952614bec1092244" dependencies = [ "deranged", "libc", @@ -15335,9 +15318,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", @@ -15375,7 +15358,7 @@ dependencies = [ "digest 0.10.7", "hkdf", "lazy_static", - "rand 0.8.6", + "rand 0.8.7", "serde", "serde_json", "sha-1", @@ -15520,9 +15503,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", ] @@ -15569,7 +15552,7 @@ dependencies = [ "monostate", "onig", "paste", - "rand 0.9.4", + "rand 0.9.5", "rayon", "rayon-cond", "regex", @@ -15577,7 +15560,7 @@ dependencies = [ "serde", "serde_json", "spm_precompiled", - "thiserror 2.0.18", + "thiserror 2.0.19", "unicode-normalization-alignments", "unicode-segmentation", "unicode_categories", @@ -15585,9 +15568,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.52.3" +version = "1.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" +checksum = "202caea871b69668250d242070849eb495be178ed697a3e98aebce5bc81a0bed" dependencies = [ "bytes", "libc", @@ -15595,20 +15578,20 @@ dependencies = [ "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.6.4", + "socket2 0.6.5", "tokio-macros", "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.7.0" +version = "2.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" +checksum = "6328af13490e73a9b4694030fafd93f8c8c6a9dede33e821c3fc63eddf8042ba" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -15633,9 +15616,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.18" +version = "0.1.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +checksum = "a3d06f0b082ba57c26b79407372e57cf2a1e28124f78e9479fe80322cf53420b" dependencies = [ "futures-core", "pin-project-lite", @@ -15644,14 +15627,15 @@ dependencies = [ [[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-io", "futures-sink", + "libc", "pin-project-lite", "tokio", ] @@ -15692,9 +15676,9 @@ dependencies = [ [[package]] name = "toml" -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 = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" +checksum = "53c96ecdfa941c8fc4fcaed14f99ada8ebed502eef533015095a07e3301d4c3c" dependencies = [ "indexmap 2.14.0", "serde_core", @@ -15702,7 +15686,7 @@ dependencies = [ "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", "toml_writer", - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] @@ -15768,15 +15752,15 @@ 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", "toml_writer", - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] @@ -15785,7 +15769,7 @@ version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.3", + "winnow 1.0.4", ] [[package]] @@ -15796,9 +15780,9 @@ checksum = "5d99f8c9a7727884afe522e9bd5edbfc91a3312b36a77b5fb8926e4c31a41801" [[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" @@ -15823,12 +15807,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ "async-compression", - "bitflags 2.13.0", + "bitflags 2.13.1", "bytes", "futures-core", "futures-util", "http 1.4.2", - "http-body 1.0.1", + "http-body 1.1.0", "http-body-util", "http-range-header", "httpdate", @@ -15877,7 +15861,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -15913,20 +15897,6 @@ dependencies = [ "tracing-core", ] -[[package]] -name = "tracing-opentelemetry" -version = "0.33.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adbc64cba7137545b8044cb1fe9814f7aacf3c6b5f9b45be8bb5db538befdb26" -dependencies = [ - "js-sys", - "opentelemetry", - "tracing", - "tracing-core", - "tracing-subscriber", - "web-time", -] - [[package]] name = "tracing-subscriber" version = "0.3.23" @@ -16010,7 +15980,7 @@ dependencies = [ "http 1.4.2", "httparse", "log", - "rand 0.8.6", + "rand 0.8.7", "rustls", "rustls-pki-types", "sha1", @@ -16020,9 +15990,9 @@ dependencies = [ [[package]] name = "twox-hash" -version = "2.1.2" +version = "2.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ea3136b675547379c4bd395ca6b938e5ad3c3d20fad76e7fe85f9e0d011419c" +checksum = "8464ec13c3691491391d9fce00f6416c9a48e46972f72d7865688be2080192c9" [[package]] name = "typed-arena" @@ -16056,9 +16026,9 @@ checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "typetag" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5a897b12c6c1151ad0b138b8db50252dc301f93bc3b027db05eec82aeed298c" +checksum = "c90e86058a30d42a1a928dfb4b49bb33c98c3a2b4909492e6b0881cd94798ec2" dependencies = [ "erased-serde", "inventory", @@ -16069,13 +16039,13 @@ dependencies = [ [[package]] name = "typetag-impl" -version = "0.2.22" +version = "0.2.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf808357c6ed7e13ba0f3277ec8d8f21b2d501274895104263985330c726c1c5" +checksum = "f153acc4e99a5f2a5aefa09fb078be54e26271b2813f6041200b224c098d8328" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 3.0.3", ] [[package]] @@ -16163,7 +16133,7 @@ dependencies = [ "galaxyui_core", "pathfinder_color", "pathfinder_geometry", - "rust-embed 8.11.0", + "rust-embed 8.12.0", "rustls", ] @@ -16356,7 +16326,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dea7109cdcd5864d4eeb1b58a1648dc9bf520360d7af16ec26d0a9354bafcfc0" dependencies = [ "base64 0.22.1", - "der 0.8.0", + "der 0.8.1", "log", "native-tls", "percent-encoding", @@ -16366,7 +16336,7 @@ dependencies = [ "ureq-proto", "utf8-zero", "webpki-root-certs", - "webpki-roots 1.0.8", + "webpki-roots 1.0.9", ] [[package]] @@ -16448,9 +16418,9 @@ checksum = "7fcfc827f90e53a02eaef5e535ee14266c1d569214c6aa70133a624d8a3164ba" [[package]] name = "utf8-width" -version = "0.1.8" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1292c0d970b54115d14f2492fe0170adf21d68a1de108eebc51c1df4f346a091" +checksum = "159a7cadce548703edd50d24069bc294c5415ecab0a480e0cd1ca06d112dc94a" [[package]] name = "utf8-zero" @@ -16477,9 +16447,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.23.4" +version = "1.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" +checksum = "bf3923a6f5c4c6382e0b653c4117f48d631ea17f38ed86e2a828e6f7412f5239" dependencies = [ "atomic", "getrandom 0.4.3", @@ -16532,13 +16502,13 @@ checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "variant_count" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1935e10c6f04d22688d07c0790f2fc0e1b1c5c2c55bc0cc87ed67656e587dd8" +checksum = "17c13b28baa922722e35ded153b394d0247b4942b4ad54f85713a7de672cdf0e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -16616,7 +16586,7 @@ dependencies = [ "log", "parking_lot", "rubato", - "thiserror 2.0.18", + "thiserror 2.0.19", ] [[package]] @@ -16756,7 +16726,7 @@ version = "0.0.0" dependencies = [ "anyhow", "galaxyui_core", - "rust-embed 8.11.0", + "rust-embed 8.12.0", ] [[package]] @@ -16792,7 +16762,7 @@ dependencies = [ "galaxy_core", "galaxy_server_client", "prost", - "thiserror 2.0.18", + "thiserror 2.0.19", "tracing", "tracing-futures", "warp_multi_agent_api", @@ -16844,7 +16814,7 @@ dependencies = [ "parking_lot", "serde", "serde_json", - "thiserror 2.0.18", + "thiserror 2.0.19", "uuid", ] @@ -16934,7 +16904,7 @@ dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", "wasm-bindgen-shared", ] @@ -17005,7 +16975,7 @@ version = "0.31.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "rustix 1.1.4", "wayland-backend", "wayland-scanner", @@ -17017,7 +16987,7 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "cursor-icon", "wayland-backend", ] @@ -17039,7 +17009,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", @@ -17051,7 +17021,7 @@ version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b6d8cf1eb2c1c31ed1f5643c88a6e53538129d4af80030c8cabd1f9fa884d91" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -17064,7 +17034,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", @@ -17078,7 +17048,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a" dependencies = [ "proc-macro2", - "quick-xml", + "quick-xml 0.39.4", "quote", ] @@ -17128,9 +17098,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d46a5a140e6f7afeccd8eae97eff335163939eac8b929834875168b29b3d267" +checksum = "b96554aa2acc8ccdb7e1c9a58a7a68dd5d13bccc69cd124cb09406db612a1c9b" dependencies = [ "rustls-pki-types", ] @@ -17141,14 +17111,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.8", + "webpki-roots 1.0.9", ] [[package]] name = "webpki-roots" -version = "1.0.8" +version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf85cb06032201fa7c6f829d7db5a7e5aa45bcc0655327713065f6f0576731bf" +checksum = "7dcd9d09a39985f5344844e66b0c530a33843579125f23e21e9f0f220850f22a" dependencies = [ "rustls-pki-types", ] @@ -17177,7 +17147,7 @@ dependencies = [ "pin-project", "rustls", "rustls-platform-verifier 0.6.2", - "thiserror 2.0.18", + "thiserror 2.0.19", "tokio", "tokio-rustls", "url", @@ -17279,10 +17249,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76e8840e1ba2881d4cbb18d2147627a56af426ff064c0401eb0c8410c6325d07" dependencies = [ "arrayvec", - "bitflags 2.13.0", + "bitflags 2.13.1", "bytemuck", "cfg-if", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "document-features", "hashbrown 0.16.1", "js-sys", @@ -17310,9 +17280,9 @@ dependencies = [ "arrayvec", "bit-set 0.9.1", "bit-vec 0.9.1", - "bitflags 2.13.0", + "bitflags 2.13.1", "bytemuck", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "document-features", "hashbrown 0.16.1", "indexmap 2.14.0", @@ -17325,7 +17295,7 @@ dependencies = [ "raw-window-handle 0.6.2", "rustc-hash 1.1.0", "smallvec", - "thiserror 2.0.18", + "thiserror 2.0.19", "wgpu-core-deps-apple", "wgpu-core-deps-emscripten", "wgpu-core-deps-wasm", @@ -17381,11 +17351,11 @@ dependencies = [ "arrayvec", "ash", "bit-set 0.9.1", - "bitflags 2.13.0", + "bitflags 2.13.1", "block2 0.6.2", "bytemuck", "cfg-if", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "glow", "glutin_wgl_sys", "gpu-allocator", @@ -17414,7 +17384,7 @@ dependencies = [ "raw-window-metal", "renderdoc-sys", "smallvec", - "thiserror 2.0.18", + "thiserror 2.0.19", "wasm-bindgen", "wayland-sys", "web-sys", @@ -17441,7 +17411,7 @@ version = "29.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84bf84cd9ca8ca45e2b223a3868f1adf9bfc0c66aeac212e76ee7e40fdadf8f5" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "bytemuck", "js-sys", "log", @@ -17622,7 +17592,7 @@ checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -17633,7 +17603,7 @@ checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -18011,11 +17981,11 @@ dependencies = [ "ahash 0.8.12", "android-activity", "atomic-waker", - "bitflags 2.13.0", + "bitflags 2.13.1", "block2 0.5.1", "bytemuck", "calloop", - "cfg_aliases 0.2.1", + "cfg_aliases 0.2.2", "concurrent-queue", "core-foundation 0.9.4", "core-graphics 0.23.2", @@ -18075,9 +18045,9 @@ dependencies = [ [[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", ] @@ -18117,7 +18087,7 @@ dependencies = [ "log", "os_pipe", "rustix 1.1.4", - "thiserror 2.0.18", + "thiserror 2.0.19", "tree_magic_mini", "wayland-backend", "wayland-client", @@ -18154,7 +18124,7 @@ dependencies = [ "pharos", "rustc_version", "send_wrapper", - "thiserror 2.0.18", + "thiserror 2.0.19", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", @@ -18239,7 +18209,7 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.13.0", + "bitflags 2.13.1", "dlib", "log", "once_cell", @@ -18358,7 +18328,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", "synstructure", ] @@ -18370,15 +18340,15 @@ checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", "synstructure", ] [[package]] name = "zbus" -version = "5.16.0" +version = "5.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eee682d202a77e4a9f3b2c2bdf48a7b28af5c08c34ddf66f98c93e5e39464285" +checksum = "fe18fb60dc696039e738717b76eaea21e7a4489bbb1885020b43c94236d7e98a" dependencies = [ "async-broadcast", "async-executor", @@ -18403,7 +18373,7 @@ dependencies = [ "uds_windows", "uuid", "windows-sys 0.61.2", - "winnow 1.0.3", + "winnow 1.0.4", "zbus_macros", "zbus_names", "zvariant", @@ -18411,14 +18381,14 @@ dependencies = [ [[package]] name = "zbus_macros" -version = "5.16.0" +version = "5.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adf1bd45a81a103745b1757754762a26e8cd01e4532e4d6c8ec431624b80d1d6" +checksum = "fe96480bed92df2b442a1a30df364e12d08eed03aeb061f2b8dc6afb2be91119" dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", "zbus_names", "zvariant", "zvariant_utils", @@ -18426,12 +18396,12 @@ dependencies = [ [[package]] name = "zbus_names" -version = "4.3.2" +version = "4.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d" +checksum = "d8bf88b4a3ff53e883001e0e0115b297a9d53c31b9c1edd2bfdd853e3428624e" dependencies = [ "serde", - "winnow 1.0.3", + "winnow 1.0.4", "zvariant", ] @@ -18443,22 +18413,22 @@ checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697" [[package]] name = "zerocopy" -version = "0.8.52" +version = "0.8.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce1022995ff5ff5d841ad7d994facc23098cd40152f2c1d11cd607c6f530653f" +checksum = "b5a105cd7b140f6eeec8acff2ea38135d3cab283ada58540f629fe51e46696eb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.52" +version = "0.8.55" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ae7f38b72ec2a254e2b87ef277cf2cd4fb97cbebf944faa6f33354da0867930" +checksum = "0fe976fb70c78cd64cccfe3a6fc142244e8a77b70959b30faf9d0ac37ee228eb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -18478,7 +18448,7 @@ checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", "synstructure", ] @@ -18499,7 +18469,7 @@ checksum = "3c50655cbb0fe3fc43170059e702f1ce5e19b84cec58dc87b037a09935c2f328" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -18532,7 +18502,7 @@ checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", ] [[package]] @@ -18557,7 +18527,7 @@ dependencies = [ "memchr", "pbkdf2", "sha1", - "thiserror 2.0.18", + "thiserror 2.0.19", "time", "xz2", "zeroize", @@ -18579,9 +18549,9 @@ 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 = "zopfli" @@ -18649,41 +18619,41 @@ dependencies = [ [[package]] name = "zvariant" -version = "5.12.0" +version = "5.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a192a0bde63360d77a7523c833d4b4ce6070a927e2c53246e4c540b1a3e27be0" +checksum = "bee2a0bcd2a907786a456fff45aaaaf54c9ba5f50b71ae9ec1a4edd200c94911" dependencies = [ "endi", "enumflags2", "serde", "url", - "winnow 1.0.3", + "winnow 1.0.4", "zvariant_derive", "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "5.12.0" +version = "5.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc6cde9c01c511074be97f7ccb6c19d0da89e3f8662e812e999dcfd4638737" +checksum = "38a708216a18780796770bfe3f4739c7c83a3e8f789b755534bbbc06e4e23e12" dependencies = [ "proc-macro-crate 3.5.0", "proc-macro2", "quote", - "syn 2.0.118", + "syn 2.0.119", "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "3.4.0" +version = "3.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e8535915cfa75547e559d8c68e8139909a4aeee076831e4ef7fc59d8172c4d6" +checksum = "90cb9383f9b45290407a1258b202d3f8f01db719eb60b4e4055c6375af4fc7c7" dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.118", - "winnow 1.0.3", + "syn 2.0.119", + "winnow 1.0.4", ] diff --git a/Cargo.toml b/Cargo.toml index 7ca2d4d7..89efca8b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -217,15 +217,6 @@ notify-debouncer-full = { git = "https://github.com/warpdotdev/notify", rev = "9 nom = "7.1.1" num-traits = "0.2" oauth2 = { version = "5.0.0", default-features = false } -opentelemetry = { version = "0.32.0", default-features = false, features = ["trace"] } -opentelemetry-http = "0.32.0" -opentelemetry-otlp = { version = "0.32.0", default-features = false, features = [ - "gzip-http", - "http-proto", - "trace", - "zstd-http", -] } -opentelemetry_sdk = { version = "0.32.1", default-features = false, features = ["trace"] } openh264 = "0.8" static_assertions = "1.1.0" url = "2.5.4" @@ -306,7 +297,6 @@ tower = "0.5.2" tower-http = "0.6.6" tracing = "0.1.40" tracing-futures = "0.2.5" -tracing-opentelemetry = { version = "0.33.0", default-features = false } tracing-subscriber = { version = "0.3.22", default-features = false, features = [ "registry", "std", @@ -453,7 +443,7 @@ split-debuginfo = "unpacked" [profile.dev.package] # Improve the performance of core terminal logic. -warp_terminal.opt-level = 3 +galaxy_terminal.opt-level = 3 # Minimize the runtime overhead of CPU profiling at the cost of a small # increase in compile time for this crate. backtrace.opt-level = 3 diff --git a/app/Cargo.toml b/app/Cargo.toml index 5b51af1d..6a6d8409 100644 --- a/app/Cargo.toml +++ b/app/Cargo.toml @@ -321,13 +321,8 @@ http_server.workspace = true hyper.workspace = true libsqlite3-sys = { version = "0.33.0", features = ["bundled"] } mio = { version = "1.1.1", features = ["os-poll", "os-ext"] } -opentelemetry.workspace = true -opentelemetry-http.workspace = true -opentelemetry-otlp.workspace = true -opentelemetry_sdk.workspace = true tokio.workspace = true tokio-util.workspace = true -tracing-opentelemetry.workspace = true tracing-subscriber.workspace = true # AWS SDK (loading credentials for BYO LLM) diff --git a/app/src/ai/bedrock/request_translator.rs b/app/src/ai/bedrock/request_translator.rs index 41523027..9f176e88 100644 --- a/app/src/ai/bedrock/request_translator.rs +++ b/app/src/ai/bedrock/request_translator.rs @@ -1718,6 +1718,69 @@ fn extract_tool_result_content(result: &api::request::input::ToolCallResult) -> None => ("User did not answer.".to_string(), true), } } + api::request::input::tool_call_result::Result::CreateDocuments(create_result) => { + match &create_result.result { + Some(api::create_documents_result::Result::Success(success)) => { + let docs_info: Vec = success + .created_documents + .iter() + .map(|doc| { + format!( + "Created document (id: {}):\n{}", + doc.document_id, doc.content + ) + }) + .collect(); + (docs_info.join("\n\n"), false) + } + Some(api::create_documents_result::Result::Error(error)) => { + (format!("Error creating documents: {}", error.message), true) + } + None => ("Create documents cancelled.".to_string(), true), + } + } + api::request::input::tool_call_result::Result::EditDocuments(edit_result) => { + match &edit_result.result { + Some(api::edit_documents_result::Result::Success(success)) => { + let docs_info: Vec = success + .updated_documents + .iter() + .map(|doc| { + format!( + "Updated document (id: {}):\n{}", + doc.document_id, doc.content + ) + }) + .collect(); + (docs_info.join("\n\n"), false) + } + Some(api::edit_documents_result::Result::Error(error)) => { + (format!("Error editing documents: {}", error.message), true) + } + None => ("Edit documents cancelled.".to_string(), true), + } + } + api::request::input::tool_call_result::Result::ReadDocuments(read_result) => { + match &read_result.result { + Some(api::read_documents_result::Result::Success(success)) => { + let docs_info: Vec = success + .documents + .iter() + .map(|doc| { + format!( + "Document (id: {}):\n{}", + doc.document_id, doc.content + ) + }) + .collect(); + (docs_info.join("\n\n"), false) + } + Some(api::read_documents_result::Result::Error(error)) => { + (format!("Error reading documents: {}", error.message), true) + } + None => ("Read documents cancelled.".to_string(), true), + } + } _ => ("Tool completed successfully.".to_string(), false), } } else { diff --git a/app/src/ai/blocklist/action_model/execute/notebooks.rs b/app/src/ai/blocklist/action_model/execute/notebooks.rs index 39b2d165..03b005c6 100644 --- a/app/src/ai/blocklist/action_model/execute/notebooks.rs +++ b/app/src/ai/blocklist/action_model/execute/notebooks.rs @@ -13,7 +13,7 @@ use crate::ai::agent::{ DocumentContext, EditDocumentsRequest, EditDocumentsResult, ReadDocumentsRequest, ReadDocumentsResult, }; -use crate::ai::document::ai_document_model::AIDocumentVersion; +use crate::ai::document::ai_document_model::{AIDocumentId, AIDocumentVersion}; use crate::cloud_object::model::persistence::CloudModel; use crate::notebooks::CloudNotebookModel; use crate::server::cloud_objects::update_manager::UpdateManager; @@ -59,10 +59,11 @@ impl NotebookExecutor { for document in documents { let client_id = ClientId::new(); + let document_id = AIDocumentId::new(); let model = CloudNotebookModel { title: document.title.clone(), data: document.content.clone(), - ai_document_id: None, + ai_document_id: Some(document_id), conversation_id: None, }; @@ -79,7 +80,7 @@ impl NotebookExecutor { }); created.push(DocumentContext { - document_id: crate::ai::document::ai_document_model::AIDocumentId::new(), + document_id, document_version: AIDocumentVersion::default(), content: document.content.clone(), line_ranges: vec![], @@ -112,10 +113,15 @@ impl NotebookExecutor { let mut documents = Vec::new(); for id in document_ids { - // Try to find the notebook by treating the ID as a SyncId string + // Look up the notebook by its ai_document_id field, falling back to SyncId matching let notebook = cloud_model .get_all_active_notebooks() - .find(|nb| nb.id.uid() == id.to_string()); + .find(|nb| nb.model().ai_document_id.as_ref() == Some(id)) + .or_else(|| { + cloud_model + .get_all_active_notebooks() + .find(|nb| nb.id.uid() == id.to_string()) + }); if let Some(notebook) = notebook { documents.push(DocumentContext { @@ -149,9 +155,15 @@ impl NotebookExecutor { for diff in diffs { let cloud_model = CloudModel::as_ref(ctx); + // Look up the notebook by its ai_document_id field, falling back to SyncId matching let notebook_data = cloud_model .get_all_active_notebooks() - .find(|nb| nb.id.uid() == diff.document_id.to_string()) + .find(|nb| nb.model().ai_document_id.as_ref() == Some(&diff.document_id)) + .or_else(|| { + cloud_model + .get_all_active_notebooks() + .find(|nb| nb.id.uid() == diff.document_id.to_string()) + }) .map(|nb| (nb.id, nb.model().data.clone())); let Some((notebook_id, current_data)) = notebook_data else { diff --git a/app/src/ai/blocklist/inline_action/create_environment_modal.rs b/app/src/ai/blocklist/inline_action/create_environment_modal.rs index cde87e37..38bba35c 100644 --- a/app/src/ai/blocklist/inline_action/create_environment_modal.rs +++ b/app/src/ai/blocklist/inline_action/create_environment_modal.rs @@ -1,11 +1,5 @@ -use warpui::elements::{ChildView, Element, Empty}; -use warpui::{AppContext, Entity, SingletonEntity, TypedActionView, View, ViewContext, ViewHandle}; - -use crate::settings_view::handoff_environment_creation_modal::{ - HandoffEnvironmentCreationModal, HandoffEnvironmentCreationModalEvent, -}; -use crate::view_components::DismissibleToast; -use crate::workspace::ToastStack; +use warpui::elements::{Element, Empty}; +use warpui::{AppContext, Entity, TypedActionView, View, ViewContext}; #[derive(Debug, Clone)] pub enum CreateEnvironmentModalEvent { @@ -15,38 +9,11 @@ pub enum CreateEnvironmentModalEvent { pub struct CreateEnvironmentModal { visible: bool, - handoff_modal: ViewHandle, } impl CreateEnvironmentModal { - pub fn new(ctx: &mut ViewContext) -> Self { - let handoff_modal = - ctx.add_typed_action_view(HandoffEnvironmentCreationModal::new_for_orchestration); - ctx.subscribe_to_view(&handoff_modal, |me, _, event, ctx| match event { - HandoffEnvironmentCreationModalEvent::Created { env_id } => { - me.visible = false; - ctx.emit(CreateEnvironmentModalEvent::Created { - environment_id: env_id.uid(), - }); - ctx.notify(); - } - HandoffEnvironmentCreationModalEvent::Cancelled => { - me.cancel(ctx); - } - HandoffEnvironmentCreationModalEvent::CreationFailed { error_message } => { - me.visible = false; - me.show_error_toast( - format!("Failed to create environment: {error_message}"), - ctx, - ); - ctx.notify(); - } - }); - - Self { - visible: false, - handoff_modal, - } + pub fn new(_ctx: &mut ViewContext) -> Self { + Self { visible: false } } pub fn is_visible(&self) -> bool { @@ -55,10 +22,7 @@ impl CreateEnvironmentModal { pub fn show(&mut self, ctx: &mut ViewContext) { self.visible = true; - self.handoff_modal.update(ctx, |modal, ctx| { - modal.show(ctx); - }); - ctx.focus(&self.handoff_modal); + ctx.emit(CreateEnvironmentModalEvent::Cancelled); ctx.notify(); } @@ -66,18 +30,6 @@ impl CreateEnvironmentModal { self.visible = false; ctx.notify(); } - - fn cancel(&mut self, ctx: &mut ViewContext) { - self.hide(ctx); - ctx.emit(CreateEnvironmentModalEvent::Cancelled); - } - - fn show_error_toast(&self, message: String, ctx: &mut ViewContext) { - let window_id = ctx.window_id(); - ToastStack::handle(ctx).update(ctx, |toast_stack, ctx| { - toast_stack.add_ephemeral_toast(DismissibleToast::error(message), window_id, ctx); - }); - } } impl Entity for CreateEnvironmentModal { @@ -96,11 +48,7 @@ impl View for CreateEnvironmentModal { } fn render(&self, _app: &AppContext) -> Box { - if !self.visible { - return Empty::new().finish(); - } - - ChildView::new(&self.handoff_modal).finish() + Empty::new().finish() } } diff --git a/app/src/ai/blocklist/inline_action/requested_command.rs b/app/src/ai/blocklist/inline_action/requested_command.rs index 26f4063b..0ddc2db0 100644 --- a/app/src/ai/blocklist/inline_action/requested_command.rs +++ b/app/src/ai/blocklist/inline_action/requested_command.rs @@ -1476,9 +1476,8 @@ impl View for RequestedCommandView { let is_input_pinned_to_top = *InputModeSettings::as_ref(app).input_mode.value() == InputMode::PinnedToTop; - // When expanded details are rendered using a regular block, having a non-zero horizontal - // margin while toggled expanded will cause the body to look wider than the header. - // The expanded details should also appear connected to the header, so we remove bottom margin in this case. + // Tracks whether the expanded command has a terminal output block directly below it. + // Used to remove the bottom margin for visual continuity with the terminal block. let is_rendered_above_expanded_command_block = { let terminal_model = self.terminal_model.lock(); @@ -1517,7 +1516,6 @@ impl View for RequestedCommandView { let header_element = self.render_header( !should_render_editor && !should_render_mcp_content - && !is_rendered_above_expanded_command_block && !has_citations_footer, app, ); @@ -1613,12 +1611,9 @@ impl View for RequestedCommandView { theme.surface_2() }; - // If the requested command state is completed and input isn't pinned to the top, we're - // going to have a regular block directly below this one with the output of the executed - // command. Since we can't control the top padding of the AI block that comes _after_ the - // subsequent regular block, we'll simply need to eliminate the bottom margin on this block - // and have the next AI block take care of the vertical spacing. Moreover, having a non-zero - // bottom margin while expanded will cause the body to look disconnected from the header. + // If the requested command is expanded above a terminal block or + // the next exchange flows directly after, remove bottom margin for + // visual continuity. let should_remove_bottom_margin = is_rendered_above_expanded_command_block || ((self.action_type.is_requested_command() || self.action_type.is_mcp_tool()) && is_last_output_message_in_output @@ -1645,28 +1640,20 @@ impl View for RequestedCommandView { && !is_input_pinned_to_top); let container = Container::new(content.finish()) - .with_margin_left(if is_rendered_above_expanded_command_block { - 0. - } else if action_status.is_some_and(|status| status.is_blocked()) { - CONTENT_HORIZONTAL_PADDING - } else { - CONTENT_HORIZONTAL_PADDING + icon_size(app) + 16. - }) - .with_margin_right(if is_rendered_above_expanded_command_block { - 0. - } else { - CONTENT_HORIZONTAL_PADDING - }) + .with_margin_left( + if action_status.is_some_and(|status| status.is_blocked()) { + CONTENT_HORIZONTAL_PADDING + } else { + CONTENT_HORIZONTAL_PADDING + icon_size(app) + 16. + }, + ) + .with_margin_right(CONTENT_HORIZONTAL_PADDING) .with_margin_bottom(if should_remove_bottom_margin { 0. } else { CONTENT_ITEM_VERTICAL_MARGIN }) - .with_corner_radius(if is_rendered_above_expanded_command_block { - CornerRadius::with_top(Radius::Pixels(8.)) - } else { - CornerRadius::with_all(Radius::Pixels(8.)) - }) + .with_corner_radius(CornerRadius::with_all(Radius::Pixels(8.))) .with_border(Border::all(1.).with_border_fill(border_color)) .finish(); diff --git a/app/src/ai/blocklist/prompt/prompt_alert.rs b/app/src/ai/blocklist/prompt/prompt_alert.rs index 56291550..68370329 100644 --- a/app/src/ai/blocklist/prompt/prompt_alert.rs +++ b/app/src/ai/blocklist/prompt/prompt_alert.rs @@ -392,7 +392,7 @@ impl View for PromptAlertView { text_fragments.push(FormattedTextFragment::plain_text(" ")); text_fragments.push(FormattedTextFragment::hyperlink_action( "Add credits", - WorkspaceAction::ShowSettingsPage(SettingsSection::BillingAndUsage), + WorkspaceAction::ShowSettingsPage(SettingsSection::About), )); } else { self.action_hyperlink(&state, &mut text_fragments, app); diff --git a/app/src/app_state.rs b/app/src/app_state.rs index 1bcc06ce..e3cb96ce 100644 --- a/app/src/app_state.rs +++ b/app/src/app_state.rs @@ -15,7 +15,6 @@ use crate::code::editor_management::CodeSource; use crate::drive::OpenGalaxyDriveObjectSettings; use crate::root_view::quake_mode_window_id; use crate::server::ids::SyncId; -use crate::settings_view::environments_page::EnvironmentsPage; use crate::settings_view::SettingsSection; use crate::tab::SelectedTabColor; use crate::terminal::ShellLaunchData; @@ -282,9 +281,7 @@ pub enum EnvVarCollectionPaneSnapshot { } #[derive(Clone, Debug, PartialEq)] -pub struct EnvironmentManagementPaneSnapshot { - pub mode: EnvironmentsPage, -} +pub struct EnvironmentManagementPaneSnapshot; #[derive(Clone, Debug, PartialEq)] pub enum SettingsPaneSnapshot { diff --git a/app/src/notebooks/editor/model.rs b/app/src/notebooks/editor/model.rs index a7517b28..cf4fcf52 100644 --- a/app/src/notebooks/editor/model.rs +++ b/app/src/notebooks/editor/model.rs @@ -1804,8 +1804,10 @@ impl NotebooksEditorModel { diffs: Vec, ctx: &mut ModelContext, ) { - // Get current markdown content - let current_markdown = self.markdown(ctx); + // Get current markdown content using the unescaped form so that line + // ranges computed by fuzzy_match_diffs (which operates on + // markdown_unescaped output) align correctly. + let current_markdown = self.markdown_unescaped(ctx); let lines: Vec<&str> = current_markdown.lines().collect(); let mut result_lines = Vec::new(); diff --git a/app/src/pane_group/mod.rs b/app/src/pane_group/mod.rs index 08a4a547..09d7eebc 100644 --- a/app/src/pane_group/mod.rs +++ b/app/src/pane_group/mod.rs @@ -8047,15 +8047,7 @@ impl View for PaneGroup { .environment_setup_mode_selector_handle() .cloned() }) - .or_else(|| { - self.downcast_pane_by_id::(pane_id) - .and_then(|emp| { - emp.environments_page_view(app) - .as_ref(app) - .environment_setup_mode_selector_handle() - .cloned() - }) - }); + .or_else(|| None); if let Some(handle) = selector_handle { stack.add_child(ChildView::new(&handle).finish()); } @@ -8074,18 +8066,7 @@ impl View for PaneGroup { } } // Render agent-assisted environment modal at tab level when open. - if let Some(pane_id) = self.pane_with_open_agent_assisted_environment_modal { - if let Some(handle) = self - .downcast_pane_by_id::(pane_id) - .and_then(|emp| { - emp.environments_page_view(app) - .as_ref(app) - .agent_assisted_environment_modal_handle(app) - .cloned() - }) - { - stack.add_child(ChildView::new(&handle).finish()); - } + if let Some(_pane_id) = self.pane_with_open_agent_assisted_environment_modal { } stack.finish() diff --git a/app/src/pane_group/pane/environment_management_pane.rs b/app/src/pane_group/pane/environment_management_pane.rs index 4be39eed..dc8f73bd 100644 --- a/app/src/pane_group/pane/environment_management_pane.rs +++ b/app/src/pane_group/pane/environment_management_pane.rs @@ -2,48 +2,26 @@ use galaxyui::{AppContext, ModelHandle, ViewContext, ViewHandle}; use super::view::PaneView; use super::{ - DetachType, PaneConfiguration, PaneContent, PaneEvent, PaneGroup, PaneId, ShareableLink, + DetachType, PaneConfiguration, PaneContent, PaneGroup, PaneId, ShareableLink, ShareableLinkError, }; -use crate::ai::ambient_agents::github_auth_url::GithubAuthRedirectTarget; use crate::app_state::{EnvironmentManagementPaneSnapshot, LeafContents}; use crate::pane_group::focus_state::PaneFocusHandle; -use crate::settings_view::environments_page::{EnvironmentsPage, EnvironmentsPageView}; -use crate::settings_view::settings_page::{PaneEventWrapper, SettingsPageEvent}; +use crate::settings_view::{SettingsView, SettingsViewEvent}; pub struct EnvironmentManagementPane { - view: ViewHandle>, + view: ViewHandle>, pane_configuration: ModelHandle, } impl EnvironmentManagementPane { pub fn new(ctx: &mut ViewContext) -> Self { - // Create the EnvironmentsPageView - let environments_page_view = ctx.add_typed_action_view(|ctx| { - let mut view = EnvironmentsPageView::new(ctx); - view.set_github_auth_redirect_target(GithubAuthRedirectTarget::FocusCloudMode, ctx); - view - }); + let settings_view = ctx.add_typed_action_view(|ctx| SettingsView::new(None, ctx)); + let pane_configuration = settings_view.as_ref(ctx).pane_configuration(); - Self::from_view(environments_page_view, ctx) - } - - pub fn from_view( - environments_page_view: ViewHandle, - ctx: &mut AppContext, - ) -> Self { - let pane_configuration = environments_page_view.as_ref(ctx).pane_configuration(); - let window_id = environments_page_view.window_id(ctx); - - let view = ctx.add_typed_action_view(window_id, |ctx| { + let view = ctx.add_typed_action_view(|ctx| { let pane_id = PaneId::from_environment_management_pane_ctx(ctx); - PaneView::new( - pane_id, - environments_page_view, - (), - pane_configuration.clone(), - ctx, - ) + PaneView::new(pane_id, settings_view, (), pane_configuration.clone(), ctx) }); Self { @@ -51,18 +29,6 @@ impl EnvironmentManagementPane { pane_configuration, } } - - pub fn environments_page_view(&self, ctx: &AppContext) -> ViewHandle { - self.view.as_ref(ctx).child(ctx) - } - - /// Returns the current mode of the environment management pane. - pub fn current_mode(&self, ctx: &AppContext) -> EnvironmentsPage { - self.environments_page_view(ctx) - .as_ref(ctx) - .current_page() - .clone() - } } impl PaneContent for EnvironmentManagementPane { @@ -78,34 +44,7 @@ impl PaneContent for EnvironmentManagementPane { ) { self.view .update(ctx, |view, ctx| view.set_focus_handle(focus_handle, ctx)); - let pane_id = self.id(); - - ctx.subscribe_to_view( - &self.environments_page_view(ctx), - move |pane_group, _, event, ctx| match event { - SettingsPageEvent::Pane(pane_event_wrapper) => { - let pane_event = match pane_event_wrapper { - PaneEventWrapper::Close => PaneEvent::Close, - }; - pane_group.handle_pane_event(pane_id, &pane_event, ctx); - } - SettingsPageEvent::EnvironmentSetupModeSelectorToggled { is_open } => { - pane_group.pane_with_open_environment_setup_mode_selector = - is_open.then_some(pane_id); - ctx.notify(); - } - SettingsPageEvent::AgentAssistedEnvironmentModalToggled { is_open } => { - pane_group.pane_with_open_agent_assisted_environment_modal = - is_open.then_some(pane_id); - ctx.notify(); - } - SettingsPageEvent::FocusModal => { - // Not applicable when hosted in a pane. - } - }, - ); - ctx.subscribe_to_view(&self.view, move |group, _, event, ctx| { group.handle_pane_view_event(pane_id, event, ctx); }); @@ -117,15 +56,11 @@ impl PaneContent for EnvironmentManagementPane { _detach_type: DetachType, ctx: &mut ViewContext, ) { - let environments_page_view = self.environments_page_view(ctx); - ctx.unsubscribe_to_view(&environments_page_view); ctx.unsubscribe_to_view(&self.view); } - fn snapshot(&self, ctx: &AppContext) -> LeafContents { - LeafContents::EnvironmentManagement(EnvironmentManagementPaneSnapshot { - mode: self.current_mode(ctx), - }) + fn snapshot(&self, _ctx: &AppContext) -> LeafContents { + LeafContents::EnvironmentManagement(EnvironmentManagementPaneSnapshot) } fn has_application_focus(&self, ctx: &mut ViewContext) -> bool { @@ -133,8 +68,7 @@ impl PaneContent for EnvironmentManagementPane { } fn focus(&self, ctx: &mut ViewContext) { - self.environments_page_view(ctx) - .update(ctx, |view, ctx| view.focus(ctx)); + ctx.focus(&self.view); } fn shareable_link( diff --git a/app/src/pane_group/pane/mod.rs b/app/src/pane_group/pane/mod.rs index bd0608ea..ae8b5b5a 100644 --- a/app/src/pane_group/pane/mod.rs +++ b/app/src/pane_group/pane/mod.rs @@ -61,7 +61,6 @@ use crate::pane_group::pane::get_started_view::GetStartedView; use crate::server::network_log_view::NetworkLogView; use crate::server::telemetry::SharingDialogSource; use crate::settings::PaneSettings; -use crate::settings_view::environments_page::EnvironmentsPageView; use crate::settings_view::SettingsView; use crate::terminal::available_shells::AvailableShell; use crate::terminal::TerminalView; @@ -214,9 +213,9 @@ impl PaneId { Self::new_from_ctx(IPaneType::EnvVarCollection, ctx) } - /// Creates a [`PaneId`] from a [`ViewContext>`] + /// Creates a [`PaneId`] from a [`ViewContext>`] (environment management stub) pub fn from_environment_management_pane_ctx( - ctx: &ViewContext>, + ctx: &ViewContext>, ) -> Self { Self::new_from_ctx(IPaneType::EnvironmentManagement, ctx) } @@ -312,9 +311,9 @@ impl PaneId { Self::new(IPaneType::EnvVarCollection, env_var_collection_view) } - /// Creates a [`PaneId`] from a [`PaneView`] entity ID. + /// Creates a [`PaneId`] from a [`PaneView`] entity ID (environment management stub). pub fn from_environment_management_pane_view( - environment_management_pane_view: &ViewHandle>, + environment_management_pane_view: &ViewHandle>, ) -> Self { Self::new( IPaneType::EnvironmentManagement, @@ -469,7 +468,7 @@ impl PaneId { ChildView::>::with_id(self.0.pane_view_id).finish() } IPaneType::EnvironmentManagement => { - ChildView::>::with_id(self.0.pane_view_id).finish() + ChildView::>::with_id(self.0.pane_view_id).finish() } IPaneType::Workflow => { ChildView::>::with_id(self.0.pane_view_id).finish() diff --git a/app/src/server/server_api.rs b/app/src/server/server_api.rs index 268aa32e..4280f9c4 100644 --- a/app/src/server/server_api.rs +++ b/app/src/server/server_api.rs @@ -1264,8 +1264,6 @@ impl ServerApiProvider { ServerExperiments::handle(ctx).update(ctx, |state, ctx| { state.apply_latest_state(experiments, ctx); }); - - settings_view::handle_experiment_change(ctx); } /// Constructs a new SeverApiProvider for tests. diff --git a/app/src/settings_view/ai_page.rs b/app/src/settings_view/ai_page.rs index 5ec492f6..6b158694 100644 --- a/app/src/settings_view/ai_page.rs +++ b/app/src/settings_view/ai_page.rs @@ -35,13 +35,7 @@ use regex::Regex; use settings::{Setting, ToggleableSetting}; use strum::IntoEnumIterator; -use super::custom_inference_modal::{ - CustomEndpointModal, CustomEndpointModalEvent, CustomEndpointModalViewState, -}; use super::execution_profile_view::{ExecutionProfileView, ExecutionProfileViewEvent}; -use super::remove_custom_endpoint_confirmation_dialog::{ - RemoveCustomEndpointConfirmationDialog, RemoveCustomEndpointConfirmationDialogEvent, -}; use super::set_default_model_modal::{SetDefaultModelModalBody, SetDefaultModelModalBodyEvent}; use super::settings_page::{ build_sub_header, build_toggle_element, render_body_item_label, @@ -708,13 +702,6 @@ pub struct AISettingsPageView { #[cfg(feature = "local_fs")] add_router_button: ViewHandle, - // Custom inference (custom endpoints) - custom_endpoint_modal_state: CustomEndpointModalViewState, - remove_custom_endpoint_confirmation_dialog: ViewHandle, - pending_remove_custom_endpoint_index: Option, - custom_inference_add_button: ViewHandle, - custom_endpoint_edit_buttons: Vec>, - // Prompt offering to switch the default Agent Mode model after a BYO key or // custom endpoint is saved while the default isn't backed by a credential. set_default_model_modal: ModalViewState>, @@ -765,7 +752,6 @@ impl AISettingsPageView { ctx, ); - me.sync_custom_endpoint_buttons(ctx); ctx.notify(); } }); @@ -1075,7 +1061,6 @@ impl AISettingsPageView { // Re-render if teams-related data changed that may affect whether features such as voice input are enabled. Self::refresh_base_model_menu(&me.base_model_dropdown, ctx); Self::refresh_coding_model_menu(&me.coding_model_dropdown, ctx); - me.sync_custom_endpoint_buttons(ctx); ctx.notify(); }); @@ -1146,7 +1131,6 @@ impl AISettingsPageView { Self::refresh_base_model_menu(&me.base_model_dropdown, ctx); Self::refresh_coding_model_menu(&me.coding_model_dropdown, ctx); me.sync_context_window_editor(ctx, false); - me.sync_custom_endpoint_buttons(ctx); // Driving the prompt off the key-store update (rather than the editor's // blur/Enter) means it fires reliably however the key was committed — // clicking outside the field, pressing Enter, or tabbing away. @@ -1229,7 +1213,6 @@ impl AISettingsPageView { Self::refresh_mcp_allowlist_dropdown(&me.mcp_allowlist_dropdown, ctx); Self::refresh_mcp_denylist_dropdown(&me.mcp_denylist_dropdown, ctx); me.sync_context_window_editor(ctx, true); - me.sync_custom_endpoint_buttons(ctx); } AISettingsChangedEvent::VoiceInputEnabled { .. } => { me.update_voice_input_dropdown_enablement(ctx); @@ -1719,68 +1702,6 @@ impl AISettingsPageView { button.set_disabled(!is_any_ai_enabled, ctx); }); - // Custom inference - let custom_inference_controls_enabled = - is_any_ai_enabled && UserWorkspaces::as_ref(ctx).is_custom_inference_enabled(ctx); - let custom_inference_add_button = ctx.add_typed_action_view(|_| { - ActionButton::new("+ Add custom model", SecondaryTheme) - .with_size(ButtonSize::Small) - .on_click(|ctx| { - ctx.dispatch_typed_action(AISettingsPageAction::OpenAddCustomEndpointModal); - }) - }); - custom_inference_add_button.update(ctx, |button, ctx| { - button.set_disabled(!custom_inference_controls_enabled, ctx); - }); - - let custom_endpoint_modal_body = - ctx.add_typed_action_view(|ctx| CustomEndpointModal::new(None, None, ctx)); - ctx.subscribe_to_view(&custom_endpoint_modal_body, |me, _, event, ctx| { - me.handle_custom_endpoint_modal_event(event, ctx); - }); - - let custom_endpoint_modal_view = ctx.add_typed_action_view(|ctx| { - Modal::new( - Some("Add custom endpoint".to_string()), - custom_endpoint_modal_body.clone(), - ctx, - ) - .with_modal_style(UiComponentStyles { - width: Some(560.), - ..Default::default() - }) - .with_header_style(UiComponentStyles { - padding: Some(Coords { - top: 24., - bottom: 0., - left: 24., - right: 24., - }), - font_size: Some(16.), - font_weight: Some(Weight::Bold), - ..Default::default() - }) - .with_body_style(UiComponentStyles { - padding: Some(Coords { - top: 0., - bottom: 24., - left: 24., - right: 0., - }), - ..Default::default() - }) - .with_background_opacity(100) - .with_max_height_percentage(CUSTOM_ENDPOINT_MODAL_MAX_HEIGHT_PERCENTAGE) - .with_dismiss_on_click() - .with_dismiss_keystroke(Keystroke::parse("escape").unwrap()) - }); - ctx.subscribe_to_view(&custom_endpoint_modal_view, |me, _, event, ctx| { - me.handle_custom_endpoint_modal_close_event(event, ctx); - }); - - let custom_endpoint_modal_state = - CustomEndpointModalViewState::new(ModalViewState::new(custom_endpoint_modal_view)); - let set_default_model_modal_body = ctx.add_typed_action_view(SetDefaultModelModalBody::new); ctx.subscribe_to_view(&set_default_model_modal_body, |me, _, event, ctx| { me.handle_set_default_model_modal_event(event, ctx); @@ -1813,21 +1734,6 @@ impl AISettingsPageView { let set_default_model_modal = ModalViewState::new(set_default_model_modal_view); let last_seen_provider_keys = ApiKeyManager::as_ref(ctx).keys().clone(); - let remove_custom_endpoint_confirmation_dialog = - ctx.add_typed_action_view(RemoveCustomEndpointConfirmationDialog::new); - ctx.subscribe_to_view( - &remove_custom_endpoint_confirmation_dialog, - |me, _, event, ctx| { - me.handle_remove_custom_endpoint_confirmation_dialog_event(event, ctx); - }, - ); - - let custom_endpoint_edit_buttons = Self::create_custom_endpoint_edit_buttons( - ApiKeyManager::as_ref(ctx).keys().custom_endpoints.len(), - custom_inference_controls_enabled, - ctx, - ); - let agent_toolbar_inline_editor = ctx.add_typed_action_view(|ctx| { AgentToolbarInlineEditor::new(AgentToolbarEditorMode::AgentView, ctx) }); @@ -1959,11 +1865,6 @@ impl AISettingsPageView { router_views, #[cfg(feature = "local_fs")] add_router_button, - custom_endpoint_modal_state, - remove_custom_endpoint_confirmation_dialog, - pending_remove_custom_endpoint_index: None, - custom_inference_add_button, - custom_endpoint_edit_buttons, set_default_model_modal, last_seen_provider_keys, #[cfg(not(target_family = "wasm"))] @@ -1986,17 +1887,9 @@ impl AISettingsPageView { ctx.notify(); } - pub fn get_modal_content(&self, app: &AppContext) -> Option> { - if self.custom_endpoint_modal_state.is_open() { - Some(self.custom_endpoint_modal_state.render()) - } else if self.set_default_model_modal.is_open() { + pub fn get_modal_content(&self, _app: &AppContext) -> Option> { + if self.set_default_model_modal.is_open() { Some(self.set_default_model_modal.render()) - } else if self - .remove_custom_endpoint_confirmation_dialog - .as_ref(app) - .is_visible() - { - Some(ChildView::new(&self.remove_custom_endpoint_confirmation_dialog).finish()) } else { None } @@ -2174,336 +2067,6 @@ impl AISettingsPageView { self.show_set_default_model_modal(description, choices, ctx); } - /// After a custom endpoint is added or saved, offer to switch the default - /// Agent Mode model to one of its models. - fn maybe_prompt_set_default_model_for_custom_endpoint( - &mut self, - endpoint_index: usize, - ctx: &mut ViewContext, - ) { - if !Self::can_use_custom_inference_controls(ctx) { - return; - } - if !Self::should_offer_default_model_switch(ctx) { - return; - } - let Some(endpoint) = ApiKeyManager::as_ref(ctx) - .keys() - .custom_endpoints - .get(endpoint_index) - .cloned() - else { - return; - }; - // Build directly from the endpoint's models rather than the synthetic - // `custom_llms`, which are rebuilt asynchronously on `KeysUpdated`. - let choices: Vec<(LLMId, String)> = endpoint - .models - .iter() - .filter(|m| !m.name.trim().is_empty() && !m.config_key.is_empty()) - .map(|m| { - ( - LLMId::from(m.config_key.clone()), - m.display_label().to_string(), - ) - }) - .collect(); - if choices.is_empty() { - return; - } - let current_default = Self::active_base_model_display_name(ctx); - let description = format!( - "You added the \"{}\" custom endpoint, but your default model is currently set to \ - {current_default}, which won't work without Warp credits. Would you like to change \ - your default model?", - endpoint.name - ); - self.show_set_default_model_modal(description, choices, ctx); - } - - fn sync_custom_endpoint_buttons(&mut self, ctx: &mut ViewContext) { - let enabled = Self::can_use_custom_inference_controls(ctx); - - self.custom_inference_add_button.update(ctx, |button, ctx| { - button.set_disabled(!enabled, ctx); - }); - - let endpoint_count = ApiKeyManager::as_ref(ctx).keys().custom_endpoints.len(); - if self.custom_endpoint_edit_buttons.len() != endpoint_count { - self.custom_endpoint_edit_buttons = - Self::create_custom_endpoint_edit_buttons(endpoint_count, enabled, ctx); - } else { - for button in &self.custom_endpoint_edit_buttons { - button.update(ctx, |button, ctx| { - button.set_disabled(!enabled, ctx); - }); - } - } - } - - fn create_custom_endpoint_edit_buttons( - count: usize, - enabled: bool, - ctx: &mut ViewContext, - ) -> Vec> { - (0..count) - .map(|index| { - let button = ctx.add_typed_action_view(move |_| { - ActionButton::new("Edit", SecondaryTheme) - .with_icon(Icon::Pencil) - .with_size(ButtonSize::Small) - .on_click(move |ctx| { - ctx.dispatch_typed_action( - AISettingsPageAction::OpenEditCustomEndpointModal(index), - ); - }) - }); - button.update(ctx, |button, ctx| { - button.set_disabled(!enabled, ctx); - }); - button - }) - .collect() - } - fn can_use_custom_inference_controls(app: &AppContext) -> bool { - FeatureFlag::CustomInferenceEndpoints.is_enabled() - && AISettings::as_ref(app).is_any_ai_enabled(app) - && UserWorkspaces::as_ref(app).is_custom_inference_enabled(app) - } - - fn show_add_custom_endpoint_modal(&mut self, ctx: &mut ViewContext) { - if !Self::can_use_custom_inference_controls(ctx) { - return; - } - self.remove_custom_endpoint_confirmation_dialog - .update(ctx, |dialog, ctx| { - dialog.hide(ctx); - }); - self.pending_remove_custom_endpoint_index = None; - - self.custom_endpoint_modal_state - .set_title(Some("Add custom endpoint".to_string()), ctx); - self.custom_endpoint_modal_state.prefill(None, None, ctx); - self.custom_endpoint_modal_state.open(ctx); - ctx.emit(AISettingsPageEvent::ShowModal); - ctx.notify(); - } - - fn show_edit_custom_endpoint_modal(&mut self, index: usize, ctx: &mut ViewContext) { - if !Self::can_use_custom_inference_controls(ctx) { - return; - } - let endpoint = ApiKeyManager::as_ref(ctx) - .keys() - .custom_endpoints - .get(index) - .cloned(); - if endpoint.is_none() { - return; - } - - self.remove_custom_endpoint_confirmation_dialog - .update(ctx, |dialog, ctx| { - dialog.hide(ctx); - }); - self.pending_remove_custom_endpoint_index = None; - - self.custom_endpoint_modal_state - .set_title(Some("Edit custom endpoint".to_string()), ctx); - self.custom_endpoint_modal_state - .prefill(endpoint.as_ref(), Some(index), ctx); - self.custom_endpoint_modal_state.open(ctx); - ctx.emit(AISettingsPageEvent::ShowModal); - ctx.notify(); - } - - fn hide_custom_endpoint_modal(&mut self, ctx: &mut ViewContext) { - self.custom_endpoint_modal_state.close(ctx); - ctx.emit(AISettingsPageEvent::HideModal); - ctx.notify(); - } - - fn handle_custom_endpoint_modal_close_event( - &mut self, - event: &ModalEvent, - ctx: &mut ViewContext, - ) { - match event { - ModalEvent::Close => { - self.hide_custom_endpoint_modal(ctx); - } - } - } - - fn handle_custom_endpoint_modal_event( - &mut self, - event: &CustomEndpointModalEvent, - ctx: &mut ViewContext, - ) { - match event { - CustomEndpointModalEvent::Close => { - self.hide_custom_endpoint_modal(ctx); - } - CustomEndpointModalEvent::AddEndpoint { - name, - url, - api_key, - models, - } => { - if !Self::can_use_custom_inference_controls(ctx) { - self.hide_custom_endpoint_modal(ctx); - return; - } - ApiKeyManager::handle(ctx).update(ctx, |manager, ctx| { - manager.add_custom_endpoint( - name.clone(), - url.clone(), - api_key.clone(), - models.clone(), - ctx, - ); - }); - self.hide_custom_endpoint_modal(ctx); - - let window_id = ctx.window_id(); - crate::ToastStack::handle(ctx).update(ctx, |toast_stack, ctx| { - let toast = crate::view_components::DismissibleToast::success( - "Endpoint added".to_string(), - ); - toast_stack.add_ephemeral_toast(toast, window_id, ctx); - }); - - // The new endpoint is appended last. - let new_index = ApiKeyManager::as_ref(ctx) - .keys() - .custom_endpoints - .len() - .saturating_sub(1); - self.maybe_prompt_set_default_model_for_custom_endpoint(new_index, ctx); - ctx.notify(); - } - CustomEndpointModalEvent::SaveEndpoint { - index, - name, - url, - api_key, - models, - } => { - if !Self::can_use_custom_inference_controls(ctx) { - self.hide_custom_endpoint_modal(ctx); - return; - } - ApiKeyManager::handle(ctx).update(ctx, |manager, ctx| { - manager.save_custom_endpoint( - *index, - name.clone(), - url.clone(), - api_key.clone(), - models.clone(), - ctx, - ); - }); - self.hide_custom_endpoint_modal(ctx); - - let window_id = ctx.window_id(); - crate::ToastStack::handle(ctx).update(ctx, |toast_stack, ctx| { - let toast = crate::view_components::DismissibleToast::success( - "Endpoint saved".to_string(), - ); - toast_stack.add_ephemeral_toast(toast, window_id, ctx); - }); - self.maybe_prompt_set_default_model_for_custom_endpoint(*index, ctx); - ctx.notify(); - } - CustomEndpointModalEvent::RemoveEndpoint { index } => { - if !Self::can_use_custom_inference_controls(ctx) { - self.hide_custom_endpoint_modal(ctx); - return; - } - self.hide_custom_endpoint_modal(ctx); - self.show_remove_custom_endpoint_confirmation_dialog(*index, ctx); - } - } - } - - fn show_remove_custom_endpoint_confirmation_dialog( - &mut self, - index: usize, - ctx: &mut ViewContext, - ) { - if !Self::can_use_custom_inference_controls(ctx) { - return; - } - let endpoint = ApiKeyManager::as_ref(ctx) - .keys() - .custom_endpoints - .get(index) - .cloned(); - let Some(endpoint) = endpoint else { - return; - }; - - let model_labels = endpoint - .models - .iter() - .map(|model| model.alias.clone().unwrap_or_else(|| model.name.clone())) - .filter(|s| !s.trim().is_empty()) - .collect(); - - self.pending_remove_custom_endpoint_index = Some(index); - self.remove_custom_endpoint_confirmation_dialog - .update(ctx, |dialog, ctx| { - dialog.show(index, endpoint.name.clone(), model_labels, ctx); - }); - ctx.notify(); - } - - fn handle_remove_custom_endpoint_confirmation_dialog_event( - &mut self, - event: &RemoveCustomEndpointConfirmationDialogEvent, - ctx: &mut ViewContext, - ) { - match event { - RemoveCustomEndpointConfirmationDialogEvent::Cancel => { - self.pending_remove_custom_endpoint_index = None; - self.remove_custom_endpoint_confirmation_dialog - .update(ctx, |dialog, ctx| { - dialog.hide(ctx); - }); - ctx.notify(); - } - RemoveCustomEndpointConfirmationDialogEvent::Confirm(index) => { - if !Self::can_use_custom_inference_controls(ctx) { - self.pending_remove_custom_endpoint_index = None; - self.remove_custom_endpoint_confirmation_dialog - .update(ctx, |dialog, ctx| { - dialog.hide(ctx); - }); - ctx.notify(); - return; - } - ApiKeyManager::handle(ctx).update(ctx, |manager, ctx| { - manager.remove_custom_endpoint(*index, ctx); - }); - self.pending_remove_custom_endpoint_index = None; - self.remove_custom_endpoint_confirmation_dialog - .update(ctx, |dialog, ctx| { - dialog.hide(ctx); - }); - self.sync_custom_endpoint_buttons(ctx); - - let window_id = ctx.window_id(); - crate::ToastStack::handle(ctx).update(ctx, |toast_stack, ctx| { - let toast = crate::view_components::DismissibleToast::success( - "Endpoint removed".to_string(), - ); - toast_stack.add_ephemeral_toast(toast, window_id, ctx); - }); - ctx.notify(); - } - } - } - #[cfg(not(target_family = "wasm"))] fn create_grok_code_editor(ctx: &mut ViewContext) -> ViewHandle { ctx.add_typed_action_view(|ctx| { @@ -3635,9 +3198,6 @@ pub enum AISettingsPageAction { #[cfg(feature = "local_fs")] OpenAddCustomRouter, - // Custom inference - OpenAddCustomEndpointModal, - OpenEditCustomEndpointModal(usize), ConnectGrokSubscription, DisconnectGrokSubscription, @@ -4440,12 +4000,6 @@ impl TypedActionView for AISettingsPageView { AISettingsPageAction::OpenAddCustomRouter => { ctx.emit(AISettingsPageEvent::OpenCustomRouterEditor(None)); } - AISettingsPageAction::OpenAddCustomEndpointModal => { - self.show_add_custom_endpoint_modal(ctx); - } - AISettingsPageAction::OpenEditCustomEndpointModal(index) => { - self.show_edit_custom_endpoint_modal(*index, ctx); - } AISettingsPageAction::ToggleCloudHandoff => { AISettings::handle(ctx).update(ctx, |settings, ctx| { report_if_error!(settings @@ -8528,11 +8082,7 @@ impl ApiKeysWidget { .with_child(chips) .finish(); - let edit_button = view - .custom_endpoint_edit_buttons - .get(index) - .map(|button| button.as_ref(app).render(app)) - .unwrap_or_else(|| Empty::new().finish()); + let edit_button = Empty::new().finish(); let row = Flex::row() .with_main_axis_size(MainAxisSize::Max) @@ -8782,7 +8332,6 @@ impl SettingsWidget for ApiKeysWidget { .with_main_axis_alignment(MainAxisAlignment::SpaceBetween) .with_cross_axis_alignment(CrossAxisAlignment::Center) .with_child(header_left) - .with_child(view.custom_inference_add_button.as_ref(app).render(app)) .finish(); column.add_child( diff --git a/app/src/settings_view/billing_and_usage/billing_cycle_usage_common.rs b/app/src/settings_view/billing_and_usage/billing_cycle_usage_common.rs deleted file mode 100644 index cafdb75e..00000000 --- a/app/src/settings_view/billing_and_usage/billing_cycle_usage_common.rs +++ /dev/null @@ -1,421 +0,0 @@ -use std::cell::RefCell; -use std::collections::HashMap; - -use galaxy_core::ui::appearance::Appearance; -use pathfinder_color::ColorU; -use pathfinder_geometry::vector::vec2f; -use thousands::Separable; -use warpui::elements::{ - Align, Border, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, DropShadow, Empty, - Flex, MainAxisAlignment, MainAxisSize, MouseStateHandle, ParentElement, Radius, Shrinkable, - Text, -}; -use warpui::fonts::{Properties, Weight}; -use warpui::Element; - -use crate::settings_view::billing_and_usage_page_v2::{ - AGGREGATE_CREDITS_DOT_COLOR, AMBIENT_CREDITS_DOT_COLOR, BASE_CREDITS_DOT_COLOR, - BONUS_CREDITS_DOT_COLOR, PAYG_CREDITS_DOT_COLOR, -}; -use crate::ui_components::blended_colors; -use crate::workspaces::workspace::{ - AiCreditsUsageAndCostSubjectType, AiCreditsUsageAndCostType, AiCreditsUsageBucket, - BillingCycleUsageEntry, -}; - -// for a bunch of this (min fill ratio, cost type order, ... ) -// you will find analogous ts code in warp-server -pub const ROW_BORDER_RADIUS: f32 = 8.; -pub const ROW_BORDER_WIDTH: f32 = 1.; -pub const TOOLTIP_GAP: f32 = 6.; - -const COST_TYPE_ORDER: &[AiCreditsUsageAndCostType] = &[ - AiCreditsUsageAndCostType::BaseLimit, - AiCreditsUsageAndCostType::BonusGrant, - AiCreditsUsageAndCostType::Payg, - AiCreditsUsageAndCostType::AmbientBonusGrant, -]; -const BUCKET_ORDER: &[AiCreditsUsageBucket] = &[ - AiCreditsUsageBucket::Ai, - AiCreditsUsageBucket::Compute, - AiCreditsUsageBucket::Platform, -]; - -/// One colored slice of the stacked bar. `cost_type` drives color; `usage_bucket` -/// drives the tooltip breakdown. -#[derive(Clone, Debug)] -pub struct BarSegment { - pub cost_type: AiCreditsUsageAndCostType, - pub usage_bucket: AiCreditsUsageBucket, - pub credits: i64, - pub cost_cents: i64, -} - -/// Shared mouse-state bag for the billing-and-usage section: the -/// All/Local/Cloud filter pills plus a tooltip handle for every interactive -/// element keyed by string id (per-member rows, team-totals cards, ...). -pub struct BillingUsageMouseStates { - pub filter_all: MouseStateHandle, - pub filter_local: MouseStateHandle, - pub filter_cloud: MouseStateHandle, - tooltip_by_subject: RefCell>, -} - -impl Default for BillingUsageMouseStates { - fn default() -> Self { - Self { - filter_all: MouseStateHandle::default(), - filter_local: MouseStateHandle::default(), - filter_cloud: MouseStateHandle::default(), - tooltip_by_subject: RefCell::new(HashMap::new()), - } - } -} - -impl BillingUsageMouseStates { - pub fn tooltip_mouse_state(&self, key: &str) -> MouseStateHandle { - let mut map = self.tooltip_by_subject.borrow_mut(); - map.entry(key.to_string()).or_default().clone() - } -} - -/// Swatch color for one cost-type bucket, mirroring the legend palette. -pub fn cost_type_color(cost_type: &AiCreditsUsageAndCostType) -> ColorU { - match cost_type { - AiCreditsUsageAndCostType::BaseLimit => BASE_CREDITS_DOT_COLOR, - AiCreditsUsageAndCostType::BonusGrant => BONUS_CREDITS_DOT_COLOR, - AiCreditsUsageAndCostType::Payg => PAYG_CREDITS_DOT_COLOR, - AiCreditsUsageAndCostType::AmbientBonusGrant => AMBIENT_CREDITS_DOT_COLOR, - AiCreditsUsageAndCostType::Aggregate => AGGREGATE_CREDITS_DOT_COLOR, - AiCreditsUsageAndCostType::Other(_) => BASE_CREDITS_DOT_COLOR, - } -} - -fn cost_type_label(cost_type: &AiCreditsUsageAndCostType) -> &'static str { - match cost_type { - AiCreditsUsageAndCostType::BaseLimit => "Base", - AiCreditsUsageAndCostType::BonusGrant => "Add-ons", - AiCreditsUsageAndCostType::Payg => "Pay-as-you-go", - AiCreditsUsageAndCostType::AmbientBonusGrant => "Cloud-only", - AiCreditsUsageAndCostType::Aggregate => "Combined", - AiCreditsUsageAndCostType::Other(_) => "Other", - } -} - -fn bucket_label(bucket: &AiCreditsUsageBucket) -> &'static str { - match bucket { - AiCreditsUsageBucket::Ai => "AI", - AiCreditsUsageBucket::Compute => "Compute", - AiCreditsUsageBucket::Platform => "Platform", - AiCreditsUsageBucket::SuggestedCodeDiffs => "Suggested code diffs", - AiCreditsUsageBucket::Voice => "Voice", - AiCreditsUsageBucket::Aggregate => "Total", - AiCreditsUsageBucket::Other(_) => "Other", - } -} - -fn cost_type_rank(cost_type: &AiCreditsUsageAndCostType) -> usize { - COST_TYPE_ORDER - .iter() - .position(|c| c == cost_type) - .unwrap_or(COST_TYPE_ORDER.len()) -} - -fn bucket_rank(bucket: &AiCreditsUsageBucket) -> usize { - BUCKET_ORDER - .iter() - .position(|b| b == bucket) - .unwrap_or(BUCKET_ORDER.len()) -} - -fn segment_sort_key(segment: &BarSegment) -> (usize, usize) { - ( - cost_type_rank(&segment.cost_type), - bucket_rank(&segment.usage_bucket), - ) -} - -/// Group `entries` by `(cost_type, usage_bucket)` into [`BarSegment`]s; returns -/// sorted segments plus row totals. Linear Vec lookup since cynic enums don't -/// impl Hash and per-row entry counts are small. -pub fn aggregate_segments<'a>( - entries: impl IntoIterator, -) -> (Vec, i64, i64) { - let mut segments: Vec = Vec::new(); - - for entry in entries { - if let Some(existing) = segments - .iter_mut() - .find(|s| s.cost_type == entry.cost_type && s.usage_bucket == entry.usage_bucket) - { - existing.credits += entry.credits_used as i64; - existing.cost_cents += entry.cost_cents as i64; - } else { - segments.push(BarSegment { - cost_type: entry.cost_type.clone(), - usage_bucket: entry.usage_bucket.clone(), - credits: entry.credits_used as i64, - cost_cents: entry.cost_cents as i64, - }); - } - } - - segments.retain(|s| s.credits > 0); - segments.sort_by_key(segment_sort_key); - - let total_credits = segments.iter().map(|s| s.credits).sum(); - let total_cost_cents = segments.iter().map(|s| s.cost_cents).sum(); - - (segments, total_credits, total_cost_cents) -} - -/// Drops Voice / SuggestedCodeDiffs entries from the usage view. -/// -/// These buckets are tracked server-side against their own dedicated per-cycle -/// limits (`VoiceRequestLimit` / `SuggestedCodeDiffsLimit`) rather than the -/// AI/Compute base credit pool — see -/// `model/sql/ai_credits_usage_and_cost/get_base_limits_usage.sql` and -/// `isBaseLimitExhaustedForBucket` in warp-server. Records are written with -/// `cost_type = BASE_LIMIT` and `cost_cents = 0`, so surfacing them here -/// would inflate the per-row `total_credits` and skew the `used / limit` -/// math without contributing to anything the user is actually billed for. -/// -/// TODO: this also hides the rare case where a user blows past their -/// dedicated Voice or SuggestedCodeDiffs limit and the resolver falls -/// through to bonus grants — those entries would have real `cost_cents` -/// and *do* draw down add-on credits. In practice ~nobody (maybe ZL?) hits -/// those limits, so we filter unconditionally for now; revisit if usage of -/// those features ever grows enough that the overflow matters. -pub fn filter_legacy_buckets(entries: &[BillingCycleUsageEntry]) -> Vec { - entries - .iter() - .filter(|e| { - e.usage_bucket != AiCreditsUsageBucket::Voice - && e.usage_bucket != AiCreditsUsageBucket::SuggestedCodeDiffs - }) - .cloned() - .collect() -} - -/// Cost-type buckets to surface in the usage legend, in display order. -/// -/// Mirrors the buckets the stacked bars actually render: legacy buckets are -/// dropped (see [`filter_legacy_buckets`]) and a cost type only counts when it -/// has real usage (`credits_used > 0`), exactly like [`aggregate_segments`], -/// which retains only segments with `credits > 0`. Without the usage check a -/// zero-credit entry (e.g. an untouched base-limit row) would list "Base" in -/// the legend even though it contributed nothing to the chart. -pub fn legend_cost_types(entries: &[BillingCycleUsageEntry]) -> Vec { - let filtered = filter_legacy_buckets(entries); - [ - AiCreditsUsageAndCostType::BaseLimit, - AiCreditsUsageAndCostType::BonusGrant, - AiCreditsUsageAndCostType::Payg, - AiCreditsUsageAndCostType::AmbientBonusGrant, - AiCreditsUsageAndCostType::Aggregate, - ] - .into_iter() - .filter(|cost_type| { - filtered - .iter() - .any(|e| e.cost_type == *cost_type && e.credits_used > 0) - }) - .collect() -} - -/// "Is there any data in `entries` that's not my own?" -pub fn has_non_viewer_data(entries: &[BillingCycleUsageEntry], viewer_uid: Option<&str>) -> bool { - entries.iter().any(|e| match &e.subject_type { - AiCreditsUsageAndCostSubjectType::Team => e.credits_used > 0, - _ => match (e.subject_uid.as_deref(), viewer_uid) { - (Some(uid), Some(viewer)) => uid != viewer, - // Unknown subject — conservatively treat as non-viewer. - _ => true, - }, - }) -} - -pub fn format_credits(credits: i64) -> String { - credits.separate_with_commas() -} - -pub fn format_cost_cents(cents: i64) -> String { - let dollars = cents / 100; - let remainder = (cents.abs() % 100) as u8; - if dollars < 0 { - format!( - "-${}.{remainder:02}", - dollars.unsigned_abs().separate_with_commas() - ) - } else { - format!("${}.{remainder:02}", dollars.separate_with_commas()) - } -} - -/// Section subheader (e.g. "Team totals", "Member usage"). One step below -/// the v2 page's bold section title. -pub fn render_section_subheader(label: &str, appearance: &Appearance) -> Box { - Text::new_inline(label.to_string(), appearance.ui_font_family(), 14.) - .with_color(appearance.theme().active_ui_text_color().into()) - .with_style(Properties::default().weight(Weight::Medium)) - .finish() -} - -/// Per-cost-type breakdown card. Parameterized by raw segments and totals so -/// it can back team-totals card hovers as well as per-member row hovers. -pub fn render_breakdown_tooltip( - segments: &[BarSegment], - total_credits: i64, - total_cost_cents: i64, - appearance: &Appearance, -) -> Box { - let theme = appearance.theme(); - let font_family = appearance.ui_font_family(); - let bg = theme.background().into_solid(); - let main = blended_colors::text_main(theme, bg); - let sub = blended_colors::text_sub(theme, bg); - - let mut column = Flex::column() - .with_cross_axis_alignment(CrossAxisAlignment::Stretch) - .with_spacing(6.); - - for line in segments { - let label = if matches!(line.usage_bucket, AiCreditsUsageBucket::Aggregate) { - cost_type_label(&line.cost_type).to_string() - } else { - format!( - "{} ({})", - cost_type_label(&line.cost_type), - bucket_label(&line.usage_bucket) - ) - }; - - column.add_child(render_tooltip_row( - Some(cost_type_color(&line.cost_type)), - label, - line.credits, - line.cost_cents, - sub, - main, - font_family, - /* bold */ false, - )); - } - - // Divider before the total row. - column.add_child( - Container::new(Empty::new().finish()) - .with_padding_top(1.) - .with_background_color(theme.outline().into_solid()) - .finish(), - ); - - column.add_child(render_tooltip_row( - /* no swatch on the total row */ None, - "Total usage".to_string(), - total_credits, - total_cost_cents, - main, - main, - font_family, - /* bold */ true, - )); - - ConstrainedBox::new( - Container::new(column.finish()) - .with_background_color(bg) - .with_corner_radius(CornerRadius::with_all(Radius::Pixels(6.))) - .with_border(Border::all(1.).with_border_color(theme.outline().into_solid())) - .with_uniform_padding(10.) - .with_drop_shadow( - DropShadow::new_with_standard_offset_and_spread(ColorU::new(0, 0, 0, 48)) - .with_offset(vec2f(0., 4.)), - ) - .finish(), - ) - .with_min_width(200.) - .with_max_width(320.) - .finish() -} - -/// Single tooltip row: `[swatch + label] [spacer] [credits / cost]` with -/// fixed-width right-aligned number columns. -#[allow(clippy::too_many_arguments)] -fn render_tooltip_row( - swatch_color: Option, - label: String, - credits: i64, - cost_cents: i64, - label_color: ColorU, - value_color: ColorU, - font_family: warpui::fonts::FamilyId, - bold: bool, -) -> Box { - let style = if bold { - Properties::default().weight(Weight::Semibold) - } else { - Properties::default() - }; - - let label_text = Text::new_inline(label, font_family, 12.) - .with_color(label_color) - .with_style(style) - .finish(); - - let mut left = Flex::row().with_cross_axis_alignment(CrossAxisAlignment::Center); - if let Some(color) = swatch_color { - left.add_child( - ConstrainedBox::new( - Container::new(Empty::new().finish()) - .with_background_color(color) - .with_corner_radius(CornerRadius::with_all(Radius::Pixels(2.))) - .finish(), - ) - .with_width(10.) - .with_height(10.) - .finish(), - ); - left.add_child(Container::new(label_text).with_margin_left(8.).finish()); - } else { - left.add_child(label_text); - } - - let credits_text = Text::new_inline(format_credits(credits), font_family, 12.) - .with_color(value_color) - .with_style(style) - .finish(); - let cost_text = Text::new_inline(format_cost_cents(cost_cents), font_family, 12.) - .with_color(value_color) - .with_style(style) - .finish(); - let divider = Text::new_inline("/".to_string(), font_family, 12.) - .with_color(label_color) - .with_style(style) - .finish(); - - let credits_col = ConstrainedBox::new(Align::new(credits_text).right().finish()) - .with_width(60.) - .finish(); - let cost_col = ConstrainedBox::new(Align::new(cost_text).right().finish()) - .with_width(64.) - .finish(); - - let right = Flex::row() - .with_cross_axis_alignment(CrossAxisAlignment::Center) - .with_child(credits_col) - .with_child(Container::new(divider).with_horizontal_margin(3.).finish()) - .with_child(cost_col) - .finish(); - - Flex::row() - .with_cross_axis_alignment(CrossAxisAlignment::Center) - .with_main_axis_alignment(MainAxisAlignment::SpaceBetween) - .with_main_axis_size(MainAxisSize::Max) - .with_child(Shrinkable::new(1., left.finish()).finish()) - .with_child(right) - .finish() -} - -#[cfg(test)] -#[path = "billing_cycle_usage_common_tests.rs"] -mod tests; diff --git a/app/src/settings_view/billing_and_usage/billing_cycle_usage_common_tests.rs b/app/src/settings_view/billing_and_usage/billing_cycle_usage_common_tests.rs deleted file mode 100644 index 4bd1fcc0..00000000 --- a/app/src/settings_view/billing_and_usage/billing_cycle_usage_common_tests.rs +++ /dev/null @@ -1,376 +0,0 @@ -use super::{ - aggregate_segments, filter_legacy_buckets, has_non_viewer_data, legend_cost_types, BarSegment, -}; -use crate::workspaces::workspace::{ - AiCreditsUsageAndCostSubjectType, AiCreditsUsageAndCostType, AiCreditsUsageBucket, - AiCreditsUsageSource, BillingCycleUsageEntry, -}; - -const VIEWER_UID: &str = "viewer-uid"; -const OTHER_UID: &str = "other-uid"; - -fn entry( - subject_type: AiCreditsUsageAndCostSubjectType, - subject_uid: Option<&str>, - cost_type: AiCreditsUsageAndCostType, - usage_bucket: AiCreditsUsageBucket, - usage_source: AiCreditsUsageSource, - credits_used: i32, - cost_cents: i32, -) -> BillingCycleUsageEntry { - BillingCycleUsageEntry { - subject_type, - subject_uid: subject_uid.map(|s| s.to_string()), - subject_display_name: None, - cost_type, - usage_bucket, - usage_source, - credits_used, - cost_cents, - } -} - -/// Boilerplate viewer-owned User row for predicate tests. -fn viewer_user_entry() -> BillingCycleUsageEntry { - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageAndCostType::BaseLimit, - AiCreditsUsageBucket::Ai, - AiCreditsUsageSource::Local, - 10, - 0, - ) -} - -#[test] -fn has_non_viewer_data_returns_false_when_entries_empty() { - assert!(!has_non_viewer_data(&[], Some(VIEWER_UID))); -} - -#[test] -fn has_non_viewer_data_returns_false_when_only_viewer_user_rows() { - let entries = vec![viewer_user_entry(), viewer_user_entry()]; - assert!(!has_non_viewer_data(&entries, Some(VIEWER_UID))); -} - -#[test] -fn has_non_viewer_data_returns_true_for_team_aggregate_row() { - // TeamAggregate visibility represents "everyone else's usage" as a single - // Team-typed row, even when the workspace currently has only one member - // (e.g. a teammate left mid-cycle after incurring AI costs). - let entries = vec![ - viewer_user_entry(), - entry( - AiCreditsUsageAndCostSubjectType::Team, - None, - AiCreditsUsageAndCostType::Aggregate, - AiCreditsUsageBucket::Aggregate, - AiCreditsUsageSource::Aggregate, - 500, - 300, - ), - ]; - assert!(has_non_viewer_data(&entries, Some(VIEWER_UID))); -} - -#[test] -fn has_non_viewer_data_returns_true_for_other_user_row() { - // PerUserTotals / FullBreakdown emit per-user rows, so a departed teammate - // shows up as a User entry with a non-viewer UID. - let entries = vec![entry( - AiCreditsUsageAndCostSubjectType::User, - Some(OTHER_UID), - AiCreditsUsageAndCostType::BaseLimit, - AiCreditsUsageBucket::Ai, - AiCreditsUsageSource::Local, - 50, - 0, - )]; - assert!(has_non_viewer_data(&entries, Some(VIEWER_UID))); -} - -#[test] -fn has_non_viewer_data_returns_true_for_service_account_row() { - let entries = vec![entry( - AiCreditsUsageAndCostSubjectType::ServiceAccount, - Some("sa-uid"), - AiCreditsUsageAndCostType::BaseLimit, - AiCreditsUsageBucket::Ai, - AiCreditsUsageSource::Cloud, - 25, - 0, - )]; - assert!(has_non_viewer_data(&entries, Some(VIEWER_UID))); -} - -#[test] -fn has_non_viewer_data_treats_missing_subject_uid_as_non_viewer() { - // Defensive: a User row with no UID is conservatively treated as a non- - // viewer subject so we never accidentally drop team scaffolding. - let entries = vec![entry( - AiCreditsUsageAndCostSubjectType::User, - None, - AiCreditsUsageAndCostType::BaseLimit, - AiCreditsUsageBucket::Ai, - AiCreditsUsageSource::Local, - 1, - 0, - )]; - assert!(has_non_viewer_data(&entries, Some(VIEWER_UID))); -} - -#[test] -fn has_non_viewer_data_treats_missing_viewer_uid_as_non_viewer() { - // Signed-out / unidentified viewer: any subject we can't prove belongs - // to them counts as non-viewer data. - let entries = vec![viewer_user_entry()]; - assert!(has_non_viewer_data(&entries, None)); -} - -#[test] -fn filter_legacy_buckets_drops_voice_and_suggested_code_diffs_in_input_order() { - let entries = vec![ - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageAndCostType::BaseLimit, - AiCreditsUsageBucket::Ai, - AiCreditsUsageSource::Local, - 10, - 0, - ), - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageAndCostType::BaseLimit, - AiCreditsUsageBucket::Voice, - AiCreditsUsageSource::Local, - 3, - 0, - ), - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageAndCostType::BaseLimit, - AiCreditsUsageBucket::Compute, - AiCreditsUsageSource::Local, - 5, - 0, - ), - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageAndCostType::BaseLimit, - AiCreditsUsageBucket::SuggestedCodeDiffs, - AiCreditsUsageSource::Local, - 7, - 0, - ), - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageAndCostType::Aggregate, - AiCreditsUsageBucket::Aggregate, - AiCreditsUsageSource::Aggregate, - 100, - 50, - ), - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageAndCostType::BaseLimit, - AiCreditsUsageBucket::Platform, - AiCreditsUsageSource::Cloud, - 2, - 0, - ), - ]; - - let filtered = filter_legacy_buckets(&entries); - - let kept_buckets: Vec<_> = filtered.iter().map(|e| e.usage_bucket.clone()).collect(); - assert_eq!( - kept_buckets, - vec![ - AiCreditsUsageBucket::Ai, - AiCreditsUsageBucket::Compute, - AiCreditsUsageBucket::Aggregate, - AiCreditsUsageBucket::Platform, - ], - "expected Voice + SuggestedCodeDiffs dropped while preserving the rest in input order" - ); -} - -#[test] -fn aggregate_segments_merges_dupes_drops_zeros_and_sorts() { - let entries = [ - // Same (BonusGrant, Compute) appears twice across different sources; - // should merge into one segment. - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageAndCostType::BonusGrant, - AiCreditsUsageBucket::Compute, - AiCreditsUsageSource::Local, - 10, - 5, - ), - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageAndCostType::BonusGrant, - AiCreditsUsageBucket::Compute, - AiCreditsUsageSource::Cloud, - 7, - 3, - ), - // BaseLimit/Ai — should sort before any BonusGrant entry. - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageAndCostType::BaseLimit, - AiCreditsUsageBucket::Ai, - AiCreditsUsageSource::Local, - 20, - 0, - ), - // Zero-credit entry: must be dropped before totals are computed (so - // the stray cost_cents don't leak into the row total). - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageAndCostType::Payg, - AiCreditsUsageBucket::Ai, - AiCreditsUsageSource::Local, - 0, - 42, - ), - ]; - - let (segments, total_credits, total_cost_cents) = aggregate_segments(entries.iter()); - - let key = |s: &BarSegment| (s.cost_type.clone(), s.usage_bucket.clone()); - let keys: Vec<_> = segments.iter().map(key).collect(); - assert_eq!( - keys, - vec![ - ( - AiCreditsUsageAndCostType::BaseLimit, - AiCreditsUsageBucket::Ai - ), - ( - AiCreditsUsageAndCostType::BonusGrant, - AiCreditsUsageBucket::Compute - ), - ], - "expected BaseLimit/Ai before BonusGrant/Compute, Payg zero-credit dropped" - ); - - let bonus = &segments[1]; - assert_eq!(bonus.credits, 17, "10 + 7 merged credits"); - assert_eq!(bonus.cost_cents, 8, "5 + 3 merged cost cents"); - - // Totals are summed *after* the zero-credit segment is dropped, so the - // stray 42 cents on the Payg/Ai entry must not appear here. - assert_eq!(total_credits, 20 + 17); - assert_eq!(total_cost_cents, 8); -} - -#[test] -fn legend_cost_types_excludes_zero_credit_bucket() { - // Regression: a base-limit row with no usage must not surface "Base" in - // the legend while only Pay-as-you-go credits were actually spent. - let entries = vec![ - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageAndCostType::BaseLimit, - AiCreditsUsageBucket::Ai, - AiCreditsUsageSource::Local, - 0, - 0, - ), - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageAndCostType::Payg, - AiCreditsUsageBucket::Ai, - AiCreditsUsageSource::Local, - 50, - 120, - ), - ]; - - assert_eq!( - legend_cost_types(&entries), - vec![AiCreditsUsageAndCostType::Payg], - "zero-credit BaseLimit row must be dropped from the legend" - ); -} - -#[test] -fn legend_cost_types_includes_used_buckets_in_display_order() { - // Buckets with real usage appear in the canonical legend order regardless - // of input order (Payg listed before BaseLimit here). - let entries = vec![ - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageAndCostType::Payg, - AiCreditsUsageBucket::Ai, - AiCreditsUsageSource::Local, - 5, - 10, - ), - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageAndCostType::BaseLimit, - AiCreditsUsageBucket::Ai, - AiCreditsUsageSource::Local, - 8, - 0, - ), - ]; - - assert_eq!( - legend_cost_types(&entries), - vec![ - AiCreditsUsageAndCostType::BaseLimit, - AiCreditsUsageAndCostType::Payg, - ], - "used buckets should render in canonical order, not input order" - ); -} - -#[test] -fn legend_cost_types_excludes_legacy_only_buckets() { - // Voice / SuggestedCodeDiffs usage is written as BaseLimit credits but is - // dropped from the bars; the legend must match and not show "Base". - let entries = vec![ - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageAndCostType::BaseLimit, - AiCreditsUsageBucket::Voice, - AiCreditsUsageSource::Local, - 12, - 0, - ), - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageAndCostType::BaseLimit, - AiCreditsUsageBucket::SuggestedCodeDiffs, - AiCreditsUsageSource::Local, - 4, - 0, - ), - ]; - - assert!( - legend_cost_types(&entries).is_empty(), - "legacy-only base-limit usage must not surface any legend bucket" - ); -} diff --git a/app/src/settings_view/billing_and_usage/billing_cycle_usage_rows.rs b/app/src/settings_view/billing_and_usage/billing_cycle_usage_rows.rs deleted file mode 100644 index c6799b44..00000000 --- a/app/src/settings_view/billing_and_usage/billing_cycle_usage_rows.rs +++ /dev/null @@ -1,838 +0,0 @@ -use std::collections::HashMap; - -use galaxy_core::channel::ChannelState; -use galaxy_core::ui::appearance::Appearance; -use itertools::Itertools as _; -use pathfinder_color::ColorU; -use pathfinder_geometry::vector::vec2f; -use warpui::elements::{ - Border, ChildAnchor, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, DropShadow, - Empty, Expanded, Flex, Hoverable, MainAxisAlignment, MainAxisSize, MouseStateHandle, - OffsetPositioning, ParentAnchor, ParentElement, ParentOffsetBounds, Radius, Shrinkable, Stack, - Text, -}; -use warpui::platform::Cursor; -use warpui::ui_components::components::UiComponent; -use warpui::{AppContext, Element, EventContext, SingletonEntity}; - -use crate::ai::AIRequestUsageModel; -use crate::auth::AuthStateProvider; -use crate::settings_view::billing_and_usage::billing_cycle_usage_common::{ - aggregate_segments, cost_type_color, format_cost_cents, format_credits, - render_breakdown_tooltip, render_section_subheader, BarSegment, BillingUsageMouseStates, - ROW_BORDER_RADIUS, ROW_BORDER_WIDTH, TOOLTIP_GAP, -}; -use crate::ui_components::blended_colors; -use crate::ui_components::icons::Icon; -use crate::workspaces::workspace::{ - AiCreditsUsageAndCostSubjectType, AiCreditsUsageAndCostType, AiCreditsUsageBucket, - AiCreditsUsageSource, BillingCycleUsageEntry, UsageVisibility, UsageVisibilityGranularity, - Workspace, WorkspaceMember, -}; - -const BAR_HEIGHT: f32 = 8.; -const MIN_FILL_RATIO: f32 = 0.05; -/// Size of the leading icons in the row credit cluster (coin + credit-card). -const ROW_ICON_SIZE: f32 = 12.; -/// Inner radius so the bar's curve sits flush against the card's inner border. -const BAR_CORNER_RADIUS: f32 = ROW_BORDER_RADIUS - ROW_BORDER_WIDTH; -const ROW_PADDING: f32 = 12.; - -const SELF_OWN_KEY: &str = "__self_own__"; -const OTHER_MEMBERS_KEY: &str = "__other_members__"; - -#[derive(Clone, Copy, Debug, Default, PartialEq, Eq)] -pub enum SourceFilter { - #[default] - All, - Local, - Cloud, -} - -impl SourceFilter { - pub fn label(self) -> &'static str { - match self { - SourceFilter::All => "All", - SourceFilter::Local => "Local", - SourceFilter::Cloud => "Cloud", - } - } - - fn matches(self, source: &AiCreditsUsageSource) -> bool { - match self { - SourceFilter::All => true, - SourceFilter::Local => *source == AiCreditsUsageSource::Local, - SourceFilter::Cloud => *source == AiCreditsUsageSource::Cloud, - } - } -} - -/// Aggregated usage for one subject (or the synthetic team aggregate). -#[derive(Debug)] -pub struct MemberUsageRow { - pub subject_type: AiCreditsUsageAndCostSubjectType, - pub subject_key: String, - /// Used to deep-link `ServiceAccount` rows to their Oz agent page. - pub subject_uid: Option, - pub display_name: String, - pub total_credits: i64, - pub total_cost_cents: i64, - /// Sorted by cost-type then bucket order; zero-credit entries dropped. - pub segments: Vec, - /// Denominator the row's stacked bar fills against. - pub bar_max_credits: i64, -} - -fn viewer_identity(app: &AppContext) -> (Option, String) { - let auth_state = AuthStateProvider::as_ref(app).get(); - let viewer_uid = auth_state.user_id().map(|uid| uid.as_string()); - let display_name = auth_state - .display_name() - .or_else(|| auth_state.username_for_display()) - .or_else(|| auth_state.user_email()) - .unwrap_or_else(|| "Your usage".to_string()); - (viewer_uid, display_name) -} - -struct GroupedSubjectUsage { - subject_type: AiCreditsUsageAndCostSubjectType, - display_name: String, - entries: Vec, -} - -impl MemberUsageRow { - fn for_viewer( - entries: &[BillingCycleUsageEntry], - viewer_uid: Option<&str>, - viewer_display_name: String, - source_filter: SourceFilter, - ) -> Self { - let viewer_entries = entries - .iter() - .filter(|e| source_filter.matches(&e.usage_source)) - // Defensive: positive-attribute to the viewer only. - .filter(|e| match (viewer_uid, e.subject_uid.as_deref()) { - (Some(uid), Some(entry_uid)) => uid == entry_uid, - _ => false, - }) - .collect_vec(); - let (segments, total_credits, total_cost_cents) = - aggregate_segments(viewer_entries.iter().copied()); - - Self { - subject_type: AiCreditsUsageAndCostSubjectType::User, - subject_key: SELF_OWN_KEY.to_string(), - subject_uid: viewer_uid.map(str::to_string), - display_name: viewer_display_name, - total_credits, - total_cost_cents, - segments, - bar_max_credits: total_credits.max(1), - } - } - - /// Viewer row built from a raw used-credits count, with no segment - /// breakdown. For callers that only have `AIRequestUsageModel`-style - /// data (no `billing_cycle_usage` entries / no workspace data). - fn for_viewer_from_total( - viewer_uid: Option, - viewer_display_name: String, - used: i64, - ) -> Self { - let segments = if used > 0 { - vec![BarSegment { - cost_type: AiCreditsUsageAndCostType::BaseLimit, - usage_bucket: AiCreditsUsageBucket::Ai, - credits: used, - cost_cents: 0, - }] - } else { - Vec::new() - }; - Self { - subject_type: AiCreditsUsageAndCostSubjectType::User, - subject_key: SELF_OWN_KEY.to_string(), - subject_uid: viewer_uid, - display_name: viewer_display_name, - total_credits: used, - total_cost_cents: 0, - segments, - bar_max_credits: used.max(1), - } - } - - /// Synthetic "Other members" aggregate row used by TeamAggregate - /// visibility — represents everyone except the viewer. - fn for_other_members(entries: &[BillingCycleUsageEntry]) -> Self { - let team_entries = entries - .iter() - .filter(|e| e.subject_type == AiCreditsUsageAndCostSubjectType::Team); - let (segments, total_credits, total_cost_cents) = aggregate_segments(team_entries); - - Self { - subject_type: AiCreditsUsageAndCostSubjectType::Team, - subject_key: OTHER_MEMBERS_KEY.to_string(), - subject_uid: None, - display_name: "Other members".to_string(), - total_credits, - total_cost_cents, - segments, - bar_max_credits: total_credits.max(1), - } - } - - /// Per-member rows for `PerUserTotals` / `FullBreakdown` visibility. - /// Iterates the workspace member list so zero-usage members still - /// get a row. Service accounts and other non-member subjects surface - /// as extra rows at the bottom, sorted by total credits desc. - fn for_each_member( - entries: &[BillingCycleUsageEntry], - members: &[WorkspaceMember], - source_filter: SourceFilter, - ) -> Vec { - // Group entries by subject for joining against the member list below. - let mut grouped: HashMap = HashMap::new(); - let mut unknown_counter = 0usize; - - for entry in entries - .iter() - .filter(|e| e.subject_type != AiCreditsUsageAndCostSubjectType::Team) - { - if !source_filter.matches(&entry.usage_source) { - continue; - } - - let key = match entry.subject_uid.as_deref() { - Some(uid) => format!("{:?}:{uid}", entry.subject_type), - None => { - unknown_counter += 1; - format!("{:?}:unknown-{unknown_counter}", entry.subject_type) - } - }; - let group = grouped.entry(key).or_insert_with(|| GroupedSubjectUsage { - subject_type: entry.subject_type.clone(), - display_name: entry - .subject_display_name - .clone() - .unwrap_or_else(|| "Unknown".to_string()), - entries: Vec::new(), - }); - group.entries.push(entry.clone()); - } - - let mut rows: Vec = Vec::with_capacity(members.len()); - - // One row per workspace member, including zero-usage members. - let mut seen_keys: std::collections::HashSet = Default::default(); - for member in members { - let key = format!( - "{:?}:{}", - AiCreditsUsageAndCostSubjectType::User, - member.uid.as_str() - ); - seen_keys.insert(key.clone()); - - let (segments, total_credits, total_cost_cents) = match grouped.remove(&key) { - Some(group) => aggregate_segments(group.entries.iter()), - None => (Vec::new(), 0, 0), - }; - - rows.push(Self { - subject_type: AiCreditsUsageAndCostSubjectType::User, - subject_key: key, - subject_uid: Some(member.uid.as_str().to_string()), - display_name: member.email.clone(), - total_credits, - total_cost_cents, - segments, - bar_max_credits: 0, - }); - } - - // Subjects not in the member list (typically service accounts) render after. - for (key, group) in grouped { - if seen_keys.contains(&key) { - continue; - } - // All entries in a group share the same subject_uid by construction - // (it's part of the grouping key), so first.is representative. - let subject_uid = group.entries.first().and_then(|e| e.subject_uid.clone()); - let (segments, total_credits, total_cost_cents) = - aggregate_segments(group.entries.iter()); - rows.push(Self { - subject_type: group.subject_type, - subject_key: key, - subject_uid, - display_name: group.display_name, - total_credits, - total_cost_cents, - segments, - bar_max_credits: 0, - }); - } - - // Sort by total credits desc, stable by subject_key. - rows.sort_by(|a, b| { - b.total_credits - .cmp(&a.total_credits) - .then_with(|| a.subject_key.cmp(&b.subject_key)) - }); - - rows - } -} - -fn build_rows( - workspace: &Workspace, - entries: &[BillingCycleUsageEntry], - visibility: &UsageVisibility, - source_filter: SourceFilter, - app: &AppContext, -) -> Vec { - let mut rows: Vec = match visibility.granularity { - UsageVisibilityGranularity::OwnOnly => { - let (viewer_uid, display_name) = viewer_identity(app); - vec![MemberUsageRow::for_viewer( - entries, - viewer_uid.as_deref(), - display_name, - source_filter, - )] - } - UsageVisibilityGranularity::TeamAggregate => { - // Force SourceFilter::All — TeamAggregate has no toggle. - let (viewer_uid, display_name) = viewer_identity(app); - let mut rows = vec![MemberUsageRow::for_viewer( - entries, - viewer_uid.as_deref(), - display_name, - SourceFilter::All, - )]; - rows.push(MemberUsageRow::for_other_members(entries)); - rows - } - UsageVisibilityGranularity::PerUserTotals | UsageVisibilityGranularity::FullBreakdown => { - MemberUsageRow::for_each_member(entries, &workspace.members, source_filter) - } - }; - - if matches!( - visibility.granularity, - UsageVisibilityGranularity::PerUserTotals | UsageVisibilityGranularity::FullBreakdown - ) { - let top = rows - .iter() - .map(|r| r.total_credits) - .max() - .unwrap_or(0) - .max(1); - for row in &mut rows { - row.bar_max_credits = top; - } - } - - rows -} - -/// True if any entry is cloud-sourced; gates the source filter toggle. -pub fn has_cloud_usage(entries: &[BillingCycleUsageEntry]) -> bool { - entries - .iter() - .any(|e| e.usage_source == AiCreditsUsageSource::Cloud) -} - -fn render_stacked_bar( - segments: &[BarSegment], - total_credits: i64, - team_max_credits: i64, - appearance: &Appearance, -) -> Box { - let theme = appearance.theme(); - let track_bg = theme.surface_overlay_1(); - let corner = Radius::Pixels(BAR_CORNER_RADIUS); - - if team_max_credits == 0 || total_credits == 0 || segments.is_empty() { - // Empty track, top-rounded on both ends. - return ConstrainedBox::new( - Container::new(Empty::new().finish()) - .with_background(track_bg) - .with_corner_radius(CornerRadius::with_top(corner)) - .finish(), - ) - .with_height(BAR_HEIGHT) - .finish(); - } - - let fill_ratio = (total_credits as f32 / team_max_credits as f32).clamp(MIN_FILL_RATIO, 1.0); - let unfill_ratio = 1.0 - fill_ratio; - let has_unfill = unfill_ratio > 0.0; - let last_segment_idx = segments.len() - 1; - - // One Expanded per segment, weighted by share of total_credits. First/last - // segment get rounded top corners (last only if no muted tail). - let mut filled = Flex::row(); - for (idx, seg) in segments.iter().enumerate() { - let weight = seg.credits as f32 / total_credits as f32; - if weight <= 0.0 { - continue; - } - let is_first = idx == 0; - let is_last_visible = idx == last_segment_idx && !has_unfill; - let segment_corner = match (is_first, is_last_visible) { - (true, true) => CornerRadius::with_top(corner), - (true, false) => CornerRadius::with_top_left(corner), - (false, true) => CornerRadius::with_top_right(corner), - (false, false) => CornerRadius::default(), - }; - filled.add_child( - Expanded::new( - weight, - Container::new(Empty::new().finish()) - .with_background_color(cost_type_color(&seg.cost_type)) - .with_corner_radius(segment_corner) - .finish(), - ) - .finish(), - ); - } - - let mut bar = Flex::row(); - bar.add_child(Expanded::new(fill_ratio, filled.finish()).finish()); - if has_unfill { - bar.add_child( - Expanded::new( - unfill_ratio, - Container::new(Empty::new().finish()) - .with_background(track_bg) - .with_corner_radius(CornerRadius::with_top_right(corner)) - .finish(), - ) - .finish(), - ); - } - - ConstrainedBox::new(bar.finish()) - .with_height(BAR_HEIGHT) - .finish() -} - -/// Per-cost-type tooltip breakdown with a "Total usage" footer. -fn render_usage_tooltip_content(row: &MemberUsageRow, appearance: &Appearance) -> Box { - render_breakdown_tooltip( - &row.segments, - row.total_credits, - row.total_cost_cents, - appearance, - ) -} - -/// Small text-only tooltip surfaced on hover of the service-account info -/// icon. Mirrors the visual treatment of `render_aggregate_legend_tooltip`. -fn render_service_account_info_tooltip(appearance: &Appearance) -> Box { - let theme = appearance.theme(); - let text = Text::new_inline( - "This is an automated agent on your team.".to_string(), - appearance.ui_font_family(), - 12., - ) - .with_color(theme.sub_text_color(theme.background()).into()) - .finish(); - Container::new(text) - .with_background_color(theme.background().into_solid()) - .with_corner_radius(CornerRadius::with_all(Radius::Pixels(6.))) - .with_border(Border::all(1.).with_border_color(theme.outline().into_solid())) - .with_horizontal_padding(12.) - .with_vertical_padding(6.) - .with_drop_shadow( - DropShadow::new_with_standard_offset_and_spread(ColorU::new(0, 0, 0, 48)) - .with_offset(vec2f(0., 4.)), - ) - .finish() -} - -/// Renders one row card (stacked bar + name/totals). -fn render_row_card( - row: &MemberUsageRow, - team_max_credits: i64, - mouse_states: &BillingUsageMouseStates, - appearance: &Appearance, -) -> Box { - let theme = appearance.theme(); - let card_bg = theme.background().into_solid(); - let main = blended_colors::text_main(theme, card_bg); - - let bar = render_stacked_bar( - &row.segments, - row.total_credits, - team_max_credits, - appearance, - ); - - let is_service_account = matches!( - row.subject_type, - AiCreditsUsageAndCostSubjectType::ServiceAccount - ); - // Service accounts with a known UID deep-link to their Oz agent page, - // mirroring the web admin panel's `getOzAgentHref` behavior. - let agent_href = if is_service_account { - row.subject_uid.as_deref().map(|uid| { - format!( - "{}/agents/{}", - ChannelState::oz_root_url(), - urlencoding::encode(uid) - ) - }) - } else { - None - }; - - let display_name_element: Box = if let Some(href) = agent_href { - let link_state = - mouse_states.tooltip_mouse_state(&format!("{}__agent_link", row.subject_key)); - appearance - .ui_builder() - .link(row.display_name.clone(), Some(href), None, link_state) - .build() - .finish() - } else { - Text::new_inline( - row.display_name.clone(), - appearance.ui_font_family(), - appearance.ui_font_size(), - ) - .with_color(main) - .finish() - }; - - let mut name_row = Flex::row() - .with_cross_axis_alignment(CrossAxisAlignment::Center) - .with_child(display_name_element); - - if is_service_account { - let info_state = - mouse_states.tooltip_mouse_state(&format!("{}__agent_info", row.subject_key)); - let info_icon = Hoverable::new(info_state, move |state| { - let info_color = appearance - .theme() - .sub_text_color(appearance.theme().background()); - let icon = ConstrainedBox::new(Icon::Info.to_warpui_icon(info_color).finish()) - .with_width(ROW_ICON_SIZE) - .with_height(ROW_ICON_SIZE) - .finish(); - let mut stack = Stack::new(); - stack.add_child(icon); - if state.is_hovered() { - stack.add_positioned_overlay_child( - render_service_account_info_tooltip(appearance), - OffsetPositioning::offset_from_parent( - vec2f(0., -TOOLTIP_GAP), - ParentOffsetBounds::WindowByPosition, - ParentAnchor::TopMiddle, - ChildAnchor::BottomMiddle, - ), - ); - } - stack.finish() - }) - .finish(); - name_row.add_child(Container::new(info_icon).with_margin_left(6.).finish()); - } - - let credits_text = Text::new_inline( - format_credits(row.total_credits), - appearance.ui_font_family(), - appearance.ui_font_size(), - ) - .with_color(main) - .finish(); - let cost_text = Text::new_inline( - format_cost_cents(row.total_cost_cents), - appearance.ui_font_family(), - appearance.ui_font_size(), - ) - .with_color(main) - .finish(); - let icon_color = theme.sub_text_color(theme.background()); - let coin_icon = ConstrainedBox::new(Icon::Credits.to_warpui_icon(icon_color).finish()) - .with_width(ROW_ICON_SIZE) - .with_height(ROW_ICON_SIZE) - .finish(); - let card_icon = ConstrainedBox::new(Icon::CreditCard.to_warpui_icon(icon_color).finish()) - .with_width(ROW_ICON_SIZE) - .with_height(ROW_ICON_SIZE) - .finish(); - let credits_cluster = Flex::row() - .with_cross_axis_alignment(CrossAxisAlignment::Center) - .with_child(coin_icon) - .with_child(Container::new(credits_text).with_margin_left(4.).finish()) - .finish(); - let cost_cluster = Flex::row() - .with_cross_axis_alignment(CrossAxisAlignment::Center) - .with_child(card_icon) - .with_child(Container::new(cost_text).with_margin_left(4.).finish()) - .finish(); - - let credits_and_cost = Flex::row() - .with_cross_axis_alignment(CrossAxisAlignment::Center) - .with_child(credits_cluster) - .with_child(Container::new(cost_cluster).with_margin_left(6.).finish()) - .finish(); - - let body = Container::new( - Flex::row() - .with_cross_axis_alignment(CrossAxisAlignment::Center) - .with_main_axis_alignment(MainAxisAlignment::SpaceBetween) - .with_main_axis_size(MainAxisSize::Max) - .with_child(Shrinkable::new(1., name_row.finish()).finish()) - .with_child( - Container::new(credits_and_cost) - .with_margin_left(16.) - .finish(), - ) - .finish(), - ) - .with_uniform_padding(ROW_PADDING) - .finish(); - - Container::new( - Flex::column() - .with_cross_axis_alignment(CrossAxisAlignment::Stretch) - .with_child(bar) - .with_child(body) - .finish(), - ) - .with_background_color(card_bg) - .with_border(Border::all(ROW_BORDER_WIDTH).with_border_color(theme.outline().into_solid())) - .with_corner_radius(CornerRadius::with_all(Radius::Pixels(ROW_BORDER_RADIUS))) - .finish() -} - -/// Row card wrapped in a Hoverable that opens the breakdown tooltip. -fn render_member_row( - row: &MemberUsageRow, - team_max_credits: i64, - tooltip_mouse_state: MouseStateHandle, - mouse_states: &BillingUsageMouseStates, - appearance: &Appearance, -) -> Box { - // No segments => no tooltip needed. - if row.segments.is_empty() { - return render_row_card(row, team_max_credits, mouse_states, appearance); - } - - // The info icon sits inside the row card, so hovering it would otherwise - // trigger both this row's breakdown tooltip and the icon's own tooltip - // on top of each other. Pull the icon's hover state up so we can - // suppress the breakdown tooltip while the icon is hovered. - let info_state = matches!( - row.subject_type, - AiCreditsUsageAndCostSubjectType::ServiceAccount - ) - .then(|| mouse_states.tooltip_mouse_state(&format!("{}__agent_info", row.subject_key))); - - Hoverable::new(tooltip_mouse_state, move |state| { - let mut stack = Stack::new(); - stack.add_child(render_row_card( - row, - team_max_credits, - mouse_states, - appearance, - )); - - let info_hovered = info_state - .as_ref() - .is_some_and(|s| s.lock().is_ok_and(|guard| guard.is_hovered())); - - if state.is_hovered() && !info_hovered { - stack.add_positioned_overlay_child( - render_usage_tooltip_content(row, appearance), - OffsetPositioning::offset_from_parent( - vec2f(0., -TOOLTIP_GAP), - ParentOffsetBounds::WindowByPosition, - ParentAnchor::TopMiddle, - ChildAnchor::BottomMiddle, - ), - ); - } - - stack.finish() - }) - .finish() -} - -pub type FilterChangeFn = std::sync::Arc; - -/// All / Local / Cloud pill toggle. -fn render_source_filter_toggle( - current: SourceFilter, - mouse_states: &BillingUsageMouseStates, - appearance: &Appearance, - on_change: FilterChangeFn, -) -> Box { - let theme = appearance.theme(); - let bg = theme.surface_1(); - let main = blended_colors::text_main(theme, bg); - let sub = blended_colors::text_sub(theme, bg); - - let options: [(SourceFilter, MouseStateHandle); 3] = [ - (SourceFilter::All, mouse_states.filter_all.clone()), - (SourceFilter::Local, mouse_states.filter_local.clone()), - (SourceFilter::Cloud, mouse_states.filter_cloud.clone()), - ]; - - let mut row = Flex::row() - .with_cross_axis_alignment(CrossAxisAlignment::Center) - .with_main_axis_size(MainAxisSize::Min); - - for (filter, mouse_state) in options { - let label = filter.label(); - let is_selected = filter == current; - let fg = if is_selected { main } else { sub }; - let font_family = appearance.ui_font_family(); - let on_change = on_change.clone(); - - let cell = Hoverable::new(mouse_state, move |_state| { - let mut cell = Container::new( - Text::new_inline(label, font_family, 11.) - .with_color(fg) - .finish(), - ) - .with_horizontal_padding(10.) - .with_vertical_padding(4.); - if is_selected { - cell = cell.with_background(theme.surface_overlay_1()); - } - cell.finish() - }) - .with_cursor(Cursor::PointingHand) - .on_click(move |ctx, _, _| { - on_change(filter, ctx); - }) - .finish(); - - row.add_child(cell); - } - - Container::new(row.finish()) - .with_border(Border::all(1.).with_border_color(theme.surface_3().into_solid())) - .with_corner_radius(CornerRadius::with_all(Radius::Pixels(6.))) - .finish() -} - -pub fn render_own_usage_with_workspace_row( - entries: &[BillingCycleUsageEntry], - mouse_states: &BillingUsageMouseStates, - appearance: &Appearance, - app: &AppContext, -) -> Box { - let (viewer_uid, display_name) = viewer_identity(app); - let row = MemberUsageRow::for_viewer( - entries, - viewer_uid.as_deref(), - display_name, - SourceFilter::All, - ); - render_member_row_list(std::slice::from_ref(&row), mouse_states, appearance) -} - -pub fn render_own_usage_solo_row( - mouse_states: &BillingUsageMouseStates, - appearance: &Appearance, - app: &AppContext, -) -> Box { - let (viewer_uid, display_name) = viewer_identity(app); - let model = AIRequestUsageModel::as_ref(app); - let row = MemberUsageRow::for_viewer_from_total( - viewer_uid, - display_name, - model.requests_used() as i64, - ); - render_member_row_list(std::slice::from_ref(&row), mouse_states, appearance) -} - -#[allow(clippy::too_many_arguments)] -pub fn render_rows( - workspace: &Workspace, - entries: &[BillingCycleUsageEntry], - visibility: &UsageVisibility, - source_filter: SourceFilter, - mouse_states: &BillingUsageMouseStates, - appearance: &Appearance, - app: &AppContext, - on_filter_change: FilterChangeFn, -) -> Box { - let rows = build_rows(workspace, entries, visibility, source_filter, app); - - let mut column = Flex::column() - .with_cross_axis_alignment(CrossAxisAlignment::Stretch) - .with_spacing(8.); - if let Some(header) = render_member_header( - visibility, - entries, - source_filter, - mouse_states, - appearance, - on_filter_change, - ) { - column.add_child(header); - } - column.add_child(render_member_row_list(&rows, mouse_states, appearance)); - column.finish() -} - -fn render_member_header( - visibility: &UsageVisibility, - entries: &[BillingCycleUsageEntry], - source_filter: SourceFilter, - mouse_states: &BillingUsageMouseStates, - appearance: &Appearance, - on_filter_change: FilterChangeFn, -) -> Option> { - let show_toggle = visibility.granularity == UsageVisibilityGranularity::FullBreakdown - && has_cloud_usage(entries); - - let subheader = render_section_subheader("Members", appearance); - let header = if show_toggle { - Flex::row() - .with_cross_axis_alignment(CrossAxisAlignment::Center) - .with_main_axis_alignment(MainAxisAlignment::SpaceBetween) - .with_main_axis_size(MainAxisSize::Max) - .with_child(subheader) - .with_child(render_source_filter_toggle( - source_filter, - mouse_states, - appearance, - on_filter_change, - )) - .finish() - } else { - subheader - }; - - Some(Container::new(header).with_margin_bottom(8.).finish()) -} - -fn render_member_row_list( - rows: &[MemberUsageRow], - mouse_states: &BillingUsageMouseStates, - appearance: &Appearance, -) -> Box { - let mut column = Flex::column() - .with_cross_axis_alignment(CrossAxisAlignment::Stretch) - .with_spacing(8.); - for row in rows { - let tooltip_state = mouse_states.tooltip_mouse_state(&row.subject_key); - column.add_child(render_member_row( - row, - row.bar_max_credits, - tooltip_state, - mouse_states, - appearance, - )); - } - column.finish() -} - -#[cfg(test)] -#[path = "billing_cycle_usage_rows_tests.rs"] -mod tests; diff --git a/app/src/settings_view/billing_and_usage/billing_cycle_usage_rows_tests.rs b/app/src/settings_view/billing_and_usage/billing_cycle_usage_rows_tests.rs deleted file mode 100644 index 16d2052d..00000000 --- a/app/src/settings_view/billing_and_usage/billing_cycle_usage_rows_tests.rs +++ /dev/null @@ -1,139 +0,0 @@ -use super::{MemberUsageRow, SourceFilter}; -use crate::workspaces::workspace::{ - AiCreditsUsageAndCostSubjectType, AiCreditsUsageAndCostType, AiCreditsUsageBucket, - AiCreditsUsageSource, BillingCycleUsageEntry, -}; - -const VIEWER_UID: &str = "viewer-uid"; -const OTHER_UID: &str = "other-uid"; - -fn entry( - subject_type: AiCreditsUsageAndCostSubjectType, - subject_uid: Option<&str>, - usage_source: AiCreditsUsageSource, - credits_used: i32, - cost_cents: i32, -) -> BillingCycleUsageEntry { - BillingCycleUsageEntry { - subject_type, - subject_uid: subject_uid.map(|s| s.to_string()), - subject_display_name: None, - cost_type: AiCreditsUsageAndCostType::BaseLimit, - usage_bucket: AiCreditsUsageBucket::Ai, - usage_source, - credits_used, - cost_cents, - } -} - -#[test] -fn build_own_usage_row_drops_team_subject_entries() { - // Team-aggregate rows belong to "everyone else" by construction; they - // must never contribute to the viewer's own row totals. - let entries = vec![ - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageSource::Local, - 10, - 5, - ), - entry( - AiCreditsUsageAndCostSubjectType::Team, - None, - AiCreditsUsageSource::Aggregate, - 999, - 999, - ), - ]; - let row = MemberUsageRow::for_viewer( - &entries, - Some(VIEWER_UID), - "viewer".to_string(), - SourceFilter::All, - ); - assert_eq!(row.total_credits, 10); - assert_eq!(row.total_cost_cents, 5); -} - -#[test] -fn build_own_usage_row_drops_other_users_entries() { - let entries = vec![ - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageSource::Local, - 10, - 0, - ), - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(OTHER_UID), - AiCreditsUsageSource::Local, - 999, - 999, - ), - ]; - let row = MemberUsageRow::for_viewer( - &entries, - Some(VIEWER_UID), - "viewer".to_string(), - SourceFilter::All, - ); - assert_eq!(row.total_credits, 10); - assert_eq!(row.total_cost_cents, 0); -} - -#[test] -fn build_own_usage_row_local_filter_drops_cloud_entries() { - let entries = vec![ - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageSource::Local, - 10, - 0, - ), - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageSource::Cloud, - 20, - 0, - ), - ]; - let row = MemberUsageRow::for_viewer( - &entries, - Some(VIEWER_UID), - "viewer".to_string(), - SourceFilter::Local, - ); - assert_eq!(row.total_credits, 10); -} - -#[test] -fn build_own_usage_row_cloud_filter_drops_local_entries() { - let entries = vec![ - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageSource::Local, - 10, - 0, - ), - entry( - AiCreditsUsageAndCostSubjectType::User, - Some(VIEWER_UID), - AiCreditsUsageSource::Cloud, - 20, - 0, - ), - ]; - let row = MemberUsageRow::for_viewer( - &entries, - Some(VIEWER_UID), - "viewer".to_string(), - SourceFilter::Cloud, - ); - assert_eq!(row.total_credits, 20); -} diff --git a/app/src/settings_view/billing_and_usage/billing_cycle_usage_section.rs b/app/src/settings_view/billing_and_usage/billing_cycle_usage_section.rs deleted file mode 100644 index 03d12e94..00000000 --- a/app/src/settings_view/billing_and_usage/billing_cycle_usage_section.rs +++ /dev/null @@ -1,801 +0,0 @@ -use chrono::{DateTime, Datelike, Local, Utc}; -use galaxy_core::ui::appearance::Appearance; -use markdown_parser::{FormattedText, FormattedTextFragment, FormattedTextLine}; -use pathfinder_color::ColorU; -use pathfinder_geometry::vector::vec2f; -use warpui::elements::{ - Border, ChildAnchor, ChildView, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, - DropShadow, Empty, Flex, FormattedTextElement, HighlightedHyperlink, Hoverable, HyperlinkLens, - MainAxisAlignment, MainAxisSize, MouseStateHandle, OffsetPositioning, ParentAnchor, - ParentElement, ParentOffsetBounds, Radius, Stack, Text, -}; -use warpui::fonts::{Properties, Weight}; -use warpui::platform::Cursor; -use warpui::{ - AppContext, Element, Entity, SingletonEntity, TypedActionView, View, ViewContext, ViewHandle, -}; - -use crate::ai::AIRequestUsageModel; -use crate::auth::{AuthManager, AuthStateProvider}; -use crate::menu::{self, Menu, MenuItem, MenuItemFields}; -use crate::settings_view::admin_actions::AdminActions; -use crate::settings_view::billing_and_usage::billing_cycle_usage_common::{ - filter_legacy_buckets, has_non_viewer_data, legend_cost_types, BillingUsageMouseStates, -}; -use crate::settings_view::billing_and_usage::billing_cycle_usage_rows::{ - has_cloud_usage, render_own_usage_solo_row, render_own_usage_with_workspace_row, render_rows, - SourceFilter, -}; -use crate::settings_view::billing_and_usage::billing_cycle_usage_team_totals::render_team_totals_block; -use crate::settings_view::billing_and_usage_page_v2::{ - AGGREGATE_CREDITS_DOT_COLOR, AMBIENT_CREDITS_DOT_COLOR, BASE_CREDITS_DOT_COLOR, - BONUS_CREDITS_DOT_COLOR, PAYG_CREDITS_DOT_COLOR, -}; -use crate::ui_components::icons::Icon; -use crate::workspaces::update_manager::TeamUpdateManager; -use crate::workspaces::user_workspaces::UserWorkspaces; -use crate::workspaces::workspace::{ - AiCreditsUsageAndCostType, BillingCycleUsageSummary, MaxPriorCycles, UsageVisibility, - UsageVisibilityGranularity, Workspace, -}; - -const HEADER_FONT_SIZE: f32 = 16.; -const LEGEND_DOT_SIZE: f32 = 8.; - -pub struct BillingCycleUsageSectionView { - selected_period_end: Option>, - period_selector_mouse_state: MouseStateHandle, - aggregate_legend_mouse_state: MouseStateHandle, - period_menu: ViewHandle>, - period_menu_open: bool, - source_filter: SourceFilter, - row_mouse_states: BillingUsageMouseStates, -} - -#[derive(Clone, Debug)] -pub enum BillingCycleUsageAction { - SelectPeriod(Option>), - TogglePeriodMenu, - ChangeSourceFilter(SourceFilter), - OpenUpgrade, - OpenAdminPanel, -} - -impl Entity for BillingCycleUsageSectionView { - type Event = (); -} - -impl BillingCycleUsageSectionView { - pub fn new(ctx: &mut ViewContext) -> Self { - ctx.subscribe_to_model(&UserWorkspaces::handle(ctx), |me, _, _, ctx| { - me.reconcile_selected_period(ctx); - // If the period menu is open while the workspace or usage data - // changes, the menu's items become stale and clicking one could - // select a period_end that no longer exists in the new data - // (which `current_summary` would then fail to resolve). Rebuild - // the items in-place so the menu always reflects the live data. - if me.period_menu_open { - me.refresh_period_menu_items(ctx); - } - ctx.notify(); - }); - ctx.subscribe_to_model(&AIRequestUsageModel::handle(ctx), |_, _, _, ctx| { - ctx.notify() - }); - ctx.subscribe_to_model(&AuthManager::handle(ctx), |_, _, _, ctx| ctx.notify()); - ctx.subscribe_to_model(&TeamUpdateManager::handle(ctx), |_, _, _, ctx| ctx.notify()); - - // `prevent_interaction_with_other_elements` so a click on the - // trigger button while the menu is open is consumed by the menu's - // outside-click dismiss handler — without it, the trigger also - // received the click and immediately re-toggled the menu open. - let period_menu = ctx.add_typed_action_view(|_| { - Menu::new() - .with_drop_shadow() - .prevent_interaction_with_other_elements() - }); - ctx.subscribe_to_view(&period_menu, |me, _, event, ctx| { - if let menu::Event::Close { .. } = event { - me.period_menu_open = false; - ctx.notify(); - } - }); - - Self { - selected_period_end: None, - period_selector_mouse_state: MouseStateHandle::default(), - aggregate_legend_mouse_state: MouseStateHandle::default(), - period_menu, - period_menu_open: false, - source_filter: SourceFilter::default(), - row_mouse_states: BillingUsageMouseStates::default(), - } - } - - fn resolved_viewer_email(app: &AppContext) -> Option { - AuthStateProvider::as_ref(app).get().user_email() - } - - fn viewer_is_admin(app: &AppContext) -> bool { - let Some(team) = UserWorkspaces::as_ref(app).current_team() else { - return false; - }; - Self::resolved_viewer_email(app) - .as_deref() - .is_some_and(|email| team.has_admin_permissions(email)) - } - - fn current_summary<'a>( - &self, - workspace: &'a Workspace, - ) -> Option<&'a BillingCycleUsageSummary> { - let summaries = &workspace.billing_cycle_usage.as_ref()?.summaries; - match self.selected_period_end { - Some(end) => summaries.iter().find(|s| s.period_end == end), - None => summaries.first(), - } - } - - fn reconcile_selected_period(&mut self, ctx: &AppContext) { - let Some(selected) = self.selected_period_end else { - return; - }; - let still_present = UserWorkspaces::as_ref(ctx) - .current_workspace() - .and_then(|ws| ws.billing_cycle_usage.as_ref()) - .map(|data| data.summaries.iter().any(|s| s.period_end == selected)) - .unwrap_or(false); - if !still_present { - self.selected_period_end = None; - } - } - - /// Whether the "Team" block + "Members" subheader should render. We - /// hide them when the viewer has no team data to show: `members.len() - /// > 1` covers the common multi-member case; `has_non_viewer_data` - /// catches the edge case where the roster shrank to one after a - /// teammate left mid-cycle but their usage is still attributed against - /// this cycle. Together they keep solo teams from showing orphan - /// scaffolding without dropping legitimate team data on departure. - /// - /// Note: per the backend invariant `VIS != OwnOnly => viewer is admin`, - /// so we don't need a separate admin gate here. - fn shows_team_section(&self, workspace: &Workspace, app: &AppContext) -> bool { - let visibility = workspace.resolve_usage_visibility(Self::viewer_is_admin(app)); - if visibility.granularity == UsageVisibilityGranularity::OwnOnly { - return false; - } - let entries = filter_legacy_buckets( - self.current_summary(workspace) - .map(|s| s.entries.as_slice()) - .unwrap_or_default(), - ); - let viewer_uid = AuthStateProvider::as_ref(app) - .get() - .user_id() - .map(|uid| uid.as_string()); - workspace.members.len() > 1 || has_non_viewer_data(&entries, viewer_uid.as_deref()) - } -} - -impl TypedActionView for BillingCycleUsageSectionView { - type Action = BillingCycleUsageAction; - - fn handle_action(&mut self, action: &Self::Action, ctx: &mut ViewContext) { - match action { - BillingCycleUsageAction::SelectPeriod(period_end) => { - self.selected_period_end = *period_end; - self.period_menu_open = false; - ctx.notify(); - } - BillingCycleUsageAction::TogglePeriodMenu => { - self.period_menu_open = !self.period_menu_open; - if self.period_menu_open { - self.refresh_period_menu_items(ctx); - } - ctx.notify(); - } - BillingCycleUsageAction::ChangeSourceFilter(filter) => { - self.source_filter = *filter; - ctx.notify(); - } - BillingCycleUsageAction::OpenUpgrade => { - if let Some(team_uid) = UserWorkspaces::as_ref(ctx).current_team_uid() { - ctx.open_url(&UserWorkspaces::upgrade_link_for_team(team_uid)); - } - } - BillingCycleUsageAction::OpenAdminPanel => { - if let Some(team_uid) = UserWorkspaces::as_ref(ctx).current_team_uid() { - AdminActions::open_admin_panel(team_uid, ctx); - } - } - } - } -} - -impl BillingCycleUsageSectionView { - fn refresh_period_menu_items(&self, ctx: &mut ViewContext) { - let Some(workspace) = UserWorkspaces::as_ref(ctx).current_workspace().cloned() else { - return; - }; - let Some(data) = workspace.billing_cycle_usage.as_ref() else { - return; - }; - let items: Vec> = data - .summaries - .iter() - .map(|summary| { - let label = format_period_range(summary.period_start, summary.period_end); - MenuItem::Item(MenuItemFields::new(label).with_on_select_action( - BillingCycleUsageAction::SelectPeriod(Some(summary.period_end)), - )) - }) - .collect(); - - self.period_menu - .update(ctx, |menu: &mut Menu, ctx| { - menu.set_items(items, ctx); - }); - } -} - -impl View for BillingCycleUsageSectionView { - fn ui_name() -> &'static str { - "BillingCycleUsageSection" - } - - fn render(&self, app: &AppContext) -> Box { - let appearance = Appearance::as_ref(app); - let workspace = UserWorkspaces::as_ref(app).current_workspace().cloned(); - match workspace.as_ref() { - Some(w) if self.shows_team_section(w, app) => { - self.render_team_usage(w, appearance, app) - } - Some(w) => self.render_own_usage_with_workspace(w, appearance, app), - None => self.render_own_usage_solo(appearance, app), - } - } -} - -impl BillingCycleUsageSectionView { - fn render_team_usage( - &self, - workspace: &Workspace, - appearance: &Appearance, - app: &AppContext, - ) -> Box { - let is_admin = Self::viewer_is_admin(app); - let visibility = workspace.resolve_usage_visibility(is_admin); - - let mut column = Flex::column().with_cross_axis_alignment(CrossAxisAlignment::Stretch); - column.add_child(self.render_header(Some(workspace), &visibility, appearance, app)); - - let entries = filter_legacy_buckets( - self.current_summary(workspace) - .map(|summary| summary.entries.as_slice()) - .unwrap_or_default(), - ); - - let is_source_filter_shown = visibility.granularity - == UsageVisibilityGranularity::FullBreakdown - && has_cloud_usage(&entries); - let source_filter = if is_source_filter_shown { - self.source_filter - } else { - SourceFilter::All - }; - - column.add_child( - Container::new(render_team_totals_block( - &entries, - &visibility, - &self.row_mouse_states, - appearance, - )) - .with_margin_top(16.) - .finish(), - ); - - if is_admin { - if let Some(banner) = self.render_visibility_cta_banner(workspace, appearance) { - column.add_child(Container::new(banner).with_margin_top(16.).finish()); - } - } - - column.add_child( - Container::new(render_rows( - workspace, - &entries, - &visibility, - source_filter, - &self.row_mouse_states, - appearance, - app, - std::sync::Arc::new(|filter, ctx| { - ctx.dispatch_typed_action(BillingCycleUsageAction::ChangeSourceFilter(filter)); - }), - )) - .with_margin_top(16.) - .finish(), - ); - - column.finish() - } - - fn render_own_usage_with_workspace( - &self, - workspace: &Workspace, - appearance: &Appearance, - app: &AppContext, - ) -> Box { - let visibility = workspace.resolve_usage_visibility(Self::viewer_is_admin(app)); - let entries = filter_legacy_buckets( - self.current_summary(workspace) - .map(|s| s.entries.as_slice()) - .unwrap_or_default(), - ); - - let mut column = Flex::column().with_cross_axis_alignment(CrossAxisAlignment::Stretch); - column.add_child(self.render_header(Some(workspace), &visibility, appearance, app)); - column.add_child( - Container::new(render_own_usage_with_workspace_row( - &entries, - &self.row_mouse_states, - appearance, - app, - )) - .with_margin_top(16.) - .finish(), - ); - column.finish() - } - - // Here when you're not on a team, there's no workspace to pull billing_cycle_usage data from. - // So we "fake" a row and source data from the AIRequestUsageModel instead - fn render_own_usage_solo(&self, appearance: &Appearance, app: &AppContext) -> Box { - let mut column = Flex::column().with_cross_axis_alignment(CrossAxisAlignment::Stretch); - column.add_child(self.render_header(None, &UsageVisibility::default(), appearance, app)); - column.add_child( - Container::new(render_own_usage_solo_row( - &self.row_mouse_states, - appearance, - app, - )) - .with_margin_top(16.) - .finish(), - ); - column.finish() - } -} - -impl BillingCycleUsageSectionView { - fn render_header( - &self, - workspace: Option<&Workspace>, - visibility: &UsageVisibility, - appearance: &Appearance, - app: &AppContext, - ) -> Box { - let theme = appearance.theme(); - let mut row = Flex::row() - .with_main_axis_alignment(MainAxisAlignment::SpaceBetween) - .with_cross_axis_alignment(CrossAxisAlignment::Center) - .with_main_axis_size(MainAxisSize::Max); - - row.add_child( - Text::new_inline("Usage", appearance.ui_font_family(), HEADER_FONT_SIZE) - .with_style(Properties::default().weight(Weight::Bold)) - .with_color(theme.active_ui_text_color().into()) - .finish(), - ); - - let mut right_side = Flex::row() - .with_cross_axis_alignment(CrossAxisAlignment::Center) - .with_main_axis_alignment(MainAxisAlignment::End); - - // Collapse to a static label when there's effectively one period to - // pick from: either the tier policy doesn't expose history at all, or - // the server returned a single canonical cycle. - if let Some(workspace) = workspace { - let summary_count = workspace - .billing_cycle_usage - .as_ref() - .map(|d| d.summaries.len()) - .unwrap_or(0); - let use_selector = - visibility.max_prior_cycles != MaxPriorCycles::None && summary_count > 1; - let period_element = if use_selector { - self.render_period_selector(workspace, appearance) - } else { - self.render_period_range_static(workspace, appearance) - }; - right_side.add_child(period_element); - } - - row.add_child(right_side.finish()); - - let mut column = Flex::column().with_cross_axis_alignment(CrossAxisAlignment::Stretch); - column.add_child(row.finish()); - - let resets_text = self.render_resets_label(appearance, app); - let legend = workspace.and_then(|workspace| self.render_legend(workspace, appearance)); - if resets_text.is_some() || legend.is_some() { - let mut secondary_row = Flex::row() - .with_cross_axis_alignment(CrossAxisAlignment::Center) - .with_main_axis_alignment(MainAxisAlignment::SpaceBetween) - .with_main_axis_size(MainAxisSize::Max); - secondary_row.add_child(resets_text.unwrap_or_else(|| Empty::new().finish())); - secondary_row.add_child(legend.unwrap_or_else(|| Empty::new().finish())); - column.add_child( - Container::new(secondary_row.finish()) - .with_margin_top(4.) - .finish(), - ); - } - - Container::new(column.finish()).finish() - } - - /// "Resets May 27, 11:24 PM EDT" - fn render_resets_label( - &self, - appearance: &Appearance, - app: &AppContext, - ) -> Option> { - if self.selected_period_end.is_some() { - return None; - } - let theme = appearance.theme(); - let reset_str = AIRequestUsageModel::as_ref(app) - .next_refresh_time_local() - .format("Resets %b %d, %-I:%M %p") - .to_string(); - Some( - Text::new_inline( - reset_str, - appearance.ui_font_family(), - appearance.ui_font_size(), - ) - .with_color(theme.sub_text_color(theme.background()).into()) - .finish(), - ) - } - - // "May 13 - Jun 13, 2026" - fn render_period_range_static( - &self, - workspace: &Workspace, - appearance: &Appearance, - ) -> Box { - let theme = appearance.theme(); - let label = self - .current_summary(workspace) - .map(|s| format_period_range(s.period_start, s.period_end)) - .or_else(|| { - workspace.billing_cycle_usage.as_ref().map(|data| { - format_period_range(data.current_period_start, data.current_period_end) - }) - }) - .unwrap_or_default(); - Text::new_inline( - label, - appearance.ui_font_family(), - appearance.ui_font_size(), - ) - .with_color(theme.sub_text_color(theme.background()).into()) - .finish() - } - - fn render_period_selector( - &self, - workspace: &Workspace, - appearance: &Appearance, - ) -> Box { - let theme = appearance.theme(); - let bg = theme.background(); - let label = self - .current_summary(workspace) - .map(|s| format_period_range(s.period_start, s.period_end)) - .unwrap_or_default(); - - let mouse_state = self.period_selector_mouse_state.clone(); - let font_family = appearance.ui_font_family(); - let font_size = appearance.ui_font_size(); - let main_text = theme.sub_text_color(bg); - - let button = Hoverable::new(mouse_state, move |_| { - let mut inner = Flex::row() - .with_cross_axis_alignment(CrossAxisAlignment::Center) - .with_main_axis_size(MainAxisSize::Min); - inner.add_child( - Text::new_inline(label.clone(), font_family, font_size) - .with_color(main_text.into()) - .finish(), - ); - inner.add_child( - Container::new( - ConstrainedBox::new(Icon::ChevronDown.to_warpui_icon(main_text).finish()) - .with_width(12.) - .with_height(12.) - .finish(), - ) - .with_margin_left(4.) - .finish(), - ); - inner.finish() - }) - .with_cursor(Cursor::PointingHand) - .on_click(|ctx, _, _| { - ctx.dispatch_typed_action(BillingCycleUsageAction::TogglePeriodMenu); - }) - .finish(); - - let mut stack = Stack::new(); - stack.add_child(button); - if self.period_menu_open { - stack.add_positioned_overlay_child( - ChildView::new(&self.period_menu).finish(), - OffsetPositioning::offset_from_parent( - vec2f(0., 4.), - ParentOffsetBounds::WindowByPosition, - ParentAnchor::BottomRight, - ChildAnchor::TopRight, - ), - ); - } - stack.finish() - } - - fn render_legend( - &self, - workspace: &Workspace, - appearance: &Appearance, - ) -> Option> { - let summary = self.current_summary(workspace)?; - // Only list buckets that actually contribute to the stacked bars: drop - // legacy buckets and cost types with no usage, so the legend never - // shows a bucket (e.g. "Base") that has zero credits in the data. - let present_buckets = legend_cost_types(&summary.entries); - if present_buckets.is_empty() { - return None; - } - - let mut row = Flex::row() - .with_cross_axis_alignment(CrossAxisAlignment::Center) - .with_main_axis_size(MainAxisSize::Min); - for (idx, bucket) in present_buckets.iter().enumerate() { - if idx > 0 { - row.add_child( - Container::new(Empty::new().finish()) - .with_margin_right(12.) - .finish(), - ); - } - row.add_child(self.render_legend_entry(bucket.clone(), appearance)); - } - Some(row.finish()) - } - - fn render_legend_entry( - &self, - cost_type: AiCreditsUsageAndCostType, - appearance: &Appearance, - ) -> Box { - let (color, label) = legend_style_for(cost_type.clone()); - let theme = appearance.theme(); - let entry = { - let mut row = Flex::row() - .with_cross_axis_alignment(CrossAxisAlignment::Center) - .with_main_axis_size(MainAxisSize::Min); - row.add_child( - ConstrainedBox::new( - Container::new(Empty::new().finish()) - .with_background_color(color) - .with_corner_radius(CornerRadius::with_all(Radius::Pixels( - LEGEND_DOT_SIZE / 2., - ))) - .finish(), - ) - .with_height(LEGEND_DOT_SIZE) - .with_width(LEGEND_DOT_SIZE) - .finish(), - ); - row.add_child( - Container::new( - Text::new_inline( - label, - appearance.ui_font_family(), - appearance.ui_font_size(), - ) - .with_color(theme.sub_text_color(theme.background()).into()) - .finish(), - ) - .with_margin_left(6.) - .finish(), - ); - row.finish() - }; - - // The Aggregate bucket replaces per-cost-type detail with a single - // "Combined" row, which isn't self-explanatory; surface a small - // hover tooltip clarifying what it includes. - if !matches!(cost_type, AiCreditsUsageAndCostType::Aggregate) { - return entry; - } - - let mouse_state = self.aggregate_legend_mouse_state.clone(); - Hoverable::new(mouse_state, move |state| { - let mut stack = Stack::new(); - stack.add_child(entry); - if state.is_hovered() { - stack.add_positioned_overlay_child( - render_aggregate_legend_tooltip(appearance), - OffsetPositioning::offset_from_parent( - vec2f(0., 6.), - ParentOffsetBounds::WindowByPosition, - ParentAnchor::BottomMiddle, - ChildAnchor::TopMiddle, - ), - ); - } - stack.finish() - }) - .finish() - } - - /// Renders the CTA banner that sits between the team-totals block and - /// the per-member rows. The copy and action vary by visibility tier: - /// non-FullBreakdown admins see an upgrade nudge; FullBreakdown admins - /// see a pointer to the admin panel where per-user spend limits actually - /// get configured. - fn render_visibility_cta_banner( - &self, - workspace: &Workspace, - appearance: &Appearance, - ) -> Option> { - let admin_granularity = workspace - .billing_metadata - .tier - .usage_visibility_policy? - .admin_granularity; - if admin_granularity == UsageVisibilityGranularity::FullBreakdown - && !workspace.billing_metadata.is_enterprise_plan() - { - return None; - } - let (link_text, trailing_copy, action, leading_icon) = - visibility_cta_for(admin_granularity)?; - - // Only show when there are teammates -- a single-member workspace - // doesn't benefit from any of the team-level visibility CTAs. - if workspace.members.len() <= 1 { - return None; - } - - let theme = appearance.theme(); - let sub_text = theme.sub_text_color(theme.background()); - let body = FormattedTextElement::new( - FormattedText::new([FormattedTextLine::Line(vec![ - FormattedTextFragment::hyperlink_action(link_text, action), - FormattedTextFragment::plain_text(format!(" {trailing_copy}")), - ])]), - appearance.ui_font_size(), - appearance.ui_font_family(), - appearance.ui_font_family(), - sub_text.into(), - HighlightedHyperlink::default(), - ) - .with_hyperlink_font_color(theme.accent().into_solid()) - .register_default_click_handlers_with_action_support(|lens, event, ctx| match lens { - HyperlinkLens::Url(u) => ctx.open_url(u), - HyperlinkLens::Action(a) => { - if let Some(act) = a.as_any().downcast_ref::() { - event.dispatch_typed_action(act.clone()); - } - } - }) - .finish(); - - let icon = ConstrainedBox::new(leading_icon.to_warpui_icon(sub_text).finish()) - .with_width(14.) - .with_height(14.) - .finish(); - - let row = Flex::row() - .with_cross_axis_alignment(CrossAxisAlignment::Center) - .with_child(Container::new(icon).with_margin_right(8.).finish()) - .with_child(body) - .finish(); - - Some( - Container::new(row) - .with_background_color(theme.surface_1().into_solid()) - .with_corner_radius(CornerRadius::with_all(Radius::Pixels(8.))) - .with_uniform_padding(12.) - .finish(), - ) - } -} - -/// Returns the (link text, trailing copy, action, icon) tuple for the -/// visibility CTA banner, or `None` to suppress the banner entirely. -fn visibility_cta_for( - granularity: UsageVisibilityGranularity, -) -> Option<(&'static str, &'static str, BillingCycleUsageAction, Icon)> { - match granularity { - UsageVisibilityGranularity::OwnOnly => Some(( - "Upgrade to Build", - "to see team-level credit usage.", - BillingCycleUsageAction::OpenUpgrade, - Icon::ArrowCircleBrokenUp, - )), - UsageVisibilityGranularity::TeamAggregate => Some(( - "Upgrade to Business", - "to see per-user credit attribution.", - BillingCycleUsageAction::OpenUpgrade, - Icon::ArrowCircleBrokenUp, - )), - UsageVisibilityGranularity::PerUserTotals => Some(( - "Upgrade to Enterprise", - "to see fine-grained credit attribution and set per-user spend limits.", - BillingCycleUsageAction::OpenUpgrade, - Icon::ArrowCircleBrokenUp, - )), - // FullBreakdown viewers already have full visibility; nudge them to - // the admin panel where per-user spend limits actually get configured. - UsageVisibilityGranularity::FullBreakdown => Some(( - "Open the admin panel", - "to set per-user spend limits.", - BillingCycleUsageAction::OpenAdminPanel, - Icon::Users, - )), - } -} - -fn legend_style_for(cost_type: AiCreditsUsageAndCostType) -> (ColorU, &'static str) { - match cost_type { - AiCreditsUsageAndCostType::BaseLimit => (BASE_CREDITS_DOT_COLOR, "Base"), - AiCreditsUsageAndCostType::BonusGrant => (BONUS_CREDITS_DOT_COLOR, "Add-ons"), - AiCreditsUsageAndCostType::Payg => (PAYG_CREDITS_DOT_COLOR, "Pay-as-you-go"), - AiCreditsUsageAndCostType::AmbientBonusGrant => (AMBIENT_CREDITS_DOT_COLOR, "Cloud-only"), - AiCreditsUsageAndCostType::Aggregate => (AGGREGATE_CREDITS_DOT_COLOR, "Combined"), - AiCreditsUsageAndCostType::Other(_) => (BASE_CREDITS_DOT_COLOR, ""), - } -} - -fn render_aggregate_legend_tooltip(appearance: &Appearance) -> Box { - let theme = appearance.theme(); - let text = Text::new_inline( - "Other team members' usage across add-on, pay-as-you-go, and cloud-only credits." - .to_string(), - appearance.ui_font_family(), - 12., - ) - .with_color(theme.sub_text_color(theme.background()).into()) - .finish(); - Container::new(text) - .with_background_color(theme.background().into_solid()) - .with_corner_radius(CornerRadius::with_all(Radius::Pixels(6.))) - .with_border(Border::all(1.).with_border_color(theme.outline().into_solid())) - .with_horizontal_padding(12.) - .with_vertical_padding(6.) - .with_drop_shadow( - DropShadow::new_with_standard_offset_and_spread(ColorU::new(0, 0, 0, 48)) - .with_offset(vec2f(0., 4.)), - ) - .finish() -} - -fn format_period_range(start: DateTime, end: DateTime) -> String { - let start = start.with_timezone(&Local); - let end = end.with_timezone(&Local); - if start.year() == end.year() { - format!("{} - {}", start.format("%b %d"), end.format("%b %d, %Y")) - } else { - format!( - "{} - {}", - start.format("%b %d, %Y"), - end.format("%b %d, %Y") - ) - } -} diff --git a/app/src/settings_view/billing_and_usage/billing_cycle_usage_team_totals.rs b/app/src/settings_view/billing_and_usage/billing_cycle_usage_team_totals.rs deleted file mode 100644 index f92962c2..00000000 --- a/app/src/settings_view/billing_and_usage/billing_cycle_usage_team_totals.rs +++ /dev/null @@ -1,371 +0,0 @@ -use galaxy_core::ui::appearance::Appearance; -use pathfinder_geometry::vector::vec2f; -use warpui::elements::{ - Border, ChildAnchor, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Empty, - Expanded, Flex, Hoverable, MainAxisAlignment, MainAxisSize, MouseStateHandle, - OffsetPositioning, ParentAnchor, ParentElement, ParentOffsetBounds, Radius, Shrinkable, Stack, - Text, -}; -use warpui::fonts::{Properties, Weight}; -use warpui::Element; - -use crate::settings_view::billing_and_usage::billing_cycle_usage_common::{ - aggregate_segments, cost_type_color, format_cost_cents, format_credits, - render_breakdown_tooltip, render_section_subheader, BarSegment, BillingUsageMouseStates, - ROW_BORDER_RADIUS, ROW_BORDER_WIDTH, TOOLTIP_GAP, -}; -use crate::ui_components::blended_colors; -use crate::workspaces::workspace::{ - AiCreditsUsageBucket, AiCreditsUsageSource, BillingCycleUsageEntry, UsageVisibility, - UsageVisibilityGranularity, -}; - -fn collapse_segments_to_cost_type(segments: Vec) -> Vec { - let mut out: Vec = Vec::new(); - for seg in segments { - if let Some(existing) = out.iter_mut().find(|s| s.cost_type == seg.cost_type) { - existing.credits += seg.credits; - existing.cost_cents += seg.cost_cents; - } else { - out.push(BarSegment { - cost_type: seg.cost_type, - usage_bucket: AiCreditsUsageBucket::Aggregate, - credits: seg.credits, - cost_cents: seg.cost_cents, - }); - } - } - out -} - -/// Pill-shaped bar at the bottom of each team-totals card. -const CARD_BAR_HEIGHT: f32 = 8.; -const CARD_BAR_RADIUS: f32 = CARD_BAR_HEIGHT / 2.; - -/// Summary backing a single team-totals card (Overall / Local / Cloud). -#[derive(Debug)] -pub struct TeamTotalCardSummary { - pub title: &'static str, - pub card_key: &'static str, - pub segments: Vec, - pub total_credits: i64, - pub total_cost_cents: i64, - pub limit_cents: Option, -} - -pub fn build_team_total_card_summaries( - entries: &[BillingCycleUsageEntry], - visibility: &UsageVisibility, -) -> Vec { - let (overall_segments, overall_credits, overall_cost) = aggregate_segments(entries.iter()); - let mut summaries = vec![TeamTotalCardSummary { - title: "Overall usage", - card_key: "__card_overall__", - segments: overall_segments, - total_credits: overall_credits, - total_cost_cents: overall_cost, - limit_cents: None, - }]; - - let shows_per_source = matches!( - visibility.granularity, - UsageVisibilityGranularity::FullBreakdown - ); - if shows_per_source { - let (local_segments, local_credits, local_cost) = aggregate_segments( - entries - .iter() - .filter(|e| e.usage_source == AiCreditsUsageSource::Local), - ); - let (cloud_segments, cloud_credits, cloud_cost) = aggregate_segments( - entries - .iter() - .filter(|e| e.usage_source == AiCreditsUsageSource::Cloud), - ); - summaries.push(TeamTotalCardSummary { - title: "Local agent usage", - card_key: "__card_local__", - segments: local_segments, - total_credits: local_credits, - total_cost_cents: local_cost, - limit_cents: None, - }); - summaries.push(TeamTotalCardSummary { - title: "Cloud agent usage", - card_key: "__card_cloud__", - segments: cloud_segments, - total_credits: cloud_credits, - total_cost_cents: cloud_cost, - limit_cents: None, - }); - } - - // Visibility tiers below FullBreakdown don't expose per-bucket detail, - // so collapse bucket-dimensioned segments into single per-cost-type lines. - // Otherwise we get a "Base (AI)" row + a separate bare "Base" row in the team aggregate card. - if !matches!( - visibility.granularity, - UsageVisibilityGranularity::FullBreakdown - ) { - for summary in &mut summaries { - summary.segments = - collapse_segments_to_cost_type(std::mem::take(&mut summary.segments)); - } - } - - summaries -} - -fn render_card_pill_bar( - segments: &[BarSegment], - total_credits: i64, - total_cost_cents: i64, - limit_cents: Option, - appearance: &Appearance, -) -> Box { - let theme = appearance.theme(); - let track_bg = theme.surface_overlay_1(); - let corner = Radius::Pixels(CARD_BAR_RADIUS); - - if total_credits == 0 || segments.is_empty() { - return ConstrainedBox::new( - Container::new(Empty::new().finish()) - .with_background(track_bg) - .with_corner_radius(CornerRadius::with_all(corner)) - .finish(), - ) - .with_height(CARD_BAR_HEIGHT) - .finish(); - } - - let fill_ratio = match limit_cents { - Some(limit) if limit > 0 => (total_cost_cents as f32 / limit as f32).clamp(0.0, 1.0), - _ => 1.0, - }; - let unfill_ratio = 1.0 - fill_ratio; - let has_unfill = unfill_ratio > 0.0; - let last_segment_idx = segments.len() - 1; - - let mut filled = Flex::row(); - for (idx, seg) in segments.iter().enumerate() { - let weight = seg.credits as f32 / total_credits as f32; - if weight <= 0.0 { - continue; - } - let is_first = idx == 0; - let is_last_visible = idx == last_segment_idx && !has_unfill; - let segment_corner = match (is_first, is_last_visible) { - (true, true) => CornerRadius::with_all(corner), - (true, false) => CornerRadius::with_left(corner), - (false, true) => CornerRadius::with_right(corner), - (false, false) => CornerRadius::default(), - }; - filled.add_child( - Expanded::new( - weight, - Container::new(Empty::new().finish()) - .with_background_color(cost_type_color(&seg.cost_type)) - .with_corner_radius(segment_corner) - .finish(), - ) - .finish(), - ); - } - - let mut bar = Flex::row(); - bar.add_child(Expanded::new(fill_ratio, filled.finish()).finish()); - if has_unfill { - bar.add_child( - Expanded::new( - unfill_ratio, - Container::new(Empty::new().finish()) - .with_background(track_bg) - .with_corner_radius(CornerRadius::with_right(corner)) - .finish(), - ) - .finish(), - ); - } - - ConstrainedBox::new(bar.finish()) - .with_height(CARD_BAR_HEIGHT) - .finish() -} - -/// Card body for one team-totals slice. Layout (top to bottom): -/// [title] -/// [$X.XX] [Limit: $Y.YY] (limit optional) -/// [(N credits)] -/// [pill stacked bar] -fn build_team_total_card( - summary: &TeamTotalCardSummary, - appearance: &Appearance, -) -> Box { - let theme = appearance.theme(); - let card_bg = theme.background().into_solid(); - let main = blended_colors::text_main(theme, card_bg); - let sub = blended_colors::text_sub(theme, card_bg); - - let title_text = Text::new_inline(summary.title.to_string(), appearance.ui_font_family(), 13.) - .with_color(sub) - .with_style(Properties::default().weight(Weight::Medium)) - .finish(); - - let cost_text = Text::new_inline( - format_cost_cents(summary.total_cost_cents), - appearance.ui_font_family(), - 24., - ) - .with_color(main) - .with_style(Properties::default().weight(Weight::Semibold)) - .finish(); - - let credits_text = Text::new_inline( - format!("({} credits)", format_credits(summary.total_credits)), - appearance.ui_font_family(), - 13., - ) - .with_color(sub) - .finish(); - - let totals_col = Flex::column() - .with_cross_axis_alignment(CrossAxisAlignment::Start) - .with_child(cost_text) - .with_child(Container::new(credits_text).with_margin_top(2.).finish()) - .finish(); - - let totals_row: Box = match summary.limit_cents { - Some(limit) => { - let limit_text = Text::new_inline( - format!("Limit: {}", format_cost_cents(limit)), - appearance.ui_font_family(), - 12., - ) - .with_color(sub) - .finish(); - Flex::row() - .with_cross_axis_alignment(CrossAxisAlignment::Center) - .with_main_axis_alignment(MainAxisAlignment::SpaceBetween) - .with_main_axis_size(MainAxisSize::Max) - .with_child(Shrinkable::new(1., totals_col).finish()) - .with_child(Container::new(limit_text).with_margin_left(16.).finish()) - .finish() - } - None => totals_col, - }; - - let bar = render_card_pill_bar( - &summary.segments, - summary.total_credits, - summary.total_cost_cents, - summary.limit_cents, - appearance, - ); - - let body = Flex::column() - .with_cross_axis_alignment(CrossAxisAlignment::Stretch) - .with_spacing(12.) - .with_child( - Flex::column() - .with_cross_axis_alignment(CrossAxisAlignment::Start) - .with_spacing(6.) - .with_child(title_text) - .with_child(totals_row) - .finish(), - ) - .with_child(bar) - .finish(); - - Container::new(body) - .with_background_color(card_bg) - .with_border(Border::all(ROW_BORDER_WIDTH).with_border_color(theme.outline().into_solid())) - .with_corner_radius(CornerRadius::with_all(Radius::Pixels(ROW_BORDER_RADIUS))) - .with_uniform_padding(16.) - .finish() -} - -fn render_team_total_card( - summary: &TeamTotalCardSummary, - tooltip_mouse_state: MouseStateHandle, - appearance: &Appearance, -) -> Box { - if summary.segments.is_empty() { - return build_team_total_card(summary, appearance); - } - - Hoverable::new(tooltip_mouse_state, move |state| { - let mut stack = Stack::new(); - stack.add_child(build_team_total_card(summary, appearance)); - - if state.is_hovered() { - stack.add_positioned_overlay_child( - render_breakdown_tooltip( - &summary.segments, - summary.total_credits, - summary.total_cost_cents, - appearance, - ), - OffsetPositioning::offset_from_parent( - vec2f(0., -TOOLTIP_GAP), - ParentOffsetBounds::WindowByPosition, - ParentAnchor::TopMiddle, - ChildAnchor::BottomMiddle, - ), - ); - } - - stack.finish() - }) - .finish() -} - -/// Horizontal row of team-totals cards (Overall + Local + Cloud). -fn render_team_totals_section( - entries: &[BillingCycleUsageEntry], - visibility: &UsageVisibility, - mouse_states: &BillingUsageMouseStates, - appearance: &Appearance, -) -> Box { - let summaries = build_team_total_card_summaries(entries, visibility); - let mut row = Flex::row() - .with_cross_axis_alignment(CrossAxisAlignment::Stretch) - .with_main_axis_size(MainAxisSize::Max) - .with_spacing(12.); - for summary in &summaries { - let tooltip_state = mouse_states.tooltip_mouse_state(summary.card_key); - row.add_child( - Expanded::new( - 1., - render_team_total_card(summary, tooltip_state, appearance), - ) - .finish(), - ); - } - row.finish() -} - -/// "Team" subheader + cards -pub fn render_team_totals_block( - entries: &[BillingCycleUsageEntry], - visibility: &UsageVisibility, - mouse_states: &BillingUsageMouseStates, - appearance: &Appearance, -) -> Box { - let mut column = Flex::column().with_cross_axis_alignment(CrossAxisAlignment::Stretch); - column.add_child( - Container::new(render_section_subheader("Team", appearance)) - .with_margin_bottom(8.) - .finish(), - ); - column.add_child(render_team_totals_section( - entries, - visibility, - mouse_states, - appearance, - )); - column.finish() -} - -#[cfg(test)] -#[path = "billing_cycle_usage_team_totals_tests.rs"] -mod tests; diff --git a/app/src/settings_view/billing_and_usage/billing_cycle_usage_team_totals_tests.rs b/app/src/settings_view/billing_and_usage/billing_cycle_usage_team_totals_tests.rs deleted file mode 100644 index b7058a74..00000000 --- a/app/src/settings_view/billing_and_usage/billing_cycle_usage_team_totals_tests.rs +++ /dev/null @@ -1,95 +0,0 @@ -use super::{build_team_total_card_summaries, TeamTotalCardSummary}; -use crate::workspaces::workspace::{ - AiCreditsUsageAndCostSubjectType, AiCreditsUsageAndCostType, AiCreditsUsageBucket, - AiCreditsUsageSource, BillingCycleUsageEntry, UsageVisibility, UsageVisibilityGranularity, -}; - -fn entry( - usage_source: AiCreditsUsageSource, - credits_used: i32, - cost_cents: i32, -) -> BillingCycleUsageEntry { - BillingCycleUsageEntry { - subject_type: AiCreditsUsageAndCostSubjectType::User, - subject_uid: Some("u".to_string()), - subject_display_name: None, - cost_type: AiCreditsUsageAndCostType::BaseLimit, - usage_bucket: AiCreditsUsageBucket::Ai, - usage_source, - credits_used, - cost_cents, - } -} - -fn visibility(granularity: UsageVisibilityGranularity) -> UsageVisibility { - UsageVisibility { - granularity, - max_prior_cycles: Default::default(), - } -} - -fn entries_two_per_source() -> Vec { - vec![ - entry(AiCreditsUsageSource::Local, 30, 10), - entry(AiCreditsUsageSource::Cloud, 70, 25), - ] -} - -fn titles(summaries: &[TeamTotalCardSummary]) -> Vec<&'static str> { - summaries.iter().map(|s| s.title).collect() -} - -#[test] -fn team_aggregate_visibility_yields_overall_card_only() { - // Server collapses teammates' usage into an `Aggregate`-source row under - // TeamAggregate, so the Local/Cloud split can't be honestly attributed - // — only the Overall card is meaningful. - let summaries = build_team_total_card_summaries( - &entries_two_per_source(), - &visibility(UsageVisibilityGranularity::TeamAggregate), - ); - assert_eq!(titles(&summaries), vec!["Overall usage"]); -} - -#[test] -fn own_only_visibility_yields_overall_card_only() { - // OwnOnly viewers don't normally render the team-totals block at all, - // but the builder should still degrade gracefully to a single card. - let summaries = build_team_total_card_summaries( - &entries_two_per_source(), - &visibility(UsageVisibilityGranularity::OwnOnly), - ); - assert_eq!(titles(&summaries), vec!["Overall usage"]); -} - -#[test] -fn per_user_totals_visibility_yields_overall_card_only() { - let summaries = build_team_total_card_summaries( - &entries_two_per_source(), - &visibility(UsageVisibilityGranularity::PerUserTotals), - ); - assert_eq!(titles(&summaries), vec!["Overall usage"]); -} - -#[test] -fn full_breakdown_visibility_returns_three_cards_with_partitioned_sums() { - let summaries = build_team_total_card_summaries( - &entries_two_per_source(), - &visibility(UsageVisibilityGranularity::FullBreakdown), - ); - - assert_eq!( - titles(&summaries), - vec!["Overall usage", "Local agent usage", "Cloud agent usage"] - ); - - // Overall = Local + Cloud; Local card = only Local entries; Cloud card = - // only Cloud entries. Distinct credits/cost per source catch any swapped - // filter. - assert_eq!(summaries[0].total_credits, 30 + 70); - assert_eq!(summaries[0].total_cost_cents, 10 + 25); - assert_eq!(summaries[1].total_credits, 30); - assert_eq!(summaries[1].total_cost_cents, 10); - assert_eq!(summaries[2].total_credits, 70); - assert_eq!(summaries[2].total_cost_cents, 25); -} diff --git a/app/src/settings_view/billing_and_usage/mod.rs b/app/src/settings_view/billing_and_usage/mod.rs deleted file mode 100644 index a1d9c139..00000000 --- a/app/src/settings_view/billing_and_usage/mod.rs +++ /dev/null @@ -1,7 +0,0 @@ -pub mod billing_cycle_usage_common; -pub mod billing_cycle_usage_rows; -pub mod billing_cycle_usage_section; -pub mod billing_cycle_usage_team_totals; -pub mod overage_limit_modal; -pub mod usage_history_entry; -pub mod usage_history_model; diff --git a/app/src/settings_view/billing_and_usage/overage_limit_modal.rs b/app/src/settings_view/billing_and_usage/overage_limit_modal.rs deleted file mode 100644 index 6b98e562..00000000 --- a/app/src/settings_view/billing_and_usage/overage_limit_modal.rs +++ /dev/null @@ -1,352 +0,0 @@ -use galaxyui::elements::{ - Align, Border, ChildView, Clipped, Container, CornerRadius, CrossAxisAlignment, Expanded, Flex, - MouseStateHandle, Padding, ParentElement, Radius, Text, -}; -use galaxyui::ui_components::button::ButtonVariant; -use galaxyui::ui_components::components::{Coords, UiComponent, UiComponentStyles}; -use galaxyui::{ - AppContext, Element, Entity, SingletonEntity, TypedActionView, View, ViewContext, ViewHandle, -}; - -use crate::editor::{EditorView, Event as EditorEvent, SingleLineEditorOptions, TextOptions}; -use crate::Appearance; - -const MAXIMUM_SPENDING_LIMIT_CENTS: u32 = 999999999; - -pub struct SpendingLimitModal { - amount_editor: ViewHandle, - cancel_button_mouse_state: MouseStateHandle, - update_button_mouse_state: MouseStateHandle, - input_error_state: Option, -} - -#[derive(Debug, Clone)] -pub enum SpendingLimitModalEvent { - Close, - Update { amount_cents: u32 }, -} - -impl Entity for SpendingLimitModal { - type Event = SpendingLimitModalEvent; -} - -#[derive(Debug, Clone)] -pub enum SpendingLimitModalAction { - Close, - Update, -} - -pub enum SpendingLimitModalInputErrorState { - InvalidNumberFormat, - NumberOutOfRange, -} - -impl SpendingLimitModal { - pub fn new(ctx: &mut ViewContext) -> Self { - let font_family = Appearance::as_ref(ctx).ui_font_family(); - let amount_editor = ctx.add_typed_action_view(|ctx| { - let options = SingleLineEditorOptions { - text: TextOptions { - font_family_override: Some(font_family), - ..Default::default() - }, - ..Default::default() - }; - let mut editor = EditorView::single_line(options, ctx); - editor.set_placeholder_text("50.00", ctx); - editor - }); - ctx.subscribe_to_view(&amount_editor, |me, _, event, ctx| { - me.handle_editor_event(event, ctx); - }); - - Self { - amount_editor, - cancel_button_mouse_state: MouseStateHandle::default(), - update_button_mouse_state: MouseStateHandle::default(), - input_error_state: None, - } - } - - fn parse_amount(&self, app: &AppContext) -> Option { - let text = self.amount_editor.as_ref(app).buffer_text(app); - let text = text.trim(); - if text.is_empty() { - return None; - } - - let cleaned = text.strip_prefix('$').unwrap_or(text); - - if let Ok(dollars) = cleaned.parse::() { - Some((dollars * 100.0).round() as u32) - } else { - None - } - } - - fn validate_input(&mut self, ctx: &mut ViewContext) { - let text = self.amount_editor.as_ref(ctx).buffer_text(ctx); - - if !self.is_valid_us_currency_format(&text) { - self.input_error_state = Some(SpendingLimitModalInputErrorState::InvalidNumberFormat); - } else if self - .parse_amount(ctx) - .is_some_and(|cents| !self.is_valid_number_range(cents)) - { - self.input_error_state = Some(SpendingLimitModalInputErrorState::NumberOutOfRange); - } else { - self.input_error_state = None; - } - - ctx.notify(); - } - - fn is_valid_number_range(&self, amount_cents: u32) -> bool { - if amount_cents < 1 { - return false; - } - - if amount_cents > MAXIMUM_SPENDING_LIMIT_CENTS { - return false; - } - - true - } - - fn is_valid_us_currency_format(&self, text: &str) -> bool { - if text.is_empty() { - return true; - } - - let cleaned = text.strip_prefix('$').unwrap_or(text); - - let decimal_count = cleaned.chars().filter(|&c| c == '.').count(); - if decimal_count > 1 { - return false; - } - - if !cleaned.chars().all(|c| c.is_ascii_digit() || c == '.') { - return false; - } - - if let Some(decimal_pos) = cleaned.find('.') { - let after_decimal = &cleaned[decimal_pos + 1..]; - if after_decimal.len() > 2 { - return false; - } - } - - true - } - - pub fn update_amount_editor(&self, cents: u32, ctx: &mut ViewContext) { - let placeholder_text = format!("{:.2}", cents as f64 / 100.0); - self.amount_editor.update(ctx, |editor, ctx| { - editor.set_placeholder_text(&placeholder_text, ctx); - editor.clear_buffer(ctx); - }); - - ctx.notify(); - } - - fn error_text(&self) -> Option { - match self.input_error_state { - Some(SpendingLimitModalInputErrorState::InvalidNumberFormat) => { - Some("Please enter a valid currency amount".to_string()) - } - Some(SpendingLimitModalInputErrorState::NumberOutOfRange) => { - Some("Please enter a price between $0.01 and $10,000,000".to_string()) - } - None => None, - } - } - - pub fn focus_input(&self, ctx: &mut ViewContext) { - ctx.focus(&self.amount_editor); - } - - fn handle_editor_event(&mut self, event: &EditorEvent, ctx: &mut ViewContext) { - match event { - EditorEvent::Enter => { - if self.input_error_state.is_none() { - if let Some(amount_cents) = self.parse_amount(ctx) { - ctx.emit(SpendingLimitModalEvent::Update { amount_cents }); - } - } - } - EditorEvent::Escape => { - ctx.emit(SpendingLimitModalEvent::Close); - } - EditorEvent::Edited(_) => { - self.validate_input(ctx); - } - _ => {} - } - } -} - -impl View for SpendingLimitModal { - fn ui_name() -> &'static str { - "SpendingLimitModal" - } - - fn render(&self, app: &AppContext) -> Box { - let appearance = Appearance::as_ref(app); - let theme = appearance.theme(); - - let description_text = Text::new( - "Warp will prevent use of premium models when this dollar limit is reached. Resets on a monthly basis.", - appearance.ui_font_family(), - 14., - ) - .with_color(theme.sub_text_color(theme.surface_2()).into()) - .finish(); - - let additional_note_text = Text::new( - "Note that AI credits made near your chosen limit may exceed it by a few dollars.", - appearance.ui_font_family(), - 12., - ) - .with_color(theme.sub_text_color(theme.surface_2()).into()) - .finish(); - - let description_section = Flex::column() - .with_child( - Container::new(description_text) - .with_margin_bottom(8.) - .finish(), - ) - .with_child(additional_note_text) - .finish(); - - let input_row = Flex::row() - .with_cross_axis_alignment(CrossAxisAlignment::Center) - .with_child( - Text::new("$", appearance.ui_font_family(), appearance.ui_font_size()) - .with_color(theme.active_ui_text_color().into()) - .finish(), - ) - .with_child( - Expanded::new( - 1., - Container::new( - Clipped::new(ChildView::new(&self.amount_editor).finish()).finish(), - ) - .with_margin_left(12.) - .finish(), - ) - .finish(), - ) - .finish(); - - let border_color = if self.input_error_state.is_some() { - theme.ui_error_color().into() - } else { - theme.outline() - }; - - let input_container = Container::new(input_row) - .with_padding(Padding::uniform(8.).with_left(16.).with_right(16.)) - .with_border(Border::all(1.).with_border_fill(border_color)) - .with_corner_radius(CornerRadius::with_all(Radius::Pixels(4.))) - .finish(); - - let button_style = UiComponentStyles { - font_size: Some(14.), - padding: Some(Coords::uniform(8.).left(12.).right(12.)), - ..Default::default() - }; - - let mut update_button = appearance - .ui_builder() - .button( - ButtonVariant::Accent, - self.update_button_mouse_state.clone(), - ) - .with_text_label("Update".to_string()) - .with_style(button_style); - - if self.input_error_state.is_some() { - update_button = update_button.disabled(); - } - - let buttons_row = Flex::row() - .with_child( - appearance - .ui_builder() - .button( - ButtonVariant::Secondary, - self.cancel_button_mouse_state.clone(), - ) - .with_text_label("Cancel".to_string()) - .with_style(button_style) - .build() - .on_click(|ctx, _, _| { - ctx.dispatch_typed_action(SpendingLimitModalAction::Close); - }) - .finish(), - ) - .with_child( - Container::new( - update_button - .build() - .on_click(|ctx, _, _| { - ctx.dispatch_typed_action(SpendingLimitModalAction::Update); - }) - .finish(), - ) - .with_margin_left(12.) - .finish(), - ) - .finish(); - - let mut main_column = Flex::column() - .with_child( - Container::new(description_section) - .with_margin_bottom(16.) - .finish(), - ) - .with_child( - Container::new(input_container) - .with_margin_bottom(if self.input_error_state.is_some() { - 8. - } else { - 24. - }) - .finish(), - ); - - if let Some(error_text) = self.error_text() { - let error_text = Text::new(error_text, appearance.ui_font_family(), 12.) - .with_color(theme.ui_error_color()) - .finish(); - - main_column = - main_column.with_child(Container::new(error_text).with_margin_bottom(24.).finish()); - } - - main_column - .with_child(Align::new(buttons_row).right().finish()) - .finish() - } -} - -impl TypedActionView for SpendingLimitModal { - type Action = SpendingLimitModalAction; - - fn handle_action(&mut self, action: &Self::Action, ctx: &mut ViewContext) { - match action { - SpendingLimitModalAction::Close => { - self.amount_editor.update(ctx, |editor, ctx| { - editor.clear_buffer(ctx); - }); - ctx.emit(SpendingLimitModalEvent::Close); - } - SpendingLimitModalAction::Update => { - if let Some(amount_cents) = self.parse_amount(ctx) { - ctx.emit(SpendingLimitModalEvent::Update { amount_cents }); - } - } - } - } -} diff --git a/app/src/settings_view/billing_and_usage/usage_history_entry.rs b/app/src/settings_view/billing_and_usage/usage_history_entry.rs deleted file mode 100644 index b3c09cae..00000000 --- a/app/src/settings_view/billing_and_usage/usage_history_entry.rs +++ /dev/null @@ -1,224 +0,0 @@ -use chrono::Local; -use galaxy_core::ui::appearance::Appearance; -use galaxy_graphql::queries::get_conversation_usage::ConversationUsage; -use galaxyui::elements::{ - Border, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Empty, Flex, Hoverable, - MainAxisAlignment, MainAxisSize, MouseStateHandle, ParentElement, Radius, Shrinkable, Text, -}; -use warpui::platform::Cursor; -use warpui::{AppContext, Element, View}; - -use crate::ai::blocklist::format_credits; -use crate::ai::blocklist::usage::conversation_usage_view::{ - ConversationUsageInfo, ConversationUsageView, DisplayMode, -}; -use crate::settings_view::billing_and_usage_page::BillingAndUsagePageAction; -use crate::ui_components::blended_colors; -use crate::ui_components::icons::Icon; - -pub struct UsageHistoryEntry { - // If no entry is provided, we will assume that this is a placeholder entry - // to display in the loading UI. - entry: Option, - is_expanded: bool, - mouse_state: Option, - tooltip_mouse_state: MouseStateHandle, -} - -impl UsageHistoryEntry { - pub fn new( - entry: Option, - is_expanded: bool, - mouse_state: Option, - tooltip_mouse_state: MouseStateHandle, - ) -> Self { - Self { - entry, - mouse_state, - is_expanded, - tooltip_mouse_state, - } - } - - pub fn render(&self, appearance: &Appearance, app: &AppContext) -> Box { - let mut res = Flex::column() - .with_cross_axis_alignment(CrossAxisAlignment::Stretch) - .with_child(self.render_header(appearance)); - - if let Some(entry) = &self.entry { - if self.is_expanded { - res = res - .with_child( - // Separator between header and usage component - Container::new(Empty::new().finish()) - .with_border( - Border::top(2.0).with_border_fill(appearance.theme().outline()), - ) - .with_overdraw_bottom(0.) - .finish(), - ) - .with_child( - ConversationUsageView::new( - ConversationUsageInfo::from(entry), - DisplayMode::Settings, - None, - self.tooltip_mouse_state.clone(), - ) - .render(app), - ); - } - } - - Container::new(res.finish()) - .with_border(Border::all(2.).with_border_fill(appearance.theme().surface_3())) - .with_background(appearance.theme().surface_2()) - .with_corner_radius(CornerRadius::with_all(Radius::Pixels(8.))) - .finish() - } - - fn render_header(&self, appearance: &Appearance) -> Box { - let Some(entry) = &self.entry else { - return self.render_loading_entry(appearance); - }; - let Some(mouse_state) = &self.mouse_state else { - // If there is a provided entry, there should always be a mouse state as well. - log::error!("Mouse state is required to render usage history entry header"); - return Empty::new().finish(); - }; - - let title_text = Text::new_inline(entry.title.clone(), appearance.ui_font_family(), 14.) - .with_color( - appearance - .theme() - .main_text_color(appearance.theme().surface_2()) - .into(), - ) - .finish(); - - let formatted_time = entry - .last_updated - .utc() - .with_timezone(&Local) - .format("%-m/%-d/%y %-I:%M %p") - .to_string(); - let time_text = Text::new_inline(formatted_time, appearance.ui_font_family(), 12.) - .with_color(blended_colors::text_sub( - appearance.theme(), - appearance.theme().surface_1(), - )) - .finish(); - - let total_credits = - entry.usage_metadata.credits_spent + entry.usage_metadata.platform_credits_spent; - let credits_spent = Text::new_inline( - format_credits(total_credits as f32), - appearance.ui_font_family(), - 14., - ) - .with_color(blended_colors::text_sub( - appearance.theme(), - appearance.theme().surface_1(), - )) - .finish(); - - let chevron_icon = if self.is_expanded { - Icon::ChevronDown - } else { - Icon::ChevronRight - }; - let chevron = ConstrainedBox::new( - chevron_icon - .to_galaxyui_icon(appearance.theme().foreground()) - .finish(), - ) - .with_width(16.) - .with_height(16.) - .finish(); - - let header_row = Container::new( - Flex::row() - .with_child( - Shrinkable::new( - 1., - Container::new( - Flex::column() - .with_child(title_text) - .with_child(Container::new(time_text).with_margin_top(4.).finish()) - .with_cross_axis_alignment(CrossAxisAlignment::Start) - .finish(), - ) - .finish(), - ) - .finish(), - ) - .with_child( - Flex::row() - .with_child(credits_spent) - .with_child(chevron) - .with_cross_axis_alignment(CrossAxisAlignment::Start) - .finish(), - ) - .with_cross_axis_alignment(CrossAxisAlignment::Start) - .with_main_axis_alignment(MainAxisAlignment::SpaceBetween) - .with_main_axis_size(MainAxisSize::Max) - .finish(), - ) - .with_uniform_padding(12.) - .with_corner_radius(if self.is_expanded { - CornerRadius::with_top(Radius::Pixels(6.)) - } else { - CornerRadius::with_all(Radius::Pixels(6.)) - }) - .with_background(appearance.theme().surface_2()) - .finish(); - - let conversation_id = entry.conversation_id.clone(); - Hoverable::new(mouse_state.clone(), |_| header_row) - .with_cursor(Cursor::PointingHand) - .on_click(move |ctx, _, _| { - ctx.dispatch_typed_action(BillingAndUsagePageAction::ToggleUsageEntryExpanded { - conversation_id: conversation_id.clone(), - }); - }) - .finish() - } - - /// Render a placeholder entry for the loading state - fn render_loading_entry(&self, appearance: &Appearance) -> Box { - let left_side = Flex::column() - .with_child(self.render_empty_text_placeholder(360., 16., appearance)) - .with_child(self.render_empty_text_placeholder(160., 12., appearance)) - .with_spacing(4.) - .with_cross_axis_alignment(CrossAxisAlignment::Start) - .finish(); - - Container::new( - Flex::row() - .with_child(left_side) - .with_child(self.render_empty_text_placeholder(52., 16., appearance)) - .with_cross_axis_alignment(CrossAxisAlignment::Start) - .with_main_axis_alignment(MainAxisAlignment::SpaceBetween) - .with_main_axis_size(MainAxisSize::Max) - .finish(), - ) - .with_uniform_padding(12.) - .with_corner_radius(CornerRadius::with_all(Radius::Pixels(6.))) - .with_background(appearance.theme().surface_2()) - .finish() - } - - /// Renders an empty rectangle to represent loading text - fn render_empty_text_placeholder( - &self, - width: f32, - height: f32, - appearance: &Appearance, - ) -> Box { - Container::new(Empty::new().finish()) - .with_padding_left(width) - .with_padding_top(height) - .with_background(appearance.theme().surface_3()) - .with_corner_radius(CornerRadius::with_all(Radius::Pixels(4.))) - .finish() - } -} diff --git a/app/src/settings_view/billing_and_usage/usage_history_model.rs b/app/src/settings_view/billing_and_usage/usage_history_model.rs deleted file mode 100644 index 6d73bbc9..00000000 --- a/app/src/settings_view/billing_and_usage/usage_history_model.rs +++ /dev/null @@ -1,140 +0,0 @@ -use std::sync::Arc; - -use galaxy_core::report_error; -use galaxy_graphql::scalars::Time; -use galaxyui::{Entity, ModelContext, SingletonEntity}; - -use crate::auth::AuthStateProvider; -use crate::server::server_api::ai::AIClient; -use crate::server::server_api::ServerApiProvider; - -const PAGE_SIZE: i32 = 20; - -pub struct UsageHistoryModel { - ai_client: Arc, - entries: Vec, - is_loading: bool, - // Whether the server indicated that there may be more entries to load. - has_more_entries: bool, -} - -impl Entity for UsageHistoryModel { - type Event = (); -} - -impl SingletonEntity for UsageHistoryModel {} - -impl UsageHistoryModel { - pub fn new(ctx: &mut ModelContext) -> Self { - let ai_client = ServerApiProvider::as_ref(ctx).get_ai_client(); - Self { - ai_client, - entries: Vec::new(), - is_loading: false, - has_more_entries: true, - } - } - - pub fn entries(&self) -> &[galaxy_graphql::queries::get_conversation_usage::ConversationUsage] { - &self.entries - } - - pub fn is_loading(&self) -> bool { - self.is_loading - } - - pub fn has_more_entries(&self) -> bool { - self.has_more_entries - } - - /// Fetches conversation usage over the past 30 days. - /// If some usage has already been loaded, this fetches the same number of entries. - /// If no usage has been loaded, this fetches PAGE_SIZE entries. - pub fn refresh_usage_history_async(&mut self, ctx: &mut ModelContext) { - if self.is_loading || !AuthStateProvider::as_ref(ctx).get().is_logged_in() { - return; - } - - // If the user has already loaded some number of entries, - // we should load that same number of items on refresh so that the list doesn't shrink - // every time the page is refreshed. - let num_items_to_fetch = if self.entries.is_empty() { - PAGE_SIZE - } else { - self.entries.len() as i32 - }; - - // Reset pagination state and clear any existing entries. - self.entries.clear(); - self.has_more_entries = true; - - self.fetch_next_page(num_items_to_fetch, None, ctx); - } - - /// Fetches the next page of conversation usage entries, appending them to the existing list. - pub fn load_more_usage_history_async(&mut self, ctx: &mut ModelContext) { - if self.is_loading || !self.has_more_entries { - return; - } - - let last_updated_end_timestamp: Option