diff --git a/.github/workflows/binaries.yml b/.github/workflows/binaries.yml index 4d9668a..eba1f53 100644 --- a/.github/workflows/binaries.yml +++ b/.github/workflows/binaries.yml @@ -60,9 +60,9 @@ jobs: path: ~/.cargo/registry key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} - name: Run sccache-cache - uses: mozilla-actions/sccache-action@v0.0.3 + uses: mozilla-actions/sccache-action@v0.0.9 - name: 'Build: binary' - run: cargo build --release --locked --target ${{ env.TARGET }} + run: cargo +stable build --release --locked --target ${{ env.TARGET }} - name: 'Upload: binary' uses: actions/upload-artifact@v4 with: @@ -73,8 +73,8 @@ jobs: - name: 'Package: deb' if: matrix.platform == 'ubuntu-latest' run: | - cargo install --locked cargo-deb - cargo deb --no-strip --target ${{ env.TARGET }} + cargo +stable install --locked cargo-deb + cargo +stable deb --no-strip --target ${{ env.TARGET }} - name: 'Upload: deb' if: matrix.platform == 'ubuntu-latest' uses: actions/upload-artifact@v4 @@ -84,8 +84,8 @@ jobs: - name: 'Package: rpm' if: matrix.platform == 'ubuntu-latest' run: | - cargo install --locked cargo-generate-rpm - cargo generate-rpm --target ${{ env.TARGET }} + cargo +stable install --locked cargo-generate-rpm + cargo +stable generate-rpm --target ${{ env.TARGET }} - name: 'Upload: rpm' if: matrix.platform == 'ubuntu-latest' uses: actions/upload-artifact@v4 diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 086e700..bafe274 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -22,8 +22,8 @@ jobs: uses: actions/checkout@v3 with: submodules: true - - name: Install Rust (1.70 w/ clippy) - uses: dtolnay/rust-toolchain@1.70 + - name: Install Rust (1.83 w/ clippy) + uses: dtolnay/rust-toolchain@1.83 with: components: clippy - name: Install Rust (nightly w/ rustfmt) @@ -34,7 +34,7 @@ jobs: path: ~/.cargo/registry key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} - name: Run sccache-cache - uses: mozilla-actions/sccache-action@v0.0.3 + uses: mozilla-actions/sccache-action@v0.0.9 - name: Check formatting run: cargo +nightly fmt --all -- --check - name: Check Clippy diff --git a/Cargo.lock b/Cargo.lock index afa92c9..0166516 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,33 +1,33 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] name = "accessory" -version = "1.3.0" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "850bb534b9dc04744fbbb71d30ad6d25a7e4cf6dc33e223c81ef3a92ebab4e0b" +checksum = "87537f9ae7cfa78d5b8ebd1a1db25959f5e737126be4d8eb44a5452fc4b63cde" dependencies = [ "macroific", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", ] [[package]] name = "addr2line" -version = "0.21.0" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" +checksum = "dfbe277e56a376000877090da837660b4427aad530e3028d44e0bffe4f89a1c1" dependencies = [ "gimli", ] [[package]] -name = "adler" -version = "1.0.2" +name = "adler2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" [[package]] name = "aead" @@ -52,9 +52,9 @@ dependencies = [ [[package]] name = "ahash" -version = "0.8.11" +version = "0.8.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" dependencies = [ "cfg-if", "once_cell", @@ -72,10 +72,16 @@ dependencies = [ ] [[package]] -name = "allocator-api2" -version = "0.2.18" +name = "aligned-vec" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c6cb57a04249c6480766f7f7cef5467412af1490f8d1e243141daddada3264f" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android-tzdata" @@ -109,26 +115,26 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.6" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8901269c6307e8d93993578286ac0edf7f195079ffff5ebdeea6a59ffb7e36bc" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.3" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c75ac65da39e5fe5ab759307499ddad880d724eed2f6ce5b5e8a26f4f387928c" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.2" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e28923312444cdd728e4738b3f9c9cac739500909bb3d3c94b43551b16517648" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -143,9 +149,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.82" +version = "1.0.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f538837af36e6f6a9be0faa67f9a314f8119e4e4b5867c6ab40ed60360142519" +checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" [[package]] name = "anymap2" @@ -155,58 +161,76 @@ checksum = "d301b3b94cb4b2f23d7917810addbbaff90738e0ca2be692bd027e70d7e0330c" [[package]] name = "aquamarine" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21cc1548309245035eb18aa7f0967da6bc65587005170c56e6ef2788a4cf3f4e" +checksum = "0f50776554130342de4836ba542aa85a4ddb361690d7e8df13774d7284c3d5c2" dependencies = [ "include_dir", "itertools 0.10.5", - "proc-macro-error", + "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", ] [[package]] -name = "arboard" -version = "3.3.2" +name = "arbitrary" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2041f1943049c7978768d84e6d0fd95de98b76d6c4727b09e78ec253d29fa58" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" + +[[package]] +name = "arboard" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1df21f715862ede32a0c525ce2ca4d52626bb0007f8c18b87a384503ac33e70" dependencies = [ "clipboard-win", - "core-graphics", "image", "log", - "objc", - "objc-foundation", - "objc_id", - "parking_lot 0.12.1", - "thiserror", - "windows-sys 0.48.0", + "objc2", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation", + "parking_lot 0.12.4", + "percent-encoding", + "windows-sys 0.59.0", "wl-clipboard-rs", "x11rb", ] [[package]] -name = "arrayref" -version = "0.3.7" +name = "arg_enum_proc_macro" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6b4930d2cb77ce62f89ee5d5289b4ac049559b1c45539271f5ed4fdc7db34545" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" dependencies = [ "serde", ] [[package]] name = "as_variant" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38fa22307249f86fb7fad906fcae77f2564caeb56d7209103c551cd1cf4798f" +checksum = "9dbc3a507a82b17ba0d98f6ce8fd6954ea0c8152e98009d36a40d8dcc8ce078a" [[package]] name = "assign" @@ -226,28 +250,40 @@ dependencies = [ [[package]] name = "async-channel" -version = "2.2.1" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "136d4d23bcc79e27423727b36823d86233aad06dfea531837b038394d11e9928" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" dependencies = [ "concurrent-queue", - "event-listener 5.3.0", - "event-listener-strategy 0.5.1", + "event-listener-strategy", "futures-core", "pin-project-lite", ] [[package]] -name = "async-executor" -version = "1.8.0" +name = "async-compression" +version = "0.4.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17ae5ebefcc48e7452b4987947920dac9450be1110cadf34d1b8c116bdbaf97c" +checksum = "b37fc50485c4f3f736a4fb14199f6d5f5ba008d7f28fe710306c92780f004c07" +dependencies = [ + "flate2", + "futures-core", + "memchr", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "async-executor" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb812ffb58524bdd10860d7d974e2f01cc0950c2438a74ee5ec2e2280c6c4ffa" dependencies = [ - "async-lock 3.3.0", "async-task", "concurrent-queue", - "fastrand 2.0.2", - "futures-lite 2.3.0", + "fastrand 2.3.0", + "futures-lite 2.6.0", + "pin-project-lite", "slab", ] @@ -277,7 +313,7 @@ dependencies = [ "log", "parking", "polling 2.8.0", - "rustix 0.37.27", + "rustix 0.37.28", "slab", "socket2 0.4.10", "waker-fn", @@ -285,21 +321,21 @@ dependencies = [ [[package]] name = "async-io" -version = "2.3.2" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dcccb0f599cfa2f8ace422d3555572f47424da5648a4382a9dd0310ff8210884" +checksum = "1237c0ae75a0f3765f58910ff9cdd0a12eeb39ab2f4c7de23262f337f0aacbb3" dependencies = [ - "async-lock 3.3.0", + "async-lock 3.4.0", "cfg-if", "concurrent-queue", "futures-io", - "futures-lite 2.3.0", + "futures-lite 2.6.0", "parking", - "polling 3.5.0", - "rustix 0.38.32", + "polling 3.8.0", + "rustix 1.0.7", "slab", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -313,12 +349,12 @@ dependencies = [ [[package]] name = "async-lock" -version = "3.3.0" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d034b430882f8381900d3fe6f0aaa3ad94f2cb4ac519b429692a1bc2dda4ae7b" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "event-listener 4.0.3", - "event-listener-strategy 0.4.0", + "event-listener 5.4.0", + "event-listener-strategy", "pin-project-lite", ] @@ -335,44 +371,44 @@ dependencies = [ "cfg-if", "event-listener 3.1.0", "futures-lite 1.13.0", - "rustix 0.38.32", + "rustix 0.38.44", "windows-sys 0.48.0", ] [[package]] name = "async-recursion" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30c5ef0ede93efbf733c1a727f3b6b5a1060bbedd5600183e66f6e4be4af0ec5" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", ] [[package]] name = "async-signal" -version = "0.2.5" +version = "0.2.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e47d90f65a225c4527103a8d747001fc56e375203592b25ad103e1ca13124c5" +checksum = "d7605a4e50d4b06df3898d5a70bf5fde51ed9059b0434b73105193bc27acce0d" dependencies = [ - "async-io 2.3.2", - "async-lock 2.8.0", + "async-io 2.4.1", + "async-lock 3.4.0", "atomic-waker", "cfg-if", "futures-core", "futures-io", - "rustix 0.38.32", + "rustix 1.0.7", "signal-hook-registry", "slab", - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] name = "async-stream" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd56dd203fef61ac097dd65721a419ddccb106b2d2b70ba60a6b529f03961a51" +checksum = "0b5a71a6f37880a80d1d7f19efd781e4b5de42c88f0722cc13bcb6cc2cfe8476" dependencies = [ "async-stream-impl", "futures-core", @@ -381,30 +417,30 @@ dependencies = [ [[package]] name = "async-stream-impl" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16e62a023e7c117e27523144c5d2459f4397fcc3cab0085af8e2224f643a0193" +checksum = "c7c24de15d275a1ecfd47a380fb4d5ec9bfe0933f309ed5e705b775596a3574d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", ] [[package]] name = "async-task" -version = "4.7.0" +version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbb36e985947064623dbd357f727af08ffd077f93d696782f3c56365fa2e2799" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.80" +version = "0.1.88" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "e539d3fca749fcee5236ab05e93a52867dd549cc157c8cb7f99595f3cedffdb5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", ] [[package]] @@ -415,9 +451,86 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.2.0" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1fdabc7756949593fe60f30ec81974b613357de856987752631dea1e3394c80" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" + +[[package]] +name = "av1-grain" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f3efb2ca85bc610acfa917b5aaa36f3fcbebed5b3182d7f877b02531c4b80c8" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "98922d6a4cfbcb08820c69d8eeccc05bb1f29bfa06b4f5b1dbfe9a868bd7608e" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "axum" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "021e862c184ae977658b36c4500f7feac3221ca5da43e3f25bd04ab6c79a29b5" +dependencies = [ + "axum-core", + "bytes", + "form_urlencoded", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-util", + "itoa", + "matchit", + "memchr", + "mime", + "percent-encoding", + "pin-project-lite", + "rustversion", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "sync_wrapper", + "tokio", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "axum-core" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68464cd0412f486726fb3373129ef5d2993f90c34bc2bc1c1e9943b2f4fc7ca6" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", + "http-body-util", + "mime", + "pin-project-lite", + "rustversion", + "sync_wrapper", + "tower-layer", + "tower-service", + "tracing", +] [[package]] name = "backoff" @@ -426,26 +539,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b62ddb9cb1ec0a098ad4bbf9344d0713fa193ae1a80af55febcff2627b6a00c1" dependencies = [ "futures-core", - "getrandom", + "getrandom 0.2.16", "instant", "pin-project-lite", - "rand", + "rand 0.8.5", "tokio", ] [[package]] name = "backtrace" -version = "0.3.71" +version = "0.3.75" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" +checksum = "6806a6321ec58106fea15becdad98371e28d92ccbc7c8f1b3b6dd724fe8f1002" dependencies = [ "addr2line", - "cc", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", + "windows-targets 0.52.6", ] [[package]] @@ -455,10 +568,25 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] -name = "base64ct" -version = "1.6.0" +name = "base64" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c3c1a368f70d6cf7302d78f8f7093da241fb8e8807c05cc9e51a125895a6d5b" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" + +[[package]] +name = "base64ct" +version = "1.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89e25b6adfb930f02d1981565a6e5d9c547ac15a96606256d3b59040e5cd4ca3" + +[[package]] +name = "bincode" +version = "1.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1f45e9417d87227c7a56d22e471c6206462cba514c7590c09aff4cf6d1ddcad" +dependencies = [ + "serde", +] [[package]] name = "bit_field" @@ -474,9 +602,12 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.5.0" +version = "2.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4b9d6a944f767f8e5e0db018570623c85f3d925ac718db4e06d0187adb21c1" +checksum = "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967" +dependencies = [ + "serde", +] [[package]] name = "bitmaps" @@ -485,10 +616,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d084b0137aaa901caf9f1e8b21daa6aa24d41cd806e111335541eff9683bd6" [[package]] -name = "blake3" -version = "1.5.1" +name = "bitstream-io" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30cca6d3674597c30ddf2c587bf8d9d65c9a84d2326d941cc79c9842dfe0ef52" +checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" + +[[package]] +name = "blake3" +version = "1.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3888aaa89e4b2a40fca9848e400f6a658a5a3978de7be858e209cafa8be9a4a0" dependencies = [ "arrayref", "arrayvec", @@ -497,12 +634,6 @@ dependencies = [ "constant_time_eq", ] -[[package]] -name = "block" -version = "0.1.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" - [[package]] name = "block-buffer" version = "0.10.4" @@ -522,19 +653,25 @@ dependencies = [ ] [[package]] -name = "blocking" -version = "1.5.1" +name = "block2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a37913e8dc4ddcc604f0c6d3bf2887c995153af3611de9e23c352b44c1b9118" +checksum = "340d2f0bdb2a43c1d3cd40513185b2bd7def0aa1052f956455114bc98f82dcf2" +dependencies = [ + "objc2", +] + +[[package]] +name = "blocking" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" dependencies = [ "async-channel", - "async-lock 3.3.0", "async-task", - "fastrand 2.0.2", "futures-io", - "futures-lite 2.3.0", + "futures-lite 2.6.0", "piper", - "tracing", ] [[package]] @@ -547,16 +684,33 @@ dependencies = [ ] [[package]] -name = "bumpalo" -version = "3.16.0" +name = "bstr" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" +checksum = "234113d19d0d7d613b40e86fb654acf958910802bcceab913a4f9e7cda03b1a4" +dependencies = [ + "memchr", + "regex-automata", + "serde", +] + +[[package]] +name = "built" +version = "0.7.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56ed6191a7e78c36abdb16ab65341eefd73d64d303fffccdbb00d51e4205967b" + +[[package]] +name = "bumpalo" +version = "3.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1628fb46dfa0b37568d12e5edd512553eccf6a22a78e8bde00bb4aed84d5bdbf" [[package]] name = "bytemuck" -version = "1.15.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d68c57235a3a081186990eca2867354726650f42f7516ca50c28d6281fd15" +checksum = "9134a6ef01ce4b366b50689c94f82c14bc72bc5d0386829828a2e2752ef7958c" [[package]] name = "byteorder" @@ -565,16 +719,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] -name = "bytes" -version = "1.6.0" +name = "byteorder-lite" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" + +[[package]] +name = "bytes" +version = "1.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71b6127be86fdcfddb610f7182ac57211d4b18a3e9c82eb2d17662f2227ad6a" [[package]] name = "bytesize" -version = "1.3.0" +version = "1.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e368af43e418a04d52505cf3dbc23dda4e3407ae2fa99fd0e4f308ce546acc" +checksum = "2e93abca9e28e0a1b9877922aacb20576e05d4679ffa78c3d6dc22a26a216659" [[package]] name = "cassowary" @@ -584,9 +744,9 @@ checksum = "df8670b8c7b9dae1793364eafadf7239c40d669904660c5960d74cfd80b46a53" [[package]] name = "castaway" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a17ed5635fc8536268e5d4de1e22e81ac34419e5f052d4d51f4e01dcc263fcc" +checksum = "0abae9be0aaf9ea96a3b1b8b1b55c602ca751eba1b1500220cea4ecbafe7c0d5" dependencies = [ "rustversion", ] @@ -602,9 +762,24 @@ dependencies = [ [[package]] name = "cc" -version = "1.0.94" +version = "1.2.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f6e324229dc011159fcc089755d1e2e216a90d43a7dea6853ca740b84f35e7" +checksum = "d0fc897dc1e865cc67c0e05a836d9d3f1df3cbe442aa4a9473b18e12624a4951" +dependencies = [ + "jobserver", + "libc", + "shlex", +] + +[[package]] +name = "cfg-expr" +version = "0.15.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] [[package]] name = "cfg-if" @@ -612,23 +787,11 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" -[[package]] -name = "cfg-vis" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3a2c3bf5fc10fe2ca157564fbe08a4cb2b0a7d2ff3fe2f9683e65d5e7c7859c" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro2", - "quote", - "syn 1.0.109", -] - [[package]] name = "cfg_aliases" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chacha20" @@ -656,16 +819,16 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.31" +version = "0.4.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "c469d952047f47f91b68d1cba3f10d63c11d73e4636f24f08daf0278abf01c4d" dependencies = [ "android-tzdata", "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", - "windows-targets 0.48.5", + "windows-link", ] [[package]] @@ -699,7 +862,7 @@ dependencies = [ "anstream", "anstyle", "clap_lex", - "strsim", + "strsim 0.10.0", ] [[package]] @@ -708,10 +871,10 @@ version = "4.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54a9bb5758fc5dfe728d1019941681eccaf0cf8a4189b692a0ee2f2ecf90a050" dependencies = [ - "heck", + "heck 0.4.1", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", ] [[package]] @@ -722,9 +885,9 @@ checksum = "cd7cc57abe963c6d3b9d8be5b06ba7c8957a930305ca90304f24ef040aa6f961" [[package]] name = "clipboard-win" -version = "5.3.1" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "79f4473f5144e20d9aceaf2972478f06ddf687831eafeeb434fbaf0acc4144ad" +checksum = "15efe7a882b08f34e38556b14f2fb3daa98769d06c7f0c1b076dfd0d983bc892" dependencies = [ "error-code", ] @@ -737,19 +900,20 @@ checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] name = "colorchoice" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] name = "compact_str" -version = "0.7.1" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f86b9c4c00838774a6d902ef931eff7470720c51d90c2e32cfe15dc304737b3f" +checksum = "3b79c4069c6cad78e2e0cdfcbd26275770669fb39fd308a752dc110e83b9af32" dependencies = [ "castaway", "cfg-if", "itoa", + "rustversion", "ryu", "static_assertions", ] @@ -773,9 +937,9 @@ dependencies = [ [[package]] name = "concurrent-queue" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16048cd947b08fa32c24458a22f5dc5e835264f689f4f5653210c69fd107363" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ "crossbeam-utils", ] @@ -788,15 +952,15 @@ checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" [[package]] name = "const_panic" -version = "0.2.8" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6051f239ecec86fde3410901ab7860d458d160371533842974fc61f96d15879b" +checksum = "2459fc9262a1aa204eb4b5764ad4f189caec88aea9634389c0a25f8be7f6265e" [[package]] name = "constant_time_eq" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7144d30dcf0fafbce74250a3963025d8d52177934239851c917d29f1df280c2" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] name = "convert_case" @@ -816,66 +980,42 @@ dependencies = [ [[package]] name = "core-foundation-sys" -version = "0.8.6" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06ea2b9bc92be3c2baa9334a323ebca2d6f074ff852cd1d7b11064035cd3868f" - -[[package]] -name = "core-graphics" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", - "foreign-types 0.5.0", - "libc", -] - -[[package]] -name = "core-graphics-types" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" -dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "libc", -] +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "cpufeatures" -version = "0.2.12" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53fe5e26ff1b7aef8bca9c6080520cfb8d9333c7568e1829cef191a9723e5504" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" dependencies = [ "libc", ] [[package]] name = "crc32fast" -version = "1.4.0" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3855a8a784b474f333699ef2bbca9db2c4a1f6d9088a90a2d25b1eb53111eaa" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" dependencies = [ "cfg-if", ] [[package]] name = "crossbeam-channel" -version = "0.5.12" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3db02a9c5b5121e1e42fbdb1aeb65f5e02624cc58c43f2884c6ccac0b82f95" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-deque" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613f8cc01fe9cf1a3eb3d7f488fd2fa8388403e97039e2f73692932e291a770d" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", @@ -892,21 +1032,21 @@ dependencies = [ [[package]] name = "crossbeam-utils" -version = "0.8.19" +version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "248e3bacc7dc6baa3b21e405ee045c3047101a49145e7e9eca583ab4c2ca5345" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "crossterm" -version = "0.27.0" +version = "0.28.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f476fe445d41c9e991fd07515a6f463074b782242ccf4a5b7b1d1012e70824df" +checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.9.1", "crossterm_winapi", - "libc", "mio", - "parking_lot 0.12.1", + "parking_lot 0.12.4", + "rustix 0.38.44", "signal-hook", "signal-hook-mio", "winapi", @@ -923,9 +1063,9 @@ dependencies = [ [[package]] name = "crunchy" -version = "0.2.2" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" [[package]] name = "crypto-common" @@ -934,7 +1074,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" dependencies = [ "generic-array", - "rand_core", + "rand_core 0.6.4", "typenum", ] @@ -958,16 +1098,15 @@ dependencies = [ [[package]] name = "curve25519-dalek" -version = "4.1.2" +version = "4.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a677b8922c94e01bdbb12126b0bc852f00447528dee1782229af9c720c3f348" +checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", "cpufeatures", "curve25519-dalek-derive", "digest", "fiat-crypto", - "platforms", "rustc_version", "serde", "subtle", @@ -982,7 +1121,7 @@ checksum = "f46882e17999c6cc590af592290432be3bce0428cb0d5f8b6715e4dc7b383eb3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", ] [[package]] @@ -991,8 +1130,18 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b750cb3417fd1b327431a470f388520309479ab0bf5e323505daf0290cd3850" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.14.4", + "darling_macro 0.14.4", +] + +[[package]] +name = "darling" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +dependencies = [ + "darling_core 0.20.11", + "darling_macro 0.20.11", ] [[package]] @@ -1005,28 +1154,58 @@ dependencies = [ "ident_case", "proc-macro2", "quote", - "strsim", + "strsim 0.10.0", "syn 1.0.109", ] +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim 0.11.1", + "syn 2.0.101", +] + [[package]] name = "darling_macro" version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a4aab4dbc9f7611d8b55048a3a16d2d010c2c8334e46304b40ac1cc14bf3b48e" dependencies = [ - "darling_core", + "darling_core 0.14.4", "quote", "syn 1.0.109", ] [[package]] -name = "deadpool" -version = "0.10.0" +name = "darling_macro" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb84100978c1c7b37f09ed3ce3e5f843af02c2a2c431bae5b19230dad2c1b490" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "date_header" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0c03c416ed1a30fbb027ef484ba6ab6f80e1eada675e1a2b92fd673c045a1f1d" + +[[package]] +name = "deadpool" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ed5957ff93768adf7a65ab167a17835c3d2c3c50d084fe305174c112f468e2f" dependencies = [ - "async-trait", "deadpool-runtime", "num_cpus", "tokio", @@ -1034,18 +1213,18 @@ dependencies = [ [[package]] name = "deadpool-runtime" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63dfa964fe2a66f3fde91fc70b267fe193d822c7e603e2a675a49a7f46ad3f49" +checksum = "092966b41edc516079bdf31ec78a2e0588d1d0c08f78b91d8307215928642b2b" dependencies = [ "tokio", ] [[package]] name = "deadpool-sqlite" -version = "0.7.0" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8010e36e12f3be22543a5e478b4af20aeead9a700dd69581a5e050a070fc22c" +checksum = "656f14fc1ab819c65f332045ea7cb38841bbe551f3b2bc7e3abefb559af4155c" dependencies = [ "deadpool", "deadpool-sync", @@ -1054,13 +1233,23 @@ dependencies = [ [[package]] name = "deadpool-sync" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8db70494c13cae4ce67b4b4dafdaf828cf0df7237ab5b9e2fcabee4965d0a0a" +checksum = "524bc3df0d57e98ecd022e21ba31166c2625e7d3e5bcc4510efaeeab4abcab04" dependencies = [ "deadpool-runtime", ] +[[package]] +name = "decancer" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d59c633be7ba6fcf3c153e11d647990e4e4bd463a4d5834ba0a45caa96da6baf" +dependencies = [ + "lazy_static 1.5.0", + "regex", +] + [[package]] name = "delegate-display" version = "2.1.1" @@ -1070,37 +1259,24 @@ dependencies = [ "macroific", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", ] [[package]] name = "der" -version = "0.7.9" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f55bf8e7b65898637379c1b74eb1551107c8294ed26d855ceb9fd1a09cfc9bc0" +checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb" dependencies = [ "const-oid", - "der_derive", - "flagset", "zeroize", ] -[[package]] -name = "der_derive" -version = "0.7.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fe87ce4529967e0ba1dcf8450bab64d97dfd5010a6256187ffe2e43e6f0e049" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.59", -] - [[package]] name = "deranged" -version = "0.3.11" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +checksum = "9c9e6a11ca8224451684bc0d7d5a7adbf8f2fd6887261a1cfc3c0432f9d4068e" dependencies = [ "powerfmt", ] @@ -1116,17 +1292,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "derive-new" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d150dea618e920167e5973d70ae6ece4385b7164e0d799fe7c122dd0a5d912ad" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.59", -] - [[package]] name = "derive_builder" version = "0.12.0" @@ -1142,7 +1307,7 @@ version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c11bdc11a0c47bc7d37d582b5285da6849c96681023680b906673c5707af7b0f" dependencies = [ - "darling", + "darling 0.14.4", "proc-macro2", "quote", "syn 1.0.109", @@ -1160,22 +1325,22 @@ dependencies = [ [[package]] name = "derive_more" -version = "0.99.17" +version = "0.99.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fb810d30a7c1953f91334de7244731fc3f3c10d7fe163338a35b9f640960321" +checksum = "6edb4b64a43d977b8e99788fe3a04d483834fba1215a7e02caa415b626497f7f" dependencies = [ "convert_case", "proc-macro2", "quote", "rustc_version", - "syn 1.0.109", + "syn 2.0.101", ] [[package]] name = "deunicode" -version = "1.4.4" +version = "1.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "322ef0094744e63628e6f0eb2295517f79276a5b342a4c2ff3042566ca181d4e" +checksum = "abd57806937c9cc163efc8ea3910e00a62e2aeb0b8119f1793a978088f8f6b04" [[package]] name = "diff" @@ -1203,16 +1368,6 @@ dependencies = [ "dirs-sys", ] -[[package]] -name = "dirs-next" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" -dependencies = [ - "cfg-if", - "dirs-sys-next", -] - [[package]] name = "dirs-sys" version = "0.3.7" @@ -1225,34 +1380,24 @@ dependencies = [ ] [[package]] -name = "dirs-sys-next" -version = "0.1.2" +name = "dispatch2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" +checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ - "libc", - "redox_users", - "winapi", + "bitflags 2.9.1", + "objc2", ] [[package]] name = "displaydoc" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "487585f4d0c6655fe74905e2504d8ad6908e4db67f744eb140876906c2f3175d" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", -] - -[[package]] -name = "dlib" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" -dependencies = [ - "libloading", + "syn 2.0.101", ] [[package]] @@ -1261,12 +1406,6 @@ version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" -[[package]] -name = "dyn-clone" -version = "1.0.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d2f3407d9a573d666de4b5bdf10569d73ca9478087346697dcbae6244bfbcd" - [[package]] name = "ed25519" version = "2.2.3" @@ -1286,7 +1425,7 @@ checksum = "4a3daa8e81a3963a60642bcc1f90a670680bd4a77535faa384e9d1c79d620871" dependencies = [ "curve25519-dalek", "ed25519", - "rand_core", + "rand_core 0.6.4", "serde", "sha2", "subtle", @@ -1295,19 +1434,54 @@ dependencies = [ [[package]] name = "edit" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c562aa71f7bc691fde4c6bf5f93ae5a5298b617c2eb44c76c87832299a17fbb4" +checksum = "f364860e764787163c8c8f58231003839be31276e821e2ad2092ddf496b1aa09" dependencies = [ "tempfile", "which", ] [[package]] -name = "either" -version = "1.11.0" +name = "editor-types" +version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a47c1c47d2f5964e29c61246e81db715514cd532db6b5116a25ea3c03d6780a2" +checksum = "a9e9e99679670f67825fcd24a23cb4eb655a0f92c82bd4d1c1a1357c0cd71e87" +dependencies = [ + "bitflags 2.9.1", + "editor-types-macros", + "keybindings", + "regex", +] + +[[package]] +name = "editor-types-macros" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42680de76cf91f231abd90cc623750d39077f7d2fadb7962325fb082871f4c66" +dependencies = [ + "editor-types-parser", + "nom", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "editor-types-parser" +version = "0.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cac4b91fe830fbbe0a60c37ba0264b6e9ffc70e3664c028234dac59e79299ad4" +dependencies = [ + "nom", + "thiserror 1.0.69", +] + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "emojis" @@ -1315,16 +1489,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3407bc749191827d456a282321770847daf4b0a1128fde02597a8ed2e987b95d" dependencies = [ - "phf 0.11.2", -] - -[[package]] -name = "encoding_rs" -version = "0.8.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59" -dependencies = [ - "cfg-if", + "phf 0.11.3", ] [[package]] @@ -1341,9 +1506,9 @@ checksum = "b5320ae4c3782150d900b79807611a59a99fc9a1d61d686faafc24b93fc8d7ca" [[package]] name = "enumflags2" -version = "0.7.9" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3278c9d5fb675e0a51dabcf4c0d355f692b064171535ba72361be1528a9d8e8d" +checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" dependencies = [ "enumflags2_derive", "serde", @@ -1351,36 +1516,36 @@ dependencies = [ [[package]] name = "enumflags2_derive" -version = "0.7.9" +version = "0.7.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" +checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", ] [[package]] name = "equivalent" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" -version = "0.3.8" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a258e46cdc063eb8519c00b9fc845fc47bcfca4130e2f08e88665ceda8474245" +checksum = "cea14ef9355e3beab063703aa9dab15afd25f0667c341310c1e5274bb1d0da18" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] name = "error-code" -version = "3.2.0" +version = "3.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0474425d51df81997e2f90a21591180b38eccf27292d755f3e30750225c175b" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" [[package]] name = "event-listener" @@ -1401,20 +1566,9 @@ dependencies = [ [[package]] name = "event-listener" -version = "4.0.3" +version = "5.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67b215c49b2b248c855fb73579eb1f4f26c38ffdc12973e20e07b91d78d5646e" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d9944b8ca13534cdfb2800775f8dd4902ff3fc75a50101466decadfdf322a24" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" dependencies = [ "concurrent-queue", "parking", @@ -1423,32 +1577,21 @@ dependencies = [ [[package]] name = "event-listener-strategy" -version = "0.4.0" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958e4d70b6d5e81971bebec42271ec641e7ff4e170a6fa605f2b8a8b65cb97d3" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" dependencies = [ - "event-listener 4.0.3", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "332f51cb23d20b0de8458b86580878211da09bcd4503cb579c225b3d124cabb3" -dependencies = [ - "event-listener 5.3.0", + "event-listener 5.4.0", "pin-project-lite", ] [[package]] name = "exr" -version = "1.72.0" +version = "1.73.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "887d93f60543e9a9362ef8a21beedd0a833c5d9610e18c67abe15a5963dcb1a4" +checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" dependencies = [ "bit_field", - "flume", "half", "lebe", "miniz_oxide", @@ -1459,25 +1602,27 @@ dependencies = [ [[package]] name = "eyeball" -version = "0.8.7" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42482893d982111055ce4b24234d6250396d3785767c6b04cedd84612a0b80fb" +checksum = "d93bd0ebf93d61d6332d3c09a96e97975968a44e19a64c947bde06e6baff383f" dependencies = [ "futures-core", "readlock", + "readlock-tokio", + "tokio", + "tokio-util", "tracing", ] [[package]] name = "eyeball-im" -version = "0.4.2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "021fab29d9670be5867b16d56a95c29a12c3c1bb654e7d589010a028716d625d" +checksum = "2bce23da1ef2af356501c395426370ef09d778989557a749a874645ec74aba23" dependencies = [ "futures-core", "imbl", "tokio", - "tokio-util", "tracing", ] @@ -1495,14 +1640,14 @@ checksum = "7360491ce676a36bf9bb3c56c1aa791658183a54d2744120f27285738d90465a" [[package]] name = "fancy_constructor" -version = "1.2.2" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f71f317e4af73b2f8f608fac190c52eac4b1879d2145df1db2fe48881ca69435" +checksum = "07b19d0e43eae2bfbafe4931b5e79c73fb1a849ca15cd41a761a7b8587f9a1a2" dependencies = [ "macroific", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", ] [[package]] @@ -1516,24 +1661,37 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.0.2" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "658bd65b1cf4c852a3cc96f18a8ce7b5640f6b703f905c7d74532294c2a63984" +checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" [[package]] name = "fdeflate" -version = "0.3.4" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f9bfee30e4dedf0ab8b422f03af778d9612b63f502710fc500a334ebe2de645" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" dependencies = [ "simd-adler32", ] [[package]] -name = "fiat-crypto" -version = "0.2.7" +name = "feruca" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c007b1ae3abe1cb6f85a16305acd418b7ca6343b953633fee2b76d8f108b830f" +checksum = "06eccaab9dc53ad4bffb4ed748baf5c1f9475d5e9cac35e1b8eac69dac56899e" +dependencies = [ + "bincode", + "bstr", + "once_cell", + "rustc-hash", + "unicode-canonical-combining-class", +] + +[[package]] +name = "fiat-crypto" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "fixedbitset" @@ -1541,65 +1699,35 @@ version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80" -[[package]] -name = "flagset" -version = "0.4.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdeb3aa5e95cf9aabc17f060cfa0ced7b83f042390760ca53bf09df9968acaa1" - [[package]] name = "flate2" -version = "1.0.28" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46303f565772937ffe1d394a4fac6f411c6013172fadde9dcdb1e147a086940e" +checksum = "7ced92e76e966ca2fd84c8f7aa01a4aea65b0eb6648d72f7c8f3e2764a67fece" dependencies = [ "crc32fast", "miniz_oxide", ] -[[package]] -name = "flume" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181" -dependencies = [ - "spin", -] - [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foreign-types" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" dependencies = [ - "foreign-types-shared 0.1.1", -] - -[[package]] -name = "foreign-types" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" -dependencies = [ - "foreign-types-macros", - "foreign-types-shared 0.3.1", -] - -[[package]] -name = "foreign-types-macros" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.59", + "foreign-types-shared", ] [[package]] @@ -1608,12 +1736,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" -[[package]] -name = "foreign-types-shared" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" - [[package]] name = "form_urlencoded" version = "1.2.1" @@ -1645,9 +1767,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -1660,9 +1782,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -1670,15 +1792,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -1687,9 +1809,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" @@ -1708,11 +1830,11 @@ dependencies = [ [[package]] name = "futures-lite" -version = "2.3.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52527eb5074e35e9339c6b4e8d12600c7128b68fb25dcb9fa9dec18f7c25f3a5" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" dependencies = [ - "fastrand 2.0.2", + "fastrand 2.3.0", "futures-core", "futures-io", "parking", @@ -1721,32 +1843,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", ] [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -1791,14 +1913,28 @@ dependencies = [ [[package]] name = "getrandom" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "94b22e06ecb0110981051723910cbf0b5f5e09a2062dd7663334ee79a9d1286c" +checksum = "335ff9f135e4384c8150d6f27c6daed433577f86b4750418338c01a1a2528592" dependencies = [ "cfg-if", "js-sys", "libc", - "wasi", + "wasi 0.11.0+wasi-snapshot-preview1", + "wasm-bindgen", +] + +[[package]] +name = "getrandom" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4" +dependencies = [ + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasi 0.14.2+wasi-0.2.4", "wasm-bindgen", ] @@ -1814,9 +1950,9 @@ dependencies = [ [[package]] name = "gimli" -version = "0.28.1" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "07e28edb80900c19c28f1072f2e8aeca7fa06b23cd4169cefe1af5aa3260783f" [[package]] name = "gloo-timers" @@ -1844,16 +1980,28 @@ dependencies = [ ] [[package]] -name = "h2" -version = "0.3.26" +name = "growable-bloom-filter" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" +checksum = "d174ccb4ba660d431329e7f0797870d0a4281e36353ec4b4a3c5eab6c2cfb6f1" dependencies = [ + "serde", + "serde_bytes", + "serde_derive", + "xxhash-rust", +] + +[[package]] +name = "h2" +version = "0.4.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9421a676d1b147b16b82c9225157dc629087ef8ec4d5e2960f9437a90dac0a5" +dependencies = [ + "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "futures-util", "http", "indexmap", "slab", @@ -1864,9 +2012,9 @@ dependencies = [ [[package]] name = "half" -version = "2.4.1" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +checksum = "459196ed295495a68f7d7fe1d84f6c4b7ff0e21fe3017b2f283c6fac3ad803c9" dependencies = [ "cfg-if", "crunchy", @@ -1874,21 +2022,31 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.14.3" +version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" +checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" dependencies = [ "ahash", +] + +[[package]] +name = "hashbrown" +version = "0.15.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84b26c544d002229e640969970a2e74021aadf6e2f96372b9c58eff97de08eb3" +dependencies = [ "allocator-api2", + "equivalent", + "foldhash", ] [[package]] name = "hashlink" -version = "0.8.4" +version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8094feaf31ff591f651a2664fb9cfd92bba7a60ce3197265e9482ebe753c8f7" +checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" dependencies = [ - "hashbrown", + "hashbrown 0.14.5", ] [[package]] @@ -1897,12 +2055,24 @@ version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +[[package]] +name = "heck" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" + [[package]] name = "hermit-abi" version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +[[package]] +name = "hermit-abi" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f154ce46856750ed433c8649605bf7ed2de3bc35fd9d2a9f30cddd873c80cb08" + [[package]] name = "hex" version = "0.4.3" @@ -1929,11 +2099,11 @@ dependencies = [ [[package]] name = "home" -version = "0.5.5" +version = "0.5.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5444c27eef6923071f7ebcc33e3444508466a76f7a2b93da00ed6e19f30c1ddb" +checksum = "589533453244b0995c858700322199b2becb13b627df2851f64a2775d024abcf" dependencies = [ - "windows-sys 0.48.0", + "windows-sys 0.59.0", ] [[package]] @@ -1952,9 +2122,9 @@ dependencies = [ [[package]] name = "http" -version = "0.2.12" +version = "1.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +checksum = "f4a85d31aea989eead29a3aaf9e1115a180df8282431156e533de47660892565" dependencies = [ "bytes", "fnv", @@ -1963,20 +2133,32 @@ dependencies = [ [[package]] name = "http-body" -version = "0.4.6" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", "http", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http", + "http-body", "pin-project-lite", ] [[package]] name = "httparse" -version = "1.8.0" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "httpdate" @@ -1995,13 +2177,12 @@ dependencies = [ [[package]] name = "hyper" -version = "0.14.28" +version = "1.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf96e135eb83a2a8ddf766e426a841d8ddd7449d5f00d34ea02b41d2f19eef80" +checksum = "cc2b571658e38e0c01b1fdca3bbbe93c00d3d71693ff2770043f8c29bc7d6f80" dependencies = [ "bytes", "futures-channel", - "futures-core", "futures-util", "h2", "http", @@ -2010,46 +2191,74 @@ dependencies = [ "httpdate", "itoa", "pin-project-lite", - "socket2 0.5.6", + "smallvec", "tokio", - "tower-service", - "tracing", "want", ] [[package]] name = "hyper-rustls" -version = "0.24.2" +version = "0.27.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +checksum = "03a01595e11bdcec50946522c32dde3fc6914743000a68b93000965f2f02406d" dependencies = [ - "futures-util", "http", "hyper", + "hyper-util", "rustls", + "rustls-pki-types", "tokio", "tokio-rustls", + "tower-service", + "webpki-roots", ] [[package]] name = "hyper-tls" -version = "0.5.0" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", + "http-body-util", "hyper", + "hyper-util", "native-tls", "tokio", "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c293b6b3d21eca78250dc7dbebd6b9210ec5530e038cbfe0661b5c47ab06e8" +dependencies = [ + "base64 0.22.1", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2 0.5.10", + "tokio", + "tower-service", + "tracing", ] [[package]] name = "iamb" -version = "0.0.10" +version = "0.0.11-alpha.1" dependencies = [ "anyhow", - "bitflags 2.5.0", + "bitflags 2.9.1", "chrono", "clap", "comrak", @@ -2057,13 +2266,15 @@ dependencies = [ "dirs", "edit", "emojis", + "feruca", "futures", "gethostname", "html5ever", "humansize", "image", - "lazy_static 1.4.0", + "lazy_static 1.5.0", "libc", + "linkify", "markup5ever_rcdom", "matrix-sdk", "mime", @@ -2074,7 +2285,7 @@ dependencies = [ "notify-rust", "open", "pretty_assertions", - "rand", + "rand 0.8.5", "ratatui", "ratatui-image", "regex", @@ -2083,30 +2294,31 @@ dependencies = [ "serde_json", "sled", "temp-dir", - "thiserror", + "thiserror 1.0.69", "tokio", "toml", "tracing", "tracing-appender", "tracing-subscriber", "unicode-segmentation", - "unicode-width", + "unicode-width 0.1.14", "url", "vergen", ] [[package]] name = "iana-time-zone" -version = "0.1.57" +version = "0.1.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fad5b825842d2b38bd206f3e81d6957625fd7f0a361e345c30e01a0ae2dd613" +checksum = "b0c919e5debc312ad217002b8048a17b7d83f80703865bbfcfebb0458b0b27d8" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", + "log", "wasm-bindgen", - "windows 0.48.0", + "windows-core 0.61.2", ] [[package]] @@ -2119,10 +2331,96 @@ dependencies = [ ] [[package]] -name = "icy_sixel" -version = "0.1.1" +name = "icu_collections" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1dc4d30216c3fc247730a4c6c74db2bd217a5454361ce24d70e504bda0cd345e" +checksum = "200072f5d0e3614556f94a9930d5dc3e0662a652823904c3a75dc3b0af7fee47" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locale_core" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde2700ccaed3872079a65fb1a78f6c0a36c91570f28755dda67bc8f7d9f00a" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_normalizer" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "436880e8e18df4d7bbc06d58432329d6458cc84531f7ac5f024e93deadb37979" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00210d6893afc98edb752b664b8890f0ef174c8adbb8d0be9710fa66fbbf72d3" + +[[package]] +name = "icu_properties" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "016c619c1eeb94efb86809b015c58f479963de65bdb6253345c1a1276f22e32b" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "potential_utf", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "298459143998310acd25ffe6810ed544932242d3f07083eee1084d83a71bd632" + +[[package]] +name = "icu_provider" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c80da27b5f4187909049ee2d72f276f0d9f99a42c306bd0131ecfe04d8e5af" +dependencies = [ + "displaydoc", + "icu_locale_core", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", +] + +[[package]] +name = "icy_sixel" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccc0a9c4770bc47b0a933256a496cfb8b6531f753ea9bccb19c6dff0ff7273fc" [[package]] name = "ident_case" @@ -2132,41 +2430,67 @@ checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" [[package]] name = "idna" -version = "0.5.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "634d9b1461af396cad843f47fdba5597a4f9e6ddd4bfb6ff5d85028c25cb12f6" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" dependencies = [ - "unicode-bidi", - "unicode-normalization", + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", ] [[package]] name = "image" -version = "0.24.9" +version = "0.25.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +checksum = "db35664ce6b9810857a38a906215e75a9c879f0696556a39f59c62829710251a" dependencies = [ "bytemuck", - "byteorder", + "byteorder-lite", "color_quant", "exr", "gif", - "jpeg-decoder", + "image-webp", "num-traits", "png", "qoi", + "ravif", + "rayon", + "rgb", "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b77d01e822461baa8409e156015a1d91735549f0f2c17691bd2d996bef238f7f" +dependencies = [ + "byteorder-lite", + "quick-error", ] [[package]] name = "imbl" -version = "2.0.3" +version = "4.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "978d142c8028edf52095703af2fad11d6f611af1246685725d6b850634647085" +checksum = "5ae128b3bc67ed43ec0a7bb1c337a9f026717628b3c4033f07ded1da3e854951" dependencies = [ "bitmaps", "imbl-sized-chunks", - "rand_core", + "rand_core 0.6.4", "rand_xoshiro", "serde", "version_check", @@ -2174,27 +2498,33 @@ dependencies = [ [[package]] name = "imbl-sized-chunks" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "144006fb58ed787dcae3f54575ff4349755b00ccc99f4b4873860b654be1ed63" +checksum = "8f4241005618a62f8d57b2febd02510fb96e0137304728543dfc5fd6f052c22d" dependencies = [ "bitmaps", ] [[package]] -name = "include_dir" -version = "0.7.3" +name = "imgref" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18762faeff7122e89e0857b02f7ce6fcc0d101d5e9ad2ad7846cc01d61b7f19e" +checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" + +[[package]] +name = "include_dir" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" dependencies = [ "include_dir_macros", ] [[package]] name = "include_dir_macros" -version = "0.7.3" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b139284b5cf57ecfa712bcc66950bb635b31aff41c188e8a4cfc758eca374a3f" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" dependencies = [ "proc-macro2", "quote", @@ -2202,9 +2532,9 @@ dependencies = [ [[package]] name = "indexed_db_futures" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6cc2083760572ee02385ab8b7c02c20925d2dd1f97a1a25a8737a238608f1152" +checksum = "43315957678a70eb21fb0d2384fe86dde0d6c859a01e24ce127eb65a0143d28c" dependencies = [ "accessory", "cfg-if", @@ -2219,41 +2549,62 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "cea70ddb795996207ad57735b50c5982d8844f38ba9ee5f1aedcfb708a2aa11e" dependencies = [ "equivalent", - "hashbrown", + "hashbrown 0.15.3", "serde", ] [[package]] name = "indoc" -version = "2.0.4" +version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e186cfbae8084e513daff4240b4797e342f988cecda4fb6c939150f96315fd8" +checksum = "f4c7245a08504955605670dbf141fceab975f15ca21570696aebe9d2e71576bd" [[package]] name = "inout" -version = "0.1.3" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +checksum = "879f10e63c20629ecabbb64a8010319738c66a5cd0c29b02d63d272b03751d01" dependencies = [ "block-padding", "generic-array", ] [[package]] -name = "instant" -version = "0.1.12" +name = "instability" +version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5bbe824c507c5da5956355e86a746d82e0e1464f65d862cc5e71da70e94b2c" +checksum = "0bf9fed6d91cfb734e7476a06bde8300a1b94e217e1b523b6f0cd1a01998c71d" +dependencies = [ + "darling 0.20.11", + "indoc", + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "instant" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" dependencies = [ "cfg-if", - "js-sys", - "wasm-bindgen", - "web-sys", +] + +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", ] [[package]] @@ -2271,26 +2622,36 @@ version = "1.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eae7b9aee968036d54dce06cebaefd919e4472e753296daccd6d344e3e2df0c2" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", "windows-sys 0.48.0", ] [[package]] name = "ipnet" -version = "2.9.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f518f335dce6725a761382244631d86cf0ccb2863413590b31338feb467f9c3" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" + +[[package]] +name = "iri-string" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc5ebe9c3a1a7a5127f920a418f7585e9e758e911d0466ed004f393b0e380b2" +dependencies = [ + "memchr", + "serde", +] [[package]] name = "is-terminal" -version = "0.4.12" +version = "0.4.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f23ff5ef2b80d608d61efee834934d862cd92461afc0560dedf493e4c033738b" +checksum = "e04d7f318608d35d4b61ddd75cbdaee86b023ebe2bd5a66ee0915f0bf93095a9" dependencies = [ - "hermit-abi", + "hermit-abi 0.5.1", "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -2312,26 +2673,52 @@ dependencies = [ ] [[package]] -name = "itoa" -version = "1.0.11" +name = "itertools" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a5f13b858c8d314ee3e8f639011f7ccefe71f97f96e50151fb991f267928e2c" + +[[package]] +name = "jobserver" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38f262f097c174adebe41eb73d66ae9c06b2844fb0da69969647bbddd9b0538a" +dependencies = [ + "getrandom 0.3.3", + "libc", +] [[package]] name = "jpeg-decoder" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" -dependencies = [ - "rayon", -] [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -2355,20 +2742,20 @@ dependencies = [ [[package]] name = "keybindings" -version = "0.0.1" +version = "0.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "680e4699c91c0622dd70da32c274881aadb1ac86252d738c3641266e90e4ca15" +checksum = "19a726307ed87e05155c31329676130e6a237e62dda80211f7e1ed811e47630f" dependencies = [ "textwrap", "unicode-segmentation", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] name = "konst" -version = "0.3.8" +version = "0.3.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8d712a8c49d4274f8d8a5cf61368cb5f3c143d149882b1a2918129e53395fdb0" +checksum = "4381b9b00c55f251f2ebe9473aef7c117e96828def1a7cb3bd3f0f903c6894e9" dependencies = [ "const_panic", "konst_kernel", @@ -2377,9 +2764,9 @@ dependencies = [ [[package]] name = "konst_kernel" -version = "0.3.8" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dac6ea8c376b6e208a81cf39b8e82bebf49652454d98a4829e907dac16ef1790" +checksum = "e4b1eb7788f3824c629b1116a7a9060d6e898c358ebff59070093d51103dcc3c" dependencies = [ "typewit", ] @@ -2392,9 +2779,9 @@ checksum = "cf186d1a8aa5f5bee5fd662bc9c1b949e0259e1bcc379d1f006847b0080c7417" [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "lebe" @@ -2404,25 +2791,25 @@ checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" [[package]] name = "libc" -version = "0.2.153" +version = "0.2.172" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd" +checksum = "d750af042f7ef4f724306de029d18836c26c1765a54a6a3f094cbd23a7267ffa" [[package]] -name = "libloading" -version = "0.8.3" +name = "libfuzzer-sys" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c2a198fb6b0eada2a8df47933734e6d35d350665a33a3593d7164fa52c75c19" +checksum = "cf78f52d400cf2d84a3a973a78a592b4adc535739e0a5597a0da6f0c357adc75" dependencies = [ - "cfg-if", - "windows-targets 0.52.5", + "arbitrary", + "cc", ] [[package]] name = "libm" -version = "0.2.8" +version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ec2a862134d2a7d32d7983ddcdd1c4923530833c9f2ea1a44fc5fa473989058" +checksum = "f9fbbcab51052fe104eb5e5d351cf728d30a5be1fe14d9be8a3b097481fb97de" [[package]] name = "libredox" @@ -2430,21 +2817,30 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.9.1", "libc", ] [[package]] name = "libsqlite3-sys" -version = "0.27.0" +version = "0.30.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf4e226dcd58b4be396f7bd3c20da8fdee2911400705297ba7d2d7cc2c30f716" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" dependencies = [ "cc", "pkg-config", "vcpkg", ] +[[package]] +name = "linkify" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dfa36d52c581e9ec783a7ce2a5e0143da6237be5811a0b3153fedfdbe9f780" +dependencies = [ + "memchr", +] + [[package]] name = "linux-raw-sys" version = "0.3.8" @@ -2453,15 +2849,27 @@ checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" [[package]] name = "linux-raw-sys" -version = "0.4.13" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01cda141df6706de531b6c46c3a33ecca755538219bd484262fa09410c13539c" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "linux-raw-sys" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" + +[[package]] +name = "litemap" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "241eaef5fd12c88705a01fc1066c48c4b36e0dd4377dcdc7ec3942cea7a69956" [[package]] name = "lock_api" -version = "0.4.11" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" +checksum = "96936507f153605bddfcda068dd804796c84324ed2510809e5b2a624c81da765" dependencies = [ "autocfg", "scopeguard", @@ -2469,19 +2877,34 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94" + +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] [[package]] name = "lru" -version = "0.12.3" +version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3262e75e648fce39813cb56ac41f3c3e3f65217ebf3844d818d1f9398cfb0dc" +checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" dependencies = [ - "hashbrown", + "hashbrown 0.15.3", ] +[[package]] +name = "lru-slab" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" + [[package]] name = "mac" version = "0.1.1" @@ -2490,14 +2913,13 @@ checksum = "c41e0c4fef86961ac6d6f8a82609f55f31b05e4fce149ac5710e439df7619ba4" [[package]] name = "mac-notification-sys" -version = "0.6.1" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51fca4d74ff9dbaac16a01b924bc3693fa2bba0862c2c633abc73f9a8ea21f64" +checksum = "0b95dfb34071d1592b45622bf93e315e3a72d414b6782aca9a015c12bec367ef" dependencies = [ "cc", - "dirs-next", - "objc-foundation", - "objc_id", + "objc2", + "objc2-foundation", "time", ] @@ -2521,7 +2943,7 @@ dependencies = [ "cfg-if", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", ] [[package]] @@ -2532,7 +2954,7 @@ checksum = "13198c120864097a565ccb3ff947672d969932b7975ebd4085732c9f09435e55" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", ] [[package]] @@ -2545,16 +2967,7 @@ dependencies = [ "macroific_core", "proc-macro2", "quote", - "syn 2.0.59", -] - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", + "syn 2.0.101", ] [[package]] @@ -2590,33 +3003,39 @@ dependencies = [ ] [[package]] -name = "matrix-pickle" -version = "0.1.1" +name = "matchit" +version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7fd26463ce5d86b8d9bb9c4142d453198ba22fb91bd46d3c9f144ae699d821d" +checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" + +[[package]] +name = "matrix-pickle" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e2551de3bba2cc65b52dc6b268df6114011fe118ac24870fbcf1b35537bd721" dependencies = [ "matrix-pickle-derive", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "matrix-pickle-derive" -version = "0.1.1" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93779aa78d39c2fe34746287b10a866192cf8af1b81767fff76bd64099acc0f5" +checksum = "f75de44c3120d78e978adbcf6d453b20ba011f3c46363e52d1dbbc72f545e9fb" dependencies = [ - "proc-macro-crate 2.0.0", - "proc-macro-error", + "proc-macro-crate 3.3.0", + "proc-macro-error2", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", ] [[package]] name = "matrix-sdk" -version = "0.7.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "336687e5fc8b33661a31681e988a67e9a3090c7fb1a8323a7f71eeaabad642ec" +checksum = "e27119e566a60f5681eb8d05f51ef10862dd9af611ac6c6e0dc9aa9bf3bcc493" dependencies = [ "anymap2", "aquamarine", @@ -2624,34 +3043,38 @@ dependencies = [ "async-channel", "async-stream", "async-trait", + "axum", "backoff", "bytes", "bytesize", - "cfg-vis", - "event-listener 4.0.3", + "event-listener 5.4.0", "eyeball", "eyeball-im", "futures-core", "futures-util", "gloo-timers", + "growable-bloom-filter", "http", - "hyper", "imbl", "indexmap", + "js_int", "matrix-sdk-base", "matrix-sdk-common", "matrix-sdk-indexeddb", "matrix-sdk-sqlite", "mime", "mime2ext", - "rand", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rand 0.8.5", "reqwest", "ruma", "serde", "serde_html_form", "serde_json", "tempfile", - "thiserror", + "thiserror 2.0.12", "tokio", "tokio-stream", "tokio-util", @@ -2659,48 +3082,54 @@ dependencies = [ "tracing", "url", "urlencoding", + "vodozemac", "zeroize", ] [[package]] name = "matrix-sdk-base" -version = "0.7.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00891954d0826a94f1d130f46cbca64176003a234c1be5d9d282970d31cf0c87" +checksum = "58884b338e0c2eb4aa09d63ba2a5937fb5bd691525884f09935900137fc6b908" dependencies = [ "as_variant", "async-trait", - "bitflags 2.5.0", + "bitflags 2.9.1", + "decancer", "eyeball", "eyeball-im", "futures-util", + "growable-bloom-filter", "matrix-sdk-common", "matrix-sdk-crypto", "matrix-sdk-store-encryption", "once_cell", + "regex", "ruma", "serde", "serde_json", - "thiserror", + "thiserror 2.0.12", "tokio", "tracing", + "unicode-normalization", ] [[package]] name = "matrix-sdk-common" -version = "0.7.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb365a626ab6f6c6a2422cfe2565522f19accb06706c6d04bca8f0f71df29c9f" +checksum = "072d77e461933834e12810d63906409f37a039acad31a16dda62b63e1f4c31cf" dependencies = [ "async-trait", + "eyeball-im", "futures-core", "futures-util", "gloo-timers", - "instant", + "imbl", "ruma", "serde", "serde_json", - "thiserror", + "thiserror 2.0.12", "tokio", "tracing", "tracing-subscriber", @@ -2711,16 +3140,16 @@ dependencies = [ [[package]] name = "matrix-sdk-crypto" -version = "0.7.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72aaeca3deb1387a63cd8c689270bd499b9eac3a594c2aaec72d7441ff00cd09" +checksum = "ed1ec9d645eb86630b2ed71e5890565ca023f569d9d0ebdcb25bfca8a088c2f3" dependencies = [ "aes", + "aquamarine", "as_variant", "async-trait", "bs58", "byteorder", - "cbc", "cfg-if", "ctr", "eyeball", @@ -2728,61 +3157,66 @@ dependencies = [ "futures-util", "hkdf", "hmac", - "itertools 0.12.1", + "itertools 0.14.0", + "js_option", "matrix-sdk-common", "pbkdf2", - "rand", + "rand 0.8.5", "rmp-serde", "ruma", "serde", "serde_json", "sha2", "subtle", - "thiserror", + "thiserror 2.0.12", + "time", "tokio", "tokio-stream", "tracing", "ulid", + "url", "vodozemac", "zeroize", ] [[package]] name = "matrix-sdk-indexeddb" -version = "0.7.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad388005c5d4ed2ff38f405d52aa7fa606f4e1ab51baf5f2504721124ed4a58b" +checksum = "da30f51dbfcd03297a04f49f92c365a41cb2b012ad3338c0fc5d4efafcbff88b" dependencies = [ "anyhow", "async-trait", - "base64", - "getrandom", + "base64 0.22.1", + "getrandom 0.2.16", "gloo-utils", + "hkdf", "indexed_db_futures", "js-sys", - "matrix-sdk-base", "matrix-sdk-crypto", "matrix-sdk-store-encryption", "ruma", "serde", "serde-wasm-bindgen", "serde_json", - "thiserror", + "sha2", + "thiserror 2.0.12", "tokio", "tracing", "wasm-bindgen", "web-sys", + "zeroize", ] [[package]] name = "matrix-sdk-sqlite" -version = "0.7.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20bd36bc5fa7ecd93516b242ba27466196d52b4a8743d85dd883a67bd6db11dc" +checksum = "74d07fb4e87c6ace1d05a87a91404acc3fd0b480ba9de75c08685ed18f1ea79f" dependencies = [ "async-trait", "deadpool-sqlite", - "itertools 0.12.1", + "itertools 0.14.0", "matrix-sdk-base", "matrix-sdk-crypto", "matrix-sdk-store-encryption", @@ -2791,7 +3225,7 @@ dependencies = [ "rusqlite", "serde", "serde_json", - "thiserror", + "thiserror 2.0.12", "tokio", "tracing", "vodozemac", @@ -2799,30 +3233,39 @@ dependencies = [ [[package]] name = "matrix-sdk-store-encryption" -version = "0.7.0" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a7e3162e9f982a4c57ab46df01a4775f697dec8899738bf62d7e97b63faa61c" +checksum = "dcc8b6650757f953664e5f906988690cef05c09d83081946adce446c45810a2d" dependencies = [ + "base64 0.22.1", "blake3", "chacha20poly1305", - "displaydoc", - "getrandom", "hmac", "pbkdf2", - "rand", + "rand 0.8.5", "rmp-serde", "serde", "serde_json", "sha2", - "thiserror", + "thiserror 2.0.12", "zeroize", ] [[package]] -name = "memchr" -version = "2.7.2" +name = "maybe-rayon" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8640c5d730cb13ebd907d8d04b52f55ac9a2eec55b440c8892f40d56c76c1d" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" [[package]] name = "memoffset" @@ -2850,15 +3293,15 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "mime2ext" -version = "0.1.52" +version = "0.1.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a85a5069ebd40e64b1985773cc81addbe9d90d7ecf60e7b5475a57ad584c70" +checksum = "cbf6f36070878c42c5233846cd3de24cf9016828fd47bc22957a687298bb21fc" [[package]] name = "mime_guess" -version = "2.0.4" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4192263c238a5f0d0c6bfd21f336a313a4ce1c450542449ca191bb657b4642ef" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" dependencies = [ "mime", "unicase", @@ -2872,53 +3315,54 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" [[package]] name = "miniz_oxide" -version = "0.7.2" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d811f3e15f28568be3407c8e7fdb6514c1cda3cb30683f15b6a1a1dc4ea14a7" +checksum = "3be647b768db090acb35d5ec5db2b0e1f1de11133ca123b9eacf5137868f892a" dependencies = [ - "adler", + "adler2", "simd-adler32", ] [[package]] name = "mio" -version = "0.8.11" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "78bed444cc8a2160f01cbcf811ef18cac863ad68ae8ca62092e8db51d51c761c" dependencies = [ "libc", "log", - "wasi", - "windows-sys 0.48.0", + "wasi 0.11.0+wasi-snapshot-preview1", + "windows-sys 0.59.0", ] [[package]] name = "modalkit" -version = "0.0.20" +version = "0.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24f070af2372cc39349d6415c9a2ae050e1e9dc71dddc9a7a036babe7ad2b192" +checksum = "6ed06f32b03a7504acadcb0d95d06f3d55258934c34b620ed95e3dae24f081a5" dependencies = [ "anymap2", "arboard", - "bitflags 2.5.0", + "bitflags 2.9.1", "crossterm", "derive_more", + "editor-types", "intervaltree", "keybindings", "nom", "radix_trie", "regex", "ropey", - "thiserror", + "thiserror 1.0.69", "unicode-segmentation", - "unicode-width", + "unicode-width 0.1.14", ] [[package]] name = "modalkit-ratatui" -version = "0.0.20" +version = "0.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df272b6822a22154034a9761ea10235746fefec7623dc1a6ed0cfbaad6f5d070" +checksum = "a33bd64f6dd0011ee88f4f12b28108d3e63df0a5c86fe595d24561be9522f6ea" dependencies = [ "crossterm", "intervaltree", @@ -2927,15 +3371,15 @@ dependencies = [ "ratatui", "regex", "serde", + "unicode-width 0.2.0", ] [[package]] name = "native-tls" -version = "0.2.11" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07226173c32f2926027b63cce4bcd8076c3552846cbe7925f3aaffeac0a3b92e" +checksum = "87de3442987e9dbec73158d5c715e7ad9072fda936bb03d19d7fa10e00520f0e" dependencies = [ - "lazy_static 1.4.0", "libc", "log", "openssl", @@ -2949,9 +3393,9 @@ dependencies = [ [[package]] name = "new_debug_unreachable" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e4a24736216ec316047a1fc4252e27dabb04218aa4a3f37c6e7ddbf1f9782b54" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" [[package]] name = "nibble_vec" @@ -2974,18 +3418,6 @@ dependencies = [ "memoffset 0.7.1", ] -[[package]] -name = "nix" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab2156c4fce2f8df6c499cc1c763e4394b7482525bf2a9701c9d79d215f519e4" -dependencies = [ - "bitflags 2.5.0", - "cfg-if", - "cfg_aliases", - "libc", -] - [[package]] name = "nom" version = "7.1.3" @@ -2996,6 +3428,12 @@ dependencies = [ "minimal-lexical", ] +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + [[package]] name = "notify-rust" version = "4.10.0" @@ -3019,6 +3457,16 @@ dependencies = [ "winapi", ] +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + [[package]] name = "num-conv" version = "0.1.0" @@ -3026,10 +3474,41 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" [[package]] -name = "num-traits" -version = "0.2.18" +name = "num-derive" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da0df0e5185db44f69b44f26786fe401b6c293d1907744beaa7fa62b2e5a517a" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] @@ -3040,7 +3519,7 @@ version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" dependencies = [ - "hermit-abi", + "hermit-abi 0.3.9", "libc", ] @@ -3054,48 +3533,94 @@ dependencies = [ ] [[package]] -name = "objc" -version = "0.2.7" +name = "objc2" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" +checksum = "88c6597e14493ab2e44ce58f2fdecf095a51f12ca57bec060a11c57332520551" dependencies = [ - "malloc_buf", + "objc2-encode", ] [[package]] -name = "objc-foundation" -version = "0.1.1" +name = "objc2-app-kit" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +checksum = "e6f29f568bec459b0ddff777cec4fe3fd8666d82d5a40ebd0ff7e66134f89bcc" dependencies = [ - "block", - "objc", - "objc_id", + "bitflags 2.9.1", + "objc2", + "objc2-core-graphics", + "objc2-foundation", ] [[package]] -name = "objc_id" -version = "0.1.1" +name = "objc2-core-foundation" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +checksum = "1c10c2894a6fed806ade6027bcd50662746363a9589d3ec9d9bef30a4e4bc166" dependencies = [ - "objc", + "bitflags 2.9.1", + "dispatch2", + "objc2", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "989c6c68c13021b5c2d6b71456ebb0f9dc78d752e86a98da7c716f4f9470f5a4" +dependencies = [ + "bitflags 2.9.1", + "dispatch2", + "objc2", + "objc2-core-foundation", + "objc2-io-surface", +] + +[[package]] +name = "objc2-encode" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-foundation" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "900831247d2fe1a09a683278e5384cfb8c80c79fe6b166f9d14bfdde0ea1b03c" +dependencies = [ + "bitflags 2.9.1", + "block2", + "libc", + "objc2", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7282e9ac92529fa3457ce90ebb15f4ecbc383e8338060960760fa2cf75420c3c" +dependencies = [ + "bitflags 2.9.1", + "objc2", + "objc2-core-foundation", ] [[package]] name = "object" -version = "0.32.2" +version = "0.36.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +checksum = "62948e14d923ea95ea2c7c86c71013138b66525b86bdc08d2dcc262bdb497b87" dependencies = [ "memchr", ] [[package]] name = "once_cell" -version = "1.19.0" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "opaque-debug" @@ -3115,13 +3640,13 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.64" +version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a0481286a310808298130d22dd1fef0fa571e05a8f44ec801801e84b216b1f" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.9.1", "cfg-if", - "foreign-types 0.3.2", + "foreign-types", "libc", "once_cell", "openssl-macros", @@ -3136,20 +3661,20 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", ] [[package]] name = "openssl-probe" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" [[package]] name = "openssl-sys" -version = "0.9.102" +version = "0.9.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c597637d56fbc83893a35eb0dd04b2b8e7a50c91e64e9493e398b5df4fb45fa2" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" dependencies = [ "cc", "libc", @@ -3169,12 +3694,12 @@ dependencies = [ [[package]] name = "os_pipe" -version = "1.1.5" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57119c3b893986491ec9aa85056780d3a0f3cf4da7cc09dd3650dbd6c6738fb9" +checksum = "db335f4760b14ead6290116f2427bf33a14d4f0617d49f78a246de10c1831224" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3185,9 +3710,9 @@ checksum = "b15813163c1d831bf4a13c3610c05c0d03b39feb07f7e09fa234dac9b15aaf39" [[package]] name = "parking" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb813b8af86854136c6922af0598d719255ecb2179515e6e7730d468f05c9cae" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" [[package]] name = "parking_lot" @@ -3202,12 +3727,12 @@ dependencies = [ [[package]] name = "parking_lot" -version = "0.12.1" +version = "0.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" +checksum = "70d58bf43669b5795d1576d0641cfb6fbb2057bf629506267a92807158584a13" dependencies = [ "lock_api", - "parking_lot_core 0.9.9", + "parking_lot_core 0.9.11", ] [[package]] @@ -3226,28 +3751,28 @@ dependencies = [ [[package]] name = "parking_lot_core" -version = "0.9.9" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" +checksum = "bc838d2a56b5b1a6c25f55575dfc605fabb63bb2365f6c2353ef9159aa69e4a5" dependencies = [ "cfg-if", "libc", - "redox_syscall 0.4.1", + "redox_syscall 0.5.12", "smallvec", - "windows-targets 0.48.5", + "windows-targets 0.52.6", ] [[package]] name = "paste" -version = "1.0.14" +version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de3145af08024dea9fa9914f381a17b8fc6034dfb00f3a84013f7ff43f29ed4c" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" [[package]] name = "pathdiff" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8835116a5c179084a830efb3adc117ab007512b535bc1a21c991d3b32a6b44dd" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" [[package]] name = "pbkdf2" @@ -3267,9 +3792,9 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "petgraph" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1d3afd2628e69da2be385eb6f2fd57c8ac7977ceeff6dc166ff1657b0e386a9" +checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db" dependencies = [ "fixedbitset", "indexmap", @@ -3286,11 +3811,11 @@ dependencies = [ [[package]] name = "phf" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ade2d8b8f33c7333b51bcf0428d37e217e9f32192ae4772156f65063b8ce03dc" +checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" dependencies = [ - "phf_shared 0.11.2", + "phf_shared 0.11.3", ] [[package]] @@ -3299,7 +3824,7 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd" dependencies = [ - "phf_generator", + "phf_generator 0.10.0", "phf_shared 0.10.0", ] @@ -3310,7 +3835,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6" dependencies = [ "phf_shared 0.10.0", - "rand", + "rand 0.8.5", +] + +[[package]] +name = "phf_generator" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" +dependencies = [ + "phf_shared 0.11.3", + "rand 0.8.5", ] [[package]] @@ -3319,23 +3854,23 @@ version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096" dependencies = [ - "siphasher", + "siphasher 0.3.11", ] [[package]] name = "phf_shared" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90fcb95eef784c2ac79119d1dd819e162b5da872ce6f3c3abe1e8ca1c082f72b" +checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "siphasher", + "siphasher 1.0.1", ] [[package]] name = "pin-project-lite" -version = "0.2.14" +version = "0.2.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bda66fc9667c18cb2758a2ac84d1167245054bcf85d5d1aaa6923f45801bdd02" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" [[package]] name = "pin-utils" @@ -3345,26 +3880,15 @@ checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] name = "piper" -version = "0.2.1" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "668d31b1c4eba19242f2088b2bf3316b82ca31082a8335764db4e083db7485d4" +checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" dependencies = [ "atomic-waker", - "fastrand 2.0.2", + "fastrand 2.3.0", "futures-io", ] -[[package]] -name = "pkcs7" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d79178be066405e0602bf3035946edef6b11b3f9dde46dfe5f8bfd7dea4b77e7" -dependencies = [ - "der", - "spki", - "x509-cert", -] - [[package]] name = "pkcs8" version = "0.10.2" @@ -3377,21 +3901,15 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.30" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231b230927b5e4ad203db57bbcbee2802f6bce620b1e4a9024a07d94e2907ec" - -[[package]] -name = "platforms" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23d408679286588f4d4644f965003d056e3dd5abcaaa938116871d7ce2fee7" +checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" [[package]] name = "png" -version = "0.17.13" +version = "0.17.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06e4b0d3d1312775e782c86c91a111aa1f910cbb65e1337f9975b5f9a554b5e1" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" dependencies = [ "bitflags 1.3.2", "crc32fast", @@ -3418,16 +3936,17 @@ dependencies = [ [[package]] name = "polling" -version = "3.5.0" +version = "3.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "24f040dee2588b4963afb4e420540439d126f73fdacf4a9c486a96d840bac3c9" +checksum = "b53a684391ad002dd6a596ceb6c74fd004fdce75f4be2e3f615068abbea5fd50" dependencies = [ "cfg-if", "concurrent-queue", + "hermit-abi 0.5.1", "pin-project-lite", - "rustix 0.38.32", + "rustix 1.0.7", "tracing", - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] [[package]] @@ -3441,6 +3960,15 @@ dependencies = [ "universal-hash", ] +[[package]] +name = "potential_utf" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5a7c30837279ca13e7c867e9e40053bc68740f988cb07f7ca6df43cc734b585" +dependencies = [ + "zerovec", +] + [[package]] name = "powerfmt" version = "0.2.0" @@ -3449,9 +3977,12 @@ checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" [[package]] name = "ppv-lite86" -version = "0.2.17" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] [[package]] name = "precomputed-hash" @@ -3461,9 +3992,9 @@ checksum = "925383efa346730478fb4838dbe9137d2a47675ad789c546d150a6e1dd4ab31c" [[package]] name = "pretty_assertions" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af7cee1a6c8a5b9208b3cb1061f10c0cb689087b3d8ce85fb9d2dd7a29b6ba66" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" dependencies = [ "diff", "yansi", @@ -3481,51 +4012,67 @@ dependencies = [ [[package]] name = "proc-macro-crate" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "edce586971a4dfaa28950c6f18ed55e0406c1ab88bbce2c6f6293a7aaba73d35" +dependencies = [ + "toml_edit 0.22.26", +] + +[[package]] +name = "proc-macro-error-attr2" version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" -dependencies = [ - "toml_edit 0.20.7", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" +checksum = "96de42df36bb9bba5542fe9f1a054b8cc87e172759a1868aa05c1f3acc89dfc5" dependencies = [ "proc-macro2", "quote", - "version_check", +] + +[[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", ] [[package]] name = "proc-macro2" -version = "1.0.81" +version = "1.0.95" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1597b0c024618f09a9c3b8655b7e430397a36d23fdafec26d6965e9eec3eba" +checksum = "02b3e5e68a3a1a02aad3ec490a98007cbc13c37cbe84a3cd7b8e406d76e7f778" dependencies = [ "unicode-ident", ] [[package]] -name = "prost" -version = "0.12.4" +name = "profiling" +version = "1.0.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f5d036824e4761737860779c906171497f6d55681139d8312388f8fe398922" +checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" +dependencies = [ + "quote", + "syn 2.0.101", +] + +[[package]] +name = "prost" +version = "0.13.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2796faa41db3ec313a31f7624d9286acf277b52de526150b7e69f3debf891ee5" dependencies = [ "bytes", "prost-derive", @@ -3533,15 +4080,15 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.12.4" +version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19de2de2a00075bf566bee3bd4db014b11587e84184d3f7a791bc17f1a8e9e48" +checksum = "8a56d757972c98b346a9b766e3f02746cde6dd1cd1d1d563472929fdd74bec4d" dependencies = [ "anyhow", - "itertools 0.12.1", + "itertools 0.14.0", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", ] [[package]] @@ -3553,6 +4100,12 @@ dependencies = [ "bytemuck", ] +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + [[package]] name = "quick-xml" version = "0.30.0" @@ -3564,22 +4117,83 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.31.0" +version = "0.37.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1004a344b30a54e2ee58d66a71b32d2db2feb0a31f9a2d302bf0536f15de2a33" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" dependencies = [ "memchr", ] [[package]] -name = "quote" -version = "1.0.36" +name = "quinn" +version = "0.11.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fa76aaf39101c457836aec0ce2316dbdc3ab723cdda1c6bd4e6ad4208acaca7" +checksum = "626214629cda6781b6dc1d316ba307189c85ba657213ce642d9c77670f8202c8" +dependencies = [ + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2 0.5.10", + "thiserror 2.0.12", + "tokio", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-proto" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49df843a9161c85bb8aae55f101bc0bac8bcafd637a620d9122fd7e0b2f7422e" +dependencies = [ + "bytes", + "getrandom 0.3.3", + "lru-slab", + "rand 0.9.1", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.12", + "tinyvec", + "tracing", + "web-time", +] + +[[package]] +name = "quinn-udp" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4e529991f949c5e25755532370b8af5d114acae52326361d68d47af64aa842" +dependencies = [ + "cfg_aliases", + "libc", + "once_cell", + "socket2 0.5.10", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "quote" +version = "1.0.40" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1885c039570dc00dcb4ff087a89e185fd56bae234ddc7f056a945bf36467248d" dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5" + [[package]] name = "radix_trie" version = "0.2.1" @@ -3597,8 +4211,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" dependencies = [ "libc", - "rand_chacha", - "rand_core", + "rand_chacha 0.3.1", + "rand_core 0.6.4", +] + +[[package]] +name = "rand" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fbfd9d094a40bf3ae768db9361049ace4c0e04a4fd6b359518bd7b73a73dd97" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", ] [[package]] @@ -3608,7 +4232,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" dependencies = [ "ppv-lite86", - "rand_core", + "rand_core 0.6.4", +] + +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", ] [[package]] @@ -3617,7 +4251,16 @@ version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" dependencies = [ - "getrandom", + "getrandom 0.2.16", +] + +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.3", ] [[package]] @@ -3626,43 +4269,95 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f97cdb2a36ed4183de61b2f824cc45c9f1037f28afe0a322e9fff4c108b5aaa" dependencies = [ - "rand_core", + "rand_core 0.6.4", ] [[package]] name = "ratatui" -version = "0.26.1" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bcb12f8fbf6c62614b0d56eb352af54f6a22410c3b079eb53ee93c7b97dd31d8" +checksum = "eabd94c2f37801c20583fc49dd5cd6b0ba68c716787c2dd6ed18571e1e63117b" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.9.1", "cassowary", "compact_str", "crossterm", "indoc", - "itertools 0.12.1", + "instability", + "itertools 0.13.0", "lru", "paste", - "stability", "strum", "unicode-segmentation", - "unicode-width", + "unicode-truncate", + "unicode-width 0.2.0", ] [[package]] name = "ratatui-image" -version = "1.0.0" +version = "8.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2264bdb808c89e8395480cfce32c197e75a3d6171063e913bca12e7919a333da" +checksum = "e8fe71c551c67f34e4fa49797227f614cd064b82855d7b72d424e40d08ec0542" dependencies = [ - "base64", - "dyn-clone", + "base64 0.21.7", "icy_sixel", "image", - "rand", + "rand 0.8.5", "ratatui", - "rustix 0.38.32", + "rustix 0.38.44", "serde", + "thiserror 1.0.69", + "windows 0.58.0", +] + +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools 0.12.1", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand 0.8.5", + "rand_chacha 0.3.1", + "simd_helpers", + "system-deps", + "thiserror 1.0.69", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6a5f31fcf7500f9401fea858ea4ab5525c99f2322cfcee732c0e6c74208c0c6" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", ] [[package]] @@ -3687,9 +4382,18 @@ dependencies = [ [[package]] name = "readlock" -version = "0.1.7" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7b323e7196daa571c8584de958be19e92941c41f845776fe06babfe8fa280a2" +checksum = "188bbae3aa4739bd264e9204da5919b2c91dd87dcce5049cf04bdf6aa17c5012" + +[[package]] +name = "readlock-tokio" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29b1800712c0d75de4b0bda5483d46eaf8df757b81df5ca2bde53d5ac2e2c5b2" +dependencies = [ + "tokio", +] [[package]] name = "redox_syscall" @@ -3702,29 +4406,29 @@ dependencies = [ [[package]] name = "redox_syscall" -version = "0.4.1" +version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" +checksum = "928fca9cf2aa042393a8325b9ead81d2f0df4cb12e1e24cef072922ccd99c5af" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.1", ] [[package]] name = "redox_users" -version = "0.4.5" +version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" dependencies = [ - "getrandom", + "getrandom 0.2.16", "libredox", - "thiserror", + "thiserror 1.0.69", ] [[package]] name = "regex" -version = "1.10.4" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" dependencies = [ "aho-corasick", "memchr", @@ -3734,9 +4438,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" dependencies = [ "aho-corasick", "memchr", @@ -3745,27 +4449,29 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.11.27" +version = "0.12.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" +checksum = "e98ff6b0dbbe4d5a37318f433d4fc82babd21631f194d370409ceb2e40b2f0b5" dependencies = [ - "base64", + "async-compression", + "base64 0.22.1", "bytes", - "encoding_rs", "futures-core", "futures-util", "h2", "http", "http-body", + "http-body-util", "hyper", "hyper-rustls", "hyper-tls", + "hyper-util", "ipnet", "js-sys", "log", @@ -3774,17 +4480,19 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", + "quinn", "rustls", - "rustls-pemfile", + "rustls-pki-types", "serde", "serde_json", "serde_urlencoded", "sync_wrapper", - "system-configuration", "tokio", "tokio-native-tls", "tokio-rustls", "tokio-util", + "tower", + "tower-http", "tower-service", "url", "wasm-bindgen", @@ -3792,29 +4500,33 @@ dependencies = [ "wasm-streams", "web-sys", "webpki-roots", - "winreg", ] [[package]] -name = "ring" -version = "0.17.8" +name = "rgb" +version = "0.8.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" + +[[package]] +name = "ring" +version = "0.17.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ "cc", "cfg-if", - "getrandom", + "getrandom 0.2.16", "libc", - "spin", "untrusted", "windows-sys 0.52.0", ] [[package]] name = "rmp" -version = "0.8.13" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bddb316f4b9cae1a3e89c02f1926d557d1142d0d2e684b038c11c1b77705229a" +checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" dependencies = [ "byteorder", "num-traits", @@ -3823,9 +4535,9 @@ dependencies = [ [[package]] name = "rmp-serde" -version = "1.2.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "938a142ab806f18b88a97b0dea523d39e0fd730a064b035726adcfc58a8a5188" +checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" dependencies = [ "byteorder", "rmp", @@ -3844,30 +4556,30 @@ dependencies = [ [[package]] name = "rpassword" -version = "7.2.0" +version = "7.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6678cf63ab3491898c0d021b493c94c9b221d91295294a2a5746eacbe5928322" +checksum = "66d4c8b64f049c6721ec8ccec37ddfc3d641c4a7fca57e8f2a89de509c73df39" dependencies = [ "libc", "rtoolbox", - "winapi", + "windows-sys 0.59.0", ] [[package]] name = "rtoolbox" -version = "0.0.1" +version = "0.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "034e22c514f5c0cb8a10ff341b9b048b5ceb21591f31c8f44c43b960f9b3524a" +checksum = "a7cc970b249fbe527d6e02e0a227762c9108b2f49d81094fe357ffc6d14d7f6f" dependencies = [ "libc", - "winapi", + "windows-sys 0.52.0", ] [[package]] name = "ruma" -version = "0.9.4" +version = "0.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2779c38df072964c63476259d9300efb07d0d1a7178c6469893636ce0c547a36" +checksum = "c64fdaae631940eda62844a8a3026aba2ba84c22588c888ebec44861ba4d0c18" dependencies = [ "assign", "js_int", @@ -3876,16 +4588,19 @@ dependencies = [ "ruma-common", "ruma-events", "ruma-federation-api", + "web-time", ] [[package]] name = "ruma-client-api" -version = "0.17.4" +version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "641837258fa214a70823477514954ef0f5d3bc6ae8e1d5d85081856a33103386" +checksum = "b9a89ac03a0f4451f946ed9aed6fdd16ef5a78a3a2849e87af4b2474a176b2fb" dependencies = [ + "as_variant", "assign", "bytes", + "date_header", "http", "js_int", "js_option", @@ -3895,44 +4610,49 @@ dependencies = [ "serde", "serde_html_form", "serde_json", + "thiserror 2.0.12", + "url", + "web-time", ] [[package]] name = "ruma-common" -version = "0.12.1" +version = "0.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bca4c33c50e47b4cdceeac71bdef0c04153b0e29aa992d9030ec14a62323e85" +checksum = "6b75da013b362664c3e161662902e5da3f77e990525681b59c6035bac27e87b4" dependencies = [ "as_variant", - "base64", + "base64 0.22.1", "bytes", "form_urlencoded", - "getrandom", + "getrandom 0.2.16", "http", "indexmap", "js-sys", "js_int", "konst", "percent-encoding", - "rand", + "rand 0.8.5", "regex", "ruma-identifiers-validation", "ruma-macros", "serde", "serde_html_form", "serde_json", - "thiserror", + "thiserror 2.0.12", + "time", "tracing", "url", "uuid", + "web-time", "wildmatch", ] [[package]] name = "ruma-events" -version = "0.27.11" +version = "0.30.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d20a52770e5a9fb30b7a1c14ba8b3dcf76dadc01674e58e40094f78e6bd5e3f1" +checksum = "6c100eb6c7691ef010f18d9af315f486fc4da621b7203c431e88352148e84551" dependencies = [ "as_variant", "indexmap", @@ -3945,19 +4665,22 @@ dependencies = [ "ruma-macros", "serde", "serde_json", - "thiserror", + "thiserror 2.0.12", "tracing", "url", + "web-time", "wildmatch", ] [[package]] name = "ruma-federation-api" -version = "0.8.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1901c1f27bc327652d58af2a130c73acef3198abeccd24cee97f7267fdf3fe7" +checksum = "373bc5a30b84574dfce3e75c33d79d6ba9843bf0eee1bf351f904eef9bea001a" dependencies = [ + "http", "js_int", + "mime", "ruma-common", "ruma-events", "serde", @@ -3966,37 +4689,37 @@ dependencies = [ [[package]] name = "ruma-identifiers-validation" -version = "0.9.3" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf8ad1259274f2f57c20901bd1cc5e4a8f23169d1c1d887b6338b02f058e9b41" +checksum = "6ad674b5e5368c53a2c90fde7dac7e30747004aaf7b1827b72874a25fc06d4d8" dependencies = [ "js_int", - "thiserror", + "thiserror 2.0.12", ] [[package]] name = "ruma-macros" -version = "0.12.0" +version = "0.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0280534a4b3e34416f883285fac4f9c408cd0b737890ae66f3e7a7056d14be80" +checksum = "c1182e83ee5cd10121974f163337b16af68a93eedfc7cdbdbd52307ac7e1d743" dependencies = [ - "once_cell", - "proc-macro-crate 2.0.0", + "cfg-if", + "proc-macro-crate 3.3.0", "proc-macro2", "quote", "ruma-identifiers-validation", "serde", - "syn 2.0.59", + "syn 2.0.101", "toml", ] [[package]] name = "rusqlite" -version = "0.30.0" +version = "0.32.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a78046161564f5e7cd9008aff3b2990b3850dc8e0349119b98e8f251e099f24d" +checksum = "7753b721174eb8ff87a9a0e799e2d7bc3749323e773db92e0984debb00019d6e" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.9.1", "fallible-iterator", "fallible-streaming-iterator", "hashlink", @@ -4006,24 +4729,30 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[package]] +name = "rustc-hash" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustc_version" -version = "0.4.0" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa0f585226d2e68097d4f95d113b15b83a82e819ab25717ec0590d9584ef366" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" dependencies = [ "semver", ] [[package]] name = "rustix" -version = "0.37.27" +version = "0.37.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" +checksum = "519165d378b97752ca44bbe15047d5d3409e875f39327546b42ac81d7e18c1b6" dependencies = [ "bitflags 1.3.2", "errno", @@ -4035,98 +4764,99 @@ dependencies = [ [[package]] name = "rustix" -version = "0.38.32" +version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65e04861e65f21776e67888bfbea442b3642beaa0138fdb1dd7a84a52dffdb89" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.9.1", "errno", "libc", - "linux-raw-sys 0.4.13", - "windows-sys 0.52.0", + "linux-raw-sys 0.4.15", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustix" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71e83d6afe7ff64890ec6b71d6a69bb8a610ab78ce364b3352876bb4c801266" +dependencies = [ + "bitflags 2.9.1", + "errno", + "libc", + "linux-raw-sys 0.9.4", + "windows-sys 0.59.0", ] [[package]] name = "rustls" -version = "0.21.10" +version = "0.23.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d5a6813c0759e4609cd494e8e725babae6a2ca7b62a5536a13daaec6fcb7ba" +checksum = "730944ca083c1c233a75c09f199e973ca499344a2b7ba9e755c457e86fb4a321" dependencies = [ - "log", + "once_cell", "ring", + "rustls-pki-types", "rustls-webpki", - "sct", + "subtle", + "zeroize", ] [[package]] -name = "rustls-pemfile" -version = "1.0.4" +name = "rustls-pki-types" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" +checksum = "229a4a4c221013e7e1f1a043678c5cc39fe5171437c88fb47151a21e6f5b5c79" dependencies = [ - "base64", + "web-time", + "zeroize", ] [[package]] name = "rustls-webpki" -version = "0.101.7" +version = "0.103.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" +checksum = "e4a72fe2bcf7a6ac6fd7d0b9e5cb68aeb7d4c0a0271730218b3e92d43b4eb435" dependencies = [ "ring", + "rustls-pki-types", "untrusted", ] [[package]] name = "rustversion" -version = "1.0.14" +version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ffc183a10b4478d04cbbbfc96d0873219d962dd5accaff2ffbd4ceb7df837f4" +checksum = "8a0d197bd2c9dc6e53b84da9556a69ba4cdfab8619eb41a8bd1cc2027a0f6b1d" [[package]] name = "ryu" -version = "1.0.17" +version = "1.0.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e86697c916019a8588c99b5fac3cead74ec0b4b819707a682fd4d23fa0ce1ba1" +checksum = "28d3b2b1366ec20994f1fd18c3c594f05c5dd4bc44d8bb0c1c632c8d6829481f" [[package]] name = "schannel" -version = "0.1.23" +version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbc91545643bcf3a0bbb6569265615222618bdf33ce4ffbbd13c4bbd4c093534" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" dependencies = [ - "windows-sys 0.52.0", + "windows-sys 0.59.0", ] -[[package]] -name = "scoped-tls" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" - [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "security-framework" -version = "2.10.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "770452e37cad93e0a50d5abc3990d2bc351c36d0328f86cefec2f2fb206eaef6" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 1.3.2", + "bitflags 2.9.1", "core-foundation", "core-foundation-sys", "libc", @@ -4135,9 +4865,9 @@ dependencies = [ [[package]] name = "security-framework-sys" -version = "2.10.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41f3cc463c0ef97e11c3461a9d3787412d30e8e7eb907c79180c4a57bf7c04ef" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" dependencies = [ "core-foundation-sys", "libc", @@ -4145,15 +4875,15 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.22" +version = "1.0.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92d43fe69e652f3df9bdc2b85b2854a0825b86e4fb76bc44d945137d053639ca" +checksum = "56e6fa9c48d24d85fb3de5ad847117517440f6beceb7798af16b4a87d616b8d0" [[package]] name = "serde" -version = "1.0.198" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9846a40c979031340571da2545a4e5b7c4163bdae79b301d5f86d03979451fcc" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] @@ -4171,29 +4901,29 @@ dependencies = [ [[package]] name = "serde_bytes" -version = "0.11.14" +version = "0.11.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +checksum = "8437fd221bde2d4ca316d61b90e337e9e702b3820b87d63caa9ba6c02bd06d96" dependencies = [ "serde", ] [[package]] name = "serde_derive" -version = "1.0.198" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e88edab869b01783ba905e7d0153f9fc1a6505a96e4ad3018011eedb838566d9" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", ] [[package]] name = "serde_html_form" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de514ef58196f1fc96dcaef80fe6170a1ce6215df9687a93fe8300e773fefc5" +checksum = "9d2de91cf02bbc07cde38891769ccd5d4f073d22a40683aa4bc7a95781aaa2c4" dependencies = [ "form_urlencoded", "indexmap", @@ -4204,31 +4934,42 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.116" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e17db7126d17feb94eb3fad46bf1a96b034e8aacbc2e775fe81505f8b0b2813" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa", + "memchr", "ryu", "serde", ] [[package]] -name = "serde_repr" -version = "0.1.18" +name = "serde_path_to_error" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b2e6b945e9d3df726b65d6ee24060aff8e3533d431f677a9695db04eff9dfdb" +checksum = "59fab13f937fa393d08645bf3a84bdfe86e296747b506ada67bb15f10f218b2a" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", ] [[package]] name = "serde_spanned" -version = "0.6.5" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" dependencies = [ "serde", ] @@ -4258,9 +4999,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -4273,14 +5014,20 @@ version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" dependencies = [ - "lazy_static 1.4.0", + "lazy_static 1.5.0", ] [[package]] -name = "signal-hook" -version = "0.3.17" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "signal-hook" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d881a16cf4426aa584979d30bd82cb33429027e42122b169753d6ef1085ed6e2" dependencies = [ "libc", "signal-hook-registry", @@ -4288,9 +5035,9 @@ dependencies = [ [[package]] name = "signal-hook-mio" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ad2e15f37ec9a6cc544097b78a1ec90001e9f71b81338ca39f430adaca99af" +checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd" dependencies = [ "libc", "mio", @@ -4299,9 +5046,9 @@ dependencies = [ [[package]] name = "signal-hook-registry" -version = "1.4.1" +version = "1.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" +checksum = "9203b8055f63a2a00e2f593bb0510367fe707d7ff1e5c872de2f537b339e5410" dependencies = [ "libc", ] @@ -4312,7 +5059,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "rand_core", + "rand_core 0.6.4", ] [[package]] @@ -4321,12 +5068,27 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + [[package]] name = "siphasher" version = "0.3.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" +[[package]] +name = "siphasher" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56199f7ddabf13fe5074ce809e7d3f42b42ae711800501b5b16ea82ad029c39d" + [[package]] name = "slab" version = "0.4.9" @@ -4354,9 +5116,9 @@ dependencies = [ [[package]] name = "slug" -version = "0.1.5" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bd94acec9c8da640005f8e135a39fc0372e74535e6b368b7a04b875f784c8c4" +checksum = "882a80f72ee45de3cc9a5afeb2da0331d58df69e4e7d8eeb5d3c7784ae67e724" dependencies = [ "deunicode", "wasm-bindgen", @@ -4364,9 +5126,9 @@ dependencies = [ [[package]] name = "smallvec" -version = "1.13.2" +version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" +checksum = "8917285742e9f3e1683f0a9c4e6b57960b7314d0b08d30d1ecd426713ee2eee9" [[package]] name = "smawk" @@ -4386,23 +5148,14 @@ dependencies = [ [[package]] name = "socket2" -version = "0.5.6" +version = "0.5.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05ffd9c0a93b7543e062e759284fcf5f5e3b098501104bfbdde4d404db792871" +checksum = "e22376abed350d73dd1cd119b57ffccad95b4e585a7cda43e286245ce23c0678" dependencies = [ "libc", "windows-sys 0.52.0", ] -[[package]] -name = "spin" -version = "0.9.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" -dependencies = [ - "lock_api", -] - [[package]] name = "spki" version = "0.7.3" @@ -4414,14 +5167,10 @@ dependencies = [ ] [[package]] -name = "stability" -version = "0.1.1" +name = "stable_deref_trait" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebd1b177894da2a2d9120208c3386066af06a488255caabc5de8ddca22dbc3ce" -dependencies = [ - "quote", - "syn 1.0.109", -] +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" [[package]] name = "static_assertions" @@ -4431,32 +5180,31 @@ checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" [[package]] name = "str_indices" -version = "0.4.3" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9557cb6521e8d009c51a8666f09356f4b817ba9ba0981a305bd86aee47bd35c" +checksum = "d08889ec5408683408db66ad89e0e1f93dff55c73a4ccc71c427d5b277ee47e6" [[package]] name = "string_cache" -version = "0.8.7" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f91138e76242f575eb1d3b38b4f1362f10d3a43f47d182a5b359af488a02293b" +checksum = "bf776ba3fa74f83bf4b63c3dcbbf82173db2632ed8452cb2d891d33f459de70f" dependencies = [ "new_debug_unreachable", - "once_cell", - "parking_lot 0.12.1", - "phf_shared 0.10.0", + "parking_lot 0.12.4", + "phf_shared 0.11.3", "precomputed-hash", "serde", ] [[package]] name = "string_cache_codegen" -version = "0.5.2" +version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988" +checksum = "c711928715f1fe0fe509c53b43e993a9a557babc2d0a3567d0a3006f1ac931a0" dependencies = [ - "phf_generator", - "phf_shared 0.10.0", + "phf_generator 0.11.3", + "phf_shared 0.11.3", "proc-macro2", "quote", ] @@ -4468,32 +5216,38 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" [[package]] -name = "strum" -version = "0.26.2" +name = "strsim" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d8cec3501a5194c432b2b7976db6b7d10ec95c253208b45f83f7136aa985e29" +checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" + +[[package]] +name = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.26.2" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6cf59daf282c0a494ba14fd21610a0325f9f90ec9d1231dea26bcb1d696c946" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" dependencies = [ - "heck", + "heck 0.5.0", "proc-macro2", "quote", "rustversion", - "syn 2.0.59", + "syn 2.0.101", ] [[package]] name = "subtle" -version = "2.5.0" +version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81cdd64d312baedb58e21336b31bc043b77e01cc99033ce76ef539f78e965ebc" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" [[package]] name = "syn" @@ -4508,9 +5262,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.59" +version = "2.0.101" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4a6531ffc7b071655e4ce2e04bd464c4830bb585a61cabb96cf808f05172615a" +checksum = "8ce2b7fc941b3a24138a0a7cf8e858bfc6a992e7978a068a5c760deb0ed43caf" dependencies = [ "proc-macro2", "quote", @@ -4519,31 +5273,43 @@ dependencies = [ [[package]] name = "sync_wrapper" -version = "0.1.2" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" - -[[package]] -name = "system-configuration" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ - "bitflags 1.3.2", - "core-foundation", - "system-configuration-sys", + "futures-core", ] [[package]] -name = "system-configuration-sys" -version = "0.5.0" +name = "synstructure" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ - "core-foundation-sys", - "libc", + "proc-macro2", + "quote", + "syn 2.0.101", ] +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck 0.5.0", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "target-lexicon" +version = "0.12.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" + [[package]] name = "tauri-winrt-notification" version = "0.1.3" @@ -4556,20 +5322,21 @@ dependencies = [ [[package]] name = "temp-dir" -version = "0.1.12" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd16aa9ffe15fe021c6ee3766772132c6e98dfa395a167e16864f61a9cfb71d6" +checksum = "83176759e9416cf81ee66cb6508dbfe9c96f20b8b56265a39917551c23c70964" [[package]] name = "tempfile" -version = "3.10.1" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "e8a64e3985349f2441a1a9ef0b853f869006c3855f2cda6862a94d26ebb9d6a1" dependencies = [ - "cfg-if", - "fastrand 2.0.2", - "rustix 0.38.32", - "windows-sys 0.52.0", + "fastrand 2.3.0", + "getrandom 0.3.3", + "once_cell", + "rustix 1.0.7", + "windows-sys 0.59.0", ] [[package]] @@ -4585,33 +5352,53 @@ dependencies = [ [[package]] name = "textwrap" -version = "0.16.1" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23d434d3f8967a09480fb04132ebe0a3e088c173e6d0ee7897abbdf4eab0f8b9" +checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" dependencies = [ "smawk", "unicode-linebreak", - "unicode-width", + "unicode-width 0.2.0", ] [[package]] name = "thiserror" -version = "1.0.58" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03468839009160513471e86a034bb2c5c0e4baae3b43f79ffc55c4a5427b3297" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "thiserror-impl", + "thiserror-impl 1.0.69", +] + +[[package]] +name = "thiserror" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "567b8a2dae586314f7be2a752ec7474332959c6460e02bde30d702a66d488708" +dependencies = [ + "thiserror-impl 2.0.12", ] [[package]] name = "thiserror-impl" -version = "1.0.58" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61f3ba182994efc43764a46c018c347bc492c79f024e705f46567b418f6d4f7" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f7cf42b4507d8ea322120659672cf1b9dbb93f8f2d4ecfd6e51350ff5b17a1d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", ] [[package]] @@ -4637,9 +5424,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.36" +version = "0.3.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885" +checksum = "8a7619e19bc266e0f9c5e6686659d394bc57973859340060a69221e57dbc0c40" dependencies = [ "deranged", "itoa", @@ -4654,25 +5441,35 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "c9e9a38711f559d9e3ce1cdb06dd7c5b8ea546bc90052da6d06bb76da74bb07c" [[package]] name = "time-macros" -version = "0.2.18" +version = "0.2.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f252a68540fde3a3877aeea552b832b40ab9a69e318efd078774a01ddee1ccf" +checksum = "3526739392ec93fd8b359c8e98514cb3e8e021beb4e5f597b00a0221f8ed8a49" dependencies = [ "num-conv", "time-core", ] [[package]] -name = "tinyvec" -version = "1.6.0" +name = "tinystr" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87cc5ceb3875bb20c2890005a4e226a4651264a5c75edb2421b52861a0a0cb50" +checksum = "5d4f6d1145dcb577acf783d4e601bc1d76a13337bb54e6233add580b07344c8b" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tinyvec" +version = "1.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09b3661f17e86524eccd4371ab0429194e0d7c008abb45f7a7495b1719463c71" dependencies = [ "tinyvec_macros", ] @@ -4685,30 +5482,29 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.37.0" +version = "1.45.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "75ef51a33ef1da925cea3e4eb122833cb377c61439ca401b770f54902b806779" dependencies = [ "backtrace", "bytes", "libc", "mio", - "num_cpus", "pin-project-lite", - "socket2 0.5.6", + "socket2 0.5.10", "tokio-macros", - "windows-sys 0.48.0", + "windows-sys 0.52.0", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", ] [[package]] @@ -4723,9 +5519,9 @@ dependencies = [ [[package]] name = "tokio-rustls" -version = "0.24.1" +version = "0.26.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" +checksum = "8e727b36a1a0e8b74c376ac2211e40c2c8af09fb4013c60d910495810f008e9b" dependencies = [ "rustls", "tokio", @@ -4733,9 +5529,9 @@ dependencies = [ [[package]] name = "tokio-stream" -version = "0.1.15" +version = "0.1.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267ac89e0bec6e691e5813911606935d77c476ff49024f98abcea3e7b15e37af" +checksum = "eca58d7bba4a75707817a2c44174253f9236b2d5fbd055602e9d5c07c139a047" dependencies = [ "futures-core", "pin-project-lite", @@ -4745,35 +5541,34 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.10" +version = "0.7.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" +checksum = "66a539a9ad6d5d281510d5bd368c973d636c02dbf8a67300bfb6b950696ad7df" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", - "tracing", ] [[package]] name = "toml" -version = "0.8.12" +version = "0.8.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9dd1545e8208b4a5af1aa9bbd0b4cf7e9ea08fabc5d0a5c67fcaafa17433aa3" +checksum = "05ae329d1f08c4d17a59bed7ff5b5a769d062e64a62d34a3261b219e62cd5aae" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.22.9", + "toml_edit 0.22.26", ] [[package]] name = "toml_datetime" -version = "0.6.5" +version = "0.6.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +checksum = "3da5db5a963e24bc68be8b17b6fa82814bb22ee8660f192bb182771d498f09a3" dependencies = [ "serde", ] @@ -4791,36 +5586,34 @@ dependencies = [ [[package]] name = "toml_edit" -version = "0.20.7" +version = "0.22.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "70f427fce4d84c72b5b732388bf4a9f4531b53f74e2887e3ecb2481f68f66d81" -dependencies = [ - "indexmap", - "toml_datetime", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.22.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e40bb779c5187258fd7aad0eb68cb8706a0a81fa712fbea808ab43c4b8374c4" +checksum = "310068873db2c5b3e7659d2cc35d21855dbafa50d1ce336397c666e3cb08137e" dependencies = [ "indexmap", "serde", "serde_spanned", "toml_datetime", - "winnow 0.6.6", + "toml_write", + "winnow 0.7.10", ] [[package]] -name = "tower" -version = "0.4.13" +name = "toml_write" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "bfb942dfe1d8e29a7ee7fcbde5bd2b9a25fb89aa70caea2eba3bee836ff41076" + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ + "futures-core", "futures-util", "pin-project-lite", + "sync_wrapper", "tokio", "tower-layer", "tower-service", @@ -4828,22 +5621,40 @@ dependencies = [ ] [[package]] -name = "tower-layer" -version = "0.3.2" +name = "tower-http" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "0fdb0c213ca27a9f57ab69ddb290fd80d970922355b83ae380b395d3986b8a2e" +dependencies = [ + "bitflags 2.9.1", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", +] + +[[package]] +name = "tower-layer" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.40" +version = "0.1.41" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" dependencies = [ "log", "pin-project-lite", @@ -4858,27 +5669,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3566e8ce28cc0a3fe42519fc80e6b4c943cc4c8cef275620eb8dac2d3d4e06cf" dependencies = [ "crossbeam-channel", - "thiserror", + "thiserror 1.0.69", "time", "tracing-subscriber", ] [[package]] name = "tracing-attributes" -version = "0.1.27" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", ] [[package]] name = "tracing-core" -version = "0.1.32" +version = "0.1.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" dependencies = [ "once_cell", "valuable", @@ -4897,9 +5708,9 @@ dependencies = [ [[package]] name = "tracing-subscriber" -version = "0.3.18" +version = "0.3.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad0f048c97dbd9faa9b7df56362b8ebcaa52adb06b498c050d2f4e32f90a7a8b" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" dependencies = [ "nu-ansi-term", "sharded-slab", @@ -4911,12 +5722,11 @@ dependencies = [ [[package]] name = "tree_magic_mini" -version = "3.1.4" +version = "3.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ee137597cdb361b55a4746983e4ac1b35ab6024396a419944ad473bb915265" +checksum = "aac5e8971f245c3389a5a76e648bfc80803ae066a1243a75db0064d7c1129d63" dependencies = [ "fnv", - "home", "memchr", "nom", "once_cell", @@ -4937,15 +5747,15 @@ checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a" [[package]] name = "typenum" -version = "1.17.0" +version = "1.18.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" [[package]] name = "typewit" -version = "1.9.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fb9ae6a3cafaf0a5d14c2302ca525f9ae8e07a0f0e6949de88d882c37a6e24" +checksum = "cb77c29baba9e4d3a6182d51fa75e3215c7fd1dab8f4ea9d107c716878e55fc0" dependencies = [ "typewit_proc_macros", ] @@ -4969,35 +5779,31 @@ dependencies = [ [[package]] name = "ulid" -version = "1.1.2" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34778c17965aa2a08913b57e1f34db9b4a63f5de31768b55bf20d2795f921259" +checksum = "470dbf6591da1b39d43c14523b2b469c86879a53e8b758c8e090a470fe7b1fbe" dependencies = [ - "getrandom", - "rand", + "rand 0.9.1", "web-time", ] [[package]] name = "unicase" -version = "2.7.0" +version = "2.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7d2d4dafb69621809a81864c9c1b864479e1235c0dd4e199924b9742439ed89" -dependencies = [ - "version_check", -] +checksum = "75b844d17643ee918803943289730bec8aac480150456169e647ed0b576ba539" [[package]] -name = "unicode-bidi" -version = "0.3.15" +name = "unicode-canonical-combining-class" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08f95100a766bf4f8f28f90d77e0a5461bbdb219042e7679bebe79004fed8d75" +checksum = "41c99d5174052d02ce765418e826597a1be18f32c114e35d9e22f92390239561" [[package]] name = "unicode-ident" -version = "1.0.12" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "5a5f39404a5da50712a4c1eecf25e90dd62b613502b7e925fd4e4d19b5c96512" [[package]] name = "unicode-linebreak" @@ -5007,24 +5813,41 @@ checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" [[package]] name = "unicode-normalization" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a56d1686db2308d901306f92a263857ef59ea39678a5458e7cb17f01415101f5" +checksum = "5033c97c4262335cded6d6fc3e5c18ab755e1a3dc96376350f3d8e9f009ad956" dependencies = [ "tinyvec", ] [[package]] name = "unicode-segmentation" -version = "1.11.0" +version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[package]] +name = "unicode-truncate" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3644627a5af5fa321c95b9b235a72fd24cd29c648c2c379431e6628655627bf" +dependencies = [ + "itertools 0.13.0", + "unicode-segmentation", + "unicode-width 0.1.14", +] [[package]] name = "unicode-width" -version = "0.1.11" +version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85" +checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" + +[[package]] +name = "unicode-width" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fc81956842c57dac11422a97c3b8195a1ff727f06e85c84ed2e8aa277c9a0fd" [[package]] name = "unicode_categories" @@ -5050,9 +5873,9 @@ checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] name = "url" -version = "2.5.0" +version = "2.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "31e6302e3bb753d46e83516cae55ae196fc0c309407cf11ab35cc51a4c2a4633" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" dependencies = [ "form_urlencoded", "idna", @@ -5073,26 +5896,44 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] -name = "utf8parse" -version = "0.2.1" +name = "utf8_iter" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.6.1" +version = "1.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e395fcf16a7a3d8127ec99782007af141946b4795001f876d54fb0d55978560" +checksum = "3cf4199d1e5d15ddd86a694e4d0dffa9c323ce759fea589f00fef9d81cc1931d" dependencies = [ - "getrandom", + "getrandom 0.3.3", + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "v_frame" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits", "wasm-bindgen", ] [[package]] name = "valuable" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" [[package]] name = "vcpkg" @@ -5102,55 +5943,63 @@ checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" [[package]] name = "vergen" -version = "8.2.5" +version = "8.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85e7dc29b3c54a2ea67ef4f953d5ec0c4085035c0ae2d325be1c0d2144bd9f16" +checksum = "2990d9ea5967266ea0ccf413a4aa5c42a93dbcfda9cb49a97de6931726b12566" dependencies = [ "anyhow", + "cfg-if", "rustversion", "time", ] [[package]] -name = "version_check" -version = "0.9.4" +name = "version-compare" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] name = "vodozemac" -version = "0.5.1" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2790dffeecc522299d72d9a855c43adb0c23ba1dc1112d79a651fdf3beb2a356" +checksum = "c022a277687e4e8685d72b95a7ca3ccfec907daa946678e715f8badaa650883d" dependencies = [ "aes", "arrayvec", - "base64", + "base64 0.22.1", + "base64ct", "cbc", + "chacha20poly1305", "curve25519-dalek", "ed25519-dalek", - "getrandom", + "getrandom 0.2.16", "hkdf", "hmac", "matrix-pickle", - "pkcs7", "prost", - "rand", + "rand 0.8.5", "serde", "serde_bytes", "serde_json", "sha2", "subtle", - "thiserror", + "thiserror 2.0.12", "x25519-dalek", "zeroize", ] [[package]] name = "waker-fn" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f3c4517f54858c779bbcbf228f4fca63d121bf85fbecb2dc578cdf4a39395690" +checksum = "317211a0dc0ceedd78fb2ca9a44aed3d7b9b26f81870d485c07122b4350673b7" [[package]] name = "want" @@ -5168,47 +6017,58 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" [[package]] -name = "wasm-bindgen" -version = "0.2.92" +name = "wasi" +version = "0.14.2+wasi-0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3" +dependencies = [ + "wit-bindgen-rt", +] + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", + "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-futures" -version = "0.4.42" +version = "0.4.50" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76bc14366121efc8dbb487ab05bcc9d346b3b5ec0eaa76e46594cabbe51762c0" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" dependencies = [ "cfg-if", "js-sys", + "once_cell", "wasm-bindgen", "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -5216,28 +6076,31 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", "wasm-bindgen-backend", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-streams" -version = "0.4.0" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b65dc4c90b63b118468cf747d8bf3566c1913ef60be765b5730ead9e0a3ba129" +checksum = "15053d8d85c7eccdbefef60f06769760a563c7f0a9d6902a13d35c7800b0ad65" dependencies = [ "futures-util", "js-sys", @@ -5248,37 +6111,36 @@ dependencies = [ [[package]] name = "wayland-backend" -version = "0.3.3" +version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d50fa61ce90d76474c87f5fc002828d81b32677340112b4ef08079a9d459a40" +checksum = "fe770181423e5fc79d3e2a7f4410b7799d5aab1de4372853de3c6aa13ca24121" dependencies = [ "cc", "downcast-rs", - "rustix 0.38.32", - "scoped-tls", + "rustix 0.38.44", "smallvec", "wayland-sys", ] [[package]] name = "wayland-client" -version = "0.31.2" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82fb96ee935c2cea6668ccb470fb7771f6215d1691746c2d896b447a00ad3f1f" +checksum = "978fa7c67b0847dbd6a9f350ca2569174974cd4082737054dbb7fbb79d7d9a61" dependencies = [ - "bitflags 2.5.0", - "rustix 0.38.32", + "bitflags 2.9.1", + "rustix 0.38.44", "wayland-backend", "wayland-scanner", ] [[package]] name = "wayland-protocols" -version = "0.31.2" +version = "0.32.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4" +checksum = "779075454e1e9a521794fed15886323ea0feda3f8b0fc1390f5398141310422a" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.9.1", "wayland-backend", "wayland-client", "wayland-scanner", @@ -5286,11 +6148,11 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.2.0" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6" +checksum = "1cb6cdc73399c0e06504c437fe3cf886f25568dd5454473d565085b36d6a8bbf" dependencies = [ - "bitflags 2.5.0", + "bitflags 2.9.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -5299,31 +6161,29 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.1" +version = "0.31.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "63b3a62929287001986fb58c789dce9b67604a397c15c611ad9f747300b6c283" +checksum = "896fdafd5d28145fce7958917d69f2fd44469b1d4e861cb5961bcbeebc6d1484" dependencies = [ "proc-macro2", - "quick-xml 0.31.0", + "quick-xml 0.37.5", "quote", ] [[package]] name = "wayland-sys" -version = "0.31.1" +version = "0.31.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15a0c8eaff5216d07f226cb7a549159267f3467b289d9a2e52fd3ef5aae2b7af" +checksum = "dbcebb399c77d5aa9fa5db874806ee7b4eba4e73650948e8f93963f128896615" dependencies = [ - "dlib", - "log", "pkg-config", ] [[package]] name = "web-sys" -version = "0.3.69" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77afa9a11836342370f4817622a2f0f418b134426d91a82dfb48f532d2ec13ef" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" dependencies = [ "js-sys", "wasm-bindgen", @@ -5341,15 +6201,18 @@ dependencies = [ [[package]] name = "webpki-roots" -version = "0.25.4" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f20c57d8d7db6d3b86154206ae5d8fba62dd39573114de97c2cb0578251f8e1" +checksum = "2853738d1cc4f2da3a225c18ec6c3721abb31961096e9dbf5ab35fa88b19cfdb" +dependencies = [ + "rustls-pki-types", +] [[package]] name = "weezl" -version = "0.1.8" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" +checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" [[package]] name = "which" @@ -5360,14 +6223,14 @@ dependencies = [ "either", "home", "once_cell", - "rustix 0.38.32", + "rustix 0.38.44", ] [[package]] name = "wildmatch" -version = "2.3.3" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "939e59c1bc731542357fdaad98b209ef78c8743d652bb61439d16b16a79eb025" +checksum = "68ce1ab1f8c62655ebe1350f589c61e505cf94d385bc6a12899442d9081e71fd" [[package]] name = "winapi" @@ -5393,21 +6256,22 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "windows" -version = "0.48.0" +version = "0.51.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e686886bc078bc1b0b600cac0147aadb815089b6e4da64016cbd754b6342700f" +checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" dependencies = [ + "windows-core 0.51.1", "windows-targets 0.48.5", ] [[package]] name = "windows" -version = "0.51.1" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca229916c5ee38c2f2bc1e9d8f04df975b4bd93f9955dc69fabb5d91270045c9" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ - "windows-core", - "windows-targets 0.48.5", + "windows-core 0.58.0", + "windows-targets 0.52.6", ] [[package]] @@ -5419,6 +6283,119 @@ dependencies = [ "windows-targets 0.48.5", ] +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings 0.1.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" +dependencies = [ + "windows-implement 0.60.0", + "windows-interface 0.59.1", + "windows-link", + "windows-result 0.3.4", + "windows-strings 0.4.2", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-implement" +version = "0.60.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a47fddd13af08290e67f4acabf4b459f647552718f683a7b415d290ac744a836" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-interface" +version = "0.59.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd9211b69f8dcdfa817bfd14bf1c97c9188afa36f4750130fcdf3f400eca9fa8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "windows-link" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76840935b766e1b0a05c0066835fb9ec80071d4c09a16f6bd5f7e655e3c14c38" + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-sys" version = "0.42.0" @@ -5449,7 +6426,16 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", ] [[package]] @@ -5469,18 +6455,18 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", ] [[package]] @@ -5497,9 +6483,9 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" [[package]] name = "windows_aarch64_msvc" @@ -5515,9 +6501,9 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" [[package]] name = "windows_i686_gnu" @@ -5533,15 +6519,15 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_msvc" @@ -5557,9 +6543,9 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" [[package]] name = "windows_x86_64_gnu" @@ -5575,9 +6561,9 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" [[package]] name = "windows_x86_64_gnullvm" @@ -5593,9 +6579,9 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] name = "windows_x86_64_msvc" @@ -5611,9 +6597,9 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] name = "winnow" @@ -5626,36 +6612,34 @@ dependencies = [ [[package]] name = "winnow" -version = "0.6.6" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0c976aaaa0e1f90dbb21e9587cdaf1d9679a1cde8875c0d6bd83ab96a208352" +checksum = "c06928c8748d81b05c9be96aad92e1b6ff01833332f281e8cfca3be4b35fc9ec" dependencies = [ "memchr", ] [[package]] -name = "winreg" -version = "0.50.0" +name = "wit-bindgen-rt" +version = "0.39.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "524e57b2c537c0f9b1e69f1965311ec12182b4122e45035b1508cd24d2adadb1" +checksum = "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1" dependencies = [ - "cfg-if", - "windows-sys 0.48.0", + "bitflags 2.9.1", ] [[package]] name = "wl-clipboard-rs" -version = "0.8.1" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12b41773911497b18ca8553c3daaf8ec9fe9819caf93d451d3055f69de028adb" +checksum = "8e5ff8d0e60065f549fafd9d6cb626203ea64a798186c80d8e7df4f8af56baeb" dependencies = [ - "derive-new", "libc", "log", - "nix 0.28.0", "os_pipe", + "rustix 0.38.44", "tempfile", - "thiserror", + "thiserror 2.0.12", "tree_magic_mini", "wayland-backend", "wayland-client", @@ -5664,21 +6648,27 @@ dependencies = [ ] [[package]] -name = "x11rb" -version = "0.13.0" +name = "writeable" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8f25ead8c7e4cba123243a6367da5d3990e0d3affa708ea19dce96356bd9f1a" +checksum = "ea2f10b9bb0928dfb1b42b65e1f9e36f7f54dbdf08457afefb38afcdec4fa2bb" + +[[package]] +name = "x11rb" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" dependencies = [ "gethostname", - "rustix 0.38.32", + "rustix 0.38.44", "x11rb-protocol", ] [[package]] name = "x11rb-protocol" -version = "0.13.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e63e71c4b8bd9ffec2c963173a4dc4cbde9ee96961d4fcb4429db9929b606c34" +checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" [[package]] name = "x25519-dalek" @@ -5687,30 +6677,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7e468321c81fb07fa7f4c636c3972b9100f0346e5b6a9f2bd0603a52f7ed277" dependencies = [ "curve25519-dalek", - "rand_core", + "rand_core 0.6.4", "serde", "zeroize", ] -[[package]] -name = "x509-cert" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1301e935010a701ae5f8655edc0ad17c44bad3ac5ce8c39185f75453b720ae94" -dependencies = [ - "const-oid", - "der", - "spki", -] - [[package]] name = "xdg-home" -version = "1.1.0" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21e5a325c3cb8398ad6cf859c1135b25dd29e186679cf2da7581d9679f63b38e" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" dependencies = [ "libc", - "winapi", + "windows-sys 0.59.0", ] [[package]] @@ -5725,10 +6704,40 @@ dependencies = [ ] [[package]] -name = "yansi" -version = "0.5.1" +name = "xxhash-rust" +version = "0.8.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09041cd90cf85f7f8b2df60c646f853b7f535ce68f85244eb6731cf89fa498ec" +checksum = "fdd20c5420375476fbd4394763288da7eb0cc0b8c11deed431a91562af7335d3" + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" + +[[package]] +name = "yoke" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f41bb01b8226ef4bfd589436a297c53d118f65921786300e427be8d487695cc" +dependencies = [ + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", +] + +[[package]] +name = "yoke-derive" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38da3c9736e16c5d3c8c597a9aaa5d1fa565d0532ae05e27c24aa62fb32c0ab6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", + "synstructure", +] [[package]] name = "zbus" @@ -5754,10 +6763,10 @@ dependencies = [ "futures-sink", "futures-util", "hex", - "nix 0.26.4", + "nix", "once_cell", "ordered-stream", - "rand", + "rand 0.8.5", "serde", "serde_repr", "sha1", @@ -5798,29 +6807,50 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.7.32" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74d4d3961e53fa4c9a25a8637fc2bfaf2595b3d3ae34875568a5cf64787716be" +checksum = "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.7.32" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ce1b18ccd8e73a9321186f97e46f9f04b778851177567b1975109d26a08d2a6" +checksum = "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", +] + +[[package]] +name = "zerofrom" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +dependencies = [ + "zerofrom-derive", +] + +[[package]] +name = "zerofrom-derive" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", + "synstructure", ] [[package]] name = "zeroize" -version = "1.7.0" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525b4ec142c6b68a2d10f01f7bbf6755599ca3f81ea53b8431b7dd348f5fdb2d" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ "zeroize_derive", ] @@ -5833,9 +6863,48 @@ checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ "proc-macro2", "quote", - "syn 2.0.59", + "syn 2.0.101", ] +[[package]] +name = "zerotrie" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "36f0bbd478583f79edad978b407914f61b2972f5af6fa089686016be8f9af595" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", +] + +[[package]] +name = "zerovec" +version = "0.11.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4a05eb080e015ba39cc9e23bbe5e7fb04d5fb040350f99f34e338d5fdd294428" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] + +[[package]] +name = "zerovec-derive" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b96237efa0c878c64bd89c436f661be4e46b2f3eff1ebb976f7ef2321d2f58f" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.101", +] + +[[package]] +name = "zune-core" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" + [[package]] name = "zune-inflate" version = "0.2.54" @@ -5845,6 +6914,15 @@ dependencies = [ "simd-adler32", ] +[[package]] +name = "zune-jpeg" +version = "0.4.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" +dependencies = [ + "zune-core", +] + [[package]] name = "zvariant" version = "3.15.2" diff --git a/Cargo.toml b/Cargo.toml index ab77424..4530498 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "iamb" -version = "0.0.10" +version = "0.0.11-alpha.1" edition = "2018" authors = ["Ulyssa "] repository = "https://github.com/ulyssa/iamb" @@ -11,7 +11,7 @@ license = "Apache-2.0" exclude = [".github", "CONTRIBUTING.md"] keywords = ["matrix", "chat", "tui", "vim"] categories = ["command-line-utilities"] -rust-version = "1.70" +rust-version = "1.83" build = "build.rs" [features] @@ -34,10 +34,11 @@ clap = {version = "~4.3", features = ["derive"]} css-color-parser = "0.1.2" dirs = "4.0.0" emojis = "0.5" +feruca = "0.10.1" futures = "0.3" gethostname = "0.4.1" html5ever = "0.26.0" -image = "0.24.5" +image = "^0.25.6" libc = "0.2" markup5ever_rcdom = "0.2.0" mime = "^0.3.16" @@ -45,8 +46,8 @@ mime_guess = "^2.0.4" nom = "7.0.0" open = "3.2.0" rand = "0.8.5" -ratatui = "0.26" -ratatui-image = { version = "1.0.0", features = ["serde"] } +ratatui = "0.29.0" +ratatui-image = { version = "~8.0.1", features = ["serde"] } regex = "^1.5" rpassword = "^7.2" serde = "^1.0" @@ -63,6 +64,7 @@ unicode-width = "0.1.10" url = {version = "^2.2.2", features = ["serde"]} edit = "0.1.4" humansize = "2.0.0" +linkify = "0.10.0" [dependencies.comrak] version = "0.22.0" @@ -70,24 +72,24 @@ default-features = false features = ["shortcodes"] [dependencies.notify-rust] -version = "4.10.0" +version = "~4.10.0" default-features = false features = ["zbus", "serde"] optional = true [dependencies.modalkit] -version = "0.0.20" +version = "0.0.23" default-features = false #git = "https://github.com/ulyssa/modalkit" -#rev = "24f3ec11c7f634005a27b26878d0fbbdcc08f272" +#rev = "e40dbb0bfeabe4cfd08facd2acb446080a330d75" [dependencies.modalkit-ratatui] -version = "0.0.20" +version = "0.0.23" #git = "https://github.com/ulyssa/modalkit" -#rev = "24f3ec11c7f634005a27b26878d0fbbdcc08f272" +#rev = "e40dbb0bfeabe4cfd08facd2acb446080a330d75" [dependencies.matrix-sdk] -version = "0.7.1" +version = "0.10.0" default-features = false features = ["e2e-encryption", "sqlite", "sso-login"] diff --git a/README.md b/README.md index 135f640..ecd68f8 100644 --- a/README.md +++ b/README.md @@ -53,7 +53,7 @@ user_id = "@user:example.com" ## Installation (via `crates.io`) -Install Rust (1.70.0 or above) and Cargo, and then run: +Install Rust (1.83.0 or above) and Cargo, and then run: ``` cargo install --locked iamb @@ -80,9 +80,27 @@ On FreeBSD a package is available from the official repositories. To install it pkg install iamb ``` +### Gentoo + +On Gentoo, an ebuild is available from the community-managed +[GURU overlay](https://wiki.gentoo.org/wiki/Project:GURU). + +You can enable the GURU overlay with: + +``` +eselect repository enable guru +emerge --sync guru +``` + +And then install `iamb` with: + +``` +emerge --ask iamb +``` + ### macOS -On macOS a [package](https://formulae.brew.sh/formula/iamb#default) is availabe in Homebrew's +On macOS a [package](https://formulae.brew.sh/formula/iamb#default) is available in Homebrew's repository. To install it simply run: ``` diff --git a/docs/iamb.1 b/docs/iamb.1 index 38070a2..2487b92 100644 --- a/docs/iamb.1 +++ b/docs/iamb.1 @@ -54,7 +54,7 @@ version and quit. View a list of joined rooms and direct messages. .It Sy ":dms" View a list of direct messages. -.It Sy ":logout" +.It Sy ":logout [user id]" Log out of .Nm . .It Sy ":rooms" @@ -63,6 +63,8 @@ View a list of joined rooms. View a list of joined spaces. .It Sy ":unreads" View a list of unread rooms. +.It Sy ":unreads clear" +Mark all rooms as read. .It Sy ":welcome" View the startup Welcome window. .El @@ -77,39 +79,54 @@ Import and decrypt keys from .Pa path . .It Sy ":verify" View a list of ongoing E2EE verifications. +.It Sy ":verify accept [key]" +Accept a verification request. +.It Sy ":verify cancel [key]" +Cancel an in-progress verification. +.It Sy ":verify confirm [key]" +Confirm an in-progress verification. +.It Sy ":verify mismatch [key]" +Reject an in-progress verification due to mismatched Emoji. +.It Sy ":verify request [user id]" +Request a new verification with the specified user. .El .Sh "MESSAGE COMMANDS" .Bl -tag -width Ds -.It Sy ":download" -Download an attachment from the selected message. +.It Sy ":download [path]" +Download an attachment from the selected message and save it to the optional path. +.It Sy ":open [path]" +Download and then open an attachment, or open a link in a message. .It Sy ":edit" Edit the selected message. .It Sy ":editor" Open an external .Ev $EDITOR to compose a message. -.It Sy ":open" -Download and then open an attachment, or open a link in a message. .It Sy ":react [shortcode]" React to the selected message with an Emoji. -.It Sy ":redact [reason]" -Redact the selected message. -.It Sy ":reply" -Reply to the selected message. -.It Sy ":unreads clear" -Mark all unread rooms as read. .It Sy ":unreact [shortcode]" Remove your reaction from the selected message. When no arguments are given, remove all of your reactions from the message. -.It Sy ":upload" +.It Sy ":redact [reason]" +Redact the selected message with the optional reason. +.It Sy ":reply" +Reply to the selected message. +.It Sy ":cancel" +Cancel the currently drafted message including replies. +.It Sy ":upload [path]" Upload an attachment and send it to the currently selected room. .El .Sh "ROOM COMMANDS" .Bl -tag -width Ds -.It Sy ":create" -Create a new room. +.It Sy ":create [arguments]" +Create a new room. Arguments can be +.Dq ++alias=[alias] , +.Dq ++public , +.Dq ++space , +and +.Dq ++encrypted . .It Sy ":invite accept" Accept an invitation to the currently focused room. .It Sy ":invite reject" @@ -117,7 +134,7 @@ Reject an invitation to the currently focused room. .It Sy ":invite send [user]" Send an invitation to a user to join the currently focused room. .It Sy ":join [room]" -Join a room. +Join a room or open it if you are already joined. .It Sy ":leave" Leave the currently focused room. .It Sy ":members" @@ -126,6 +143,10 @@ View a list of members of the currently focused room. Set the name of the currently focused room. .It Sy ":room name unset" Unset the name of the currently focused room. +.It Sy ":room dm set" +Mark the currently focused room as a direct message. +.It Sy ":room dm unset" +Mark the currently focused room as a normal room. .It Sy ":room notify set [level]" Set a notification level for the currently focused room. Valid levels are @@ -153,12 +174,16 @@ Remove a tag from the currently focused room. Set the topic of the currently focused room. .It Sy ":room topic unset" Unset the topic of the currently focused room. +.It Sy ":room topic show" +Show the topic of the currently focused room. .It Sy ":room alias set [alias]" Create and point the given alias to the room. .It Sy ":room alias unset [alias]" Delete the provided alias from the room's alternative alias list. .It Sy ":room alias show" Show alternative aliases to the room, if any are set. +.It Sy ":room id show" +Show the Matrix identifier for the room. .It Sy ":room canon set [alias]" Set the room's canonical alias to the one provided, and make the previous one an alternative alias. .It Sy ":room canon unset [alias]" @@ -173,6 +198,18 @@ Unban a user from this room with an optional reason. Kick a user from this room with an optional reason. .El +.Sh "SPACE COMMANDS" +.Bl -tag -width Ds +.It Sy ":space child set [room_id] [arguments]" +Add a room to the currently focused space. +.Dq ++suggested +marks the room as a suggested child. +.Dq ++order=[string] +specifies a string by which children are lexicographically ordered. +.It Sy ":space child remove" +Remove the selected room from the currently focused space. +.El + .Sh "WINDOW COMMANDS" .Bl -tag -width Ds .It Sy ":horizontal [cmd]" diff --git a/docs/iamb.5 b/docs/iamb.5 index 90d86de..8357f96 100644 --- a/docs/iamb.5 +++ b/docs/iamb.5 @@ -173,6 +173,9 @@ respective shortcodes. .It Sy message_user_color Defines whether or not the message body is colored like the username. +.It Sy normal_after_send +Defines whether to reset input to Normal mode after sending a message. + .It Sy notifications When this subsection is present, you can enable and configure push notifications. See @@ -208,6 +211,9 @@ See .Sx "SORTING LISTS" for more details. +.It Sy state_event_display +Defines whether the state events like joined or left are shown. + .It Sy typing_notice_send Defines whether or not the typing state is sent. @@ -231,6 +237,10 @@ Possible values are Specify the width of the column where usernames are displayed in a room. Usernames that are too long are truncated. Defaults to 30. + +.It Sy tabstop +Number of spaces that a counts for. +Defaults to 4. .El .Ss Example 1: Avoid showing Emojis (useful for terminals w/o support) @@ -269,6 +279,8 @@ to use the desktop mechanism (default). Setting this field to .Dq Sy bell will use the terminal bell instead. +Both can be used via +.Dq Sy desktop|bell . .It Sy show_message controls whether to show the message in the desktop notification, and defaults to @@ -332,9 +344,29 @@ window. Defaults to .Sy ["power",\ "id"] . .El + +The available values are: +.Bl -tag -width Ds +.It Sy favorite +Put favorite rooms before other rooms. +.It Sy lowpriority +Put lowpriority rooms after other rooms. +.It Sy name +Sort rooms by alphabetically ascending room name. +.It Sy alias +Sort rooms by alphabetically ascending canonical room alias. +.It Sy id +Sort rooms by alphabetically ascending Matrix room identifier. +.It Sy unread +Put unread rooms before other rooms. +.It Sy recent +Sort rooms by most recent message timestamp. +.It Sy invite +Put invites before other rooms. +.El .El -.Ss Example 1: Group room members by ther server first +.Ss Example 1: Group room members by their server first .Bd -literal -offset indent [settings.sort] members = ["server", "localpart"] diff --git a/docs/iamb.metainfo.xml b/docs/iamb.metainfo.xml index e064c6c..ee7b3b0 100644 --- a/docs/iamb.metainfo.xml +++ b/docs/iamb.metainfo.xml @@ -1,12 +1,13 @@ - iamb + chat.iamb.iamb iamb A terminal Matrix client for Vim addicts https://iamb.chat + @@ -14,6 +15,7 @@ Ulyssa + Ulyssa CC-BY-SA-4.0 Apache-2.0 @@ -23,8 +25,8 @@ - https://iamb.chat/static/images/iamb-demo.gif - Example conversation within iamb + https://iamb.chat/static/images/metainfo-screenshot.png + Example screenshot of room and lists of rooms, spaces and members within iamb @@ -37,7 +39,6 @@

- https://iamb.chat/images/iamb.svg iamb.desktop diff --git a/flake.lock b/flake.lock index ff2375f..00e09a9 100644 --- a/flake.lock +++ b/flake.lock @@ -5,29 +5,11 @@ "systems": "systems" }, "locked": { - "lastModified": 1709126324, - "narHash": "sha256-q6EQdSeUZOG26WelxqkmR7kArjgWCdw5sfJVHPH/7j8=", + "lastModified": 1731533236, + "narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=", "owner": "numtide", "repo": "flake-utils", - "rev": "d465f4819400de7c8d874d50b982301f28a84605", - "type": "github" - }, - "original": { - "owner": "numtide", - "repo": "flake-utils", - "type": "github" - } - }, - "flake-utils_2": { - "inputs": { - "systems": "systems_2" - }, - "locked": { - "lastModified": 1705309234, - "narHash": "sha256-uNRRNRKmJyCRC/8y1RqBkqWBLM034y4qN7EprSdmgyA=", - "owner": "numtide", - "repo": "flake-utils", - "rev": "1ef2e671c3b0c19053962c07dbda38332dcebf26", + "rev": "11707dc2f618dd54ca8739b309ec4fc024de578b", "type": "github" }, "original": { @@ -38,11 +20,11 @@ }, "nixpkgs": { "locked": { - "lastModified": 1709703039, - "narHash": "sha256-6hqgQ8OK6gsMu1VtcGKBxKQInRLHtzulDo9Z5jxHEFY=", + "lastModified": 1736883708, + "narHash": "sha256-uQ+NQ0/xYU0N1CnXsa2zghgNaOPxWpMJXSUJJ9W7140=", "owner": "nixos", "repo": "nixpkgs", - "rev": "9df3e30ce24fd28c7b3e2de0d986769db5d6225d", + "rev": "eb62e6aa39ea67e0b8018ba8ea077efe65807dc8", "type": "github" }, "original": { @@ -54,11 +36,11 @@ }, "nixpkgs_2": { "locked": { - "lastModified": 1706487304, - "narHash": "sha256-LE8lVX28MV2jWJsidW13D2qrHU/RUUONendL2Q/WlJg=", + "lastModified": 1736320768, + "narHash": "sha256-nIYdTAiKIGnFNugbomgBJR+Xv5F1ZQU+HfaBqJKroC0=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "90f456026d284c22b3e3497be980b2e47d0b28ac", + "rev": "4bc9c909d9ac828a039f288cf872d16d38185db8", "type": "github" }, "original": { @@ -77,15 +59,14 @@ }, "rust-overlay": { "inputs": { - "flake-utils": "flake-utils_2", "nixpkgs": "nixpkgs_2" }, "locked": { - "lastModified": 1709863839, - "narHash": "sha256-QpEL5FmZNi2By3sKZY55wGniFXc4wEn9PQczlE8TG0o=", + "lastModified": 1736994333, + "narHash": "sha256-v4Jrok5yXsZ6dwj2+2uo5cSyUi9fBTurHqHvNHLT1XA=", "owner": "oxalica", "repo": "rust-overlay", - "rev": "e5ab9ee98f479081ad971473d2bc13c59e9fbc0a", + "rev": "848db855cb9e88785996e961951659570fc58814", "type": "github" }, "original": { @@ -108,21 +89,6 @@ "repo": "default", "type": "github" } - }, - "systems_2": { - "locked": { - "lastModified": 1681028828, - "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", - "owner": "nix-systems", - "repo": "default", - "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", - "type": "github" - }, - "original": { - "owner": "nix-systems", - "repo": "default", - "type": "github" - } } }, "root": "root", diff --git a/flake.nix b/flake.nix index 43dfd3b..e12bb5b 100644 --- a/flake.nix +++ b/flake.nix @@ -14,7 +14,7 @@ # We only need the nightly overlay in the devShell because .rs files are formatted with nightly. overlays = [ (import rust-overlay) ]; pkgs = import nixpkgs { inherit system overlays; }; - rustNightly = pkgs.rust-bin.nightly."2024-03-08".default; + rustNightly = pkgs.rust-bin.nightly."2024-12-12".default; in with pkgs; { @@ -27,7 +27,7 @@ }; nativeBuildInputs = [ pkg-config ]; buildInputs = [ openssl ] ++ lib.optionals stdenv.isDarwin - (with darwin.apple_sdk.frameworks; [ AppKit Security Cocoa]); + (with darwin.apple_sdk.frameworks; [ AppKit Security Cocoa ]); }; devShell = mkShell { @@ -38,6 +38,7 @@ pkg-config cargo-tarpaulin cargo-watch + sqlite ]; }; }); diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..d2c98e1 --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "1.83" +components = [ "clippy" ] diff --git a/src/base.rs b/src/base.rs index 5c6bdb9..bb4f491 100644 --- a/src/base.rs +++ b/src/base.rs @@ -12,6 +12,7 @@ use std::sync::Arc; use std::time::{Duration, Instant}; use emojis::Emoji; +use matrix_sdk::ruma::events::receipt::ReceiptThread; use ratatui::{ buffer::Buffer, layout::{Alignment, Rect}, @@ -47,6 +48,7 @@ use matrix_sdk::{ }, room::redaction::{OriginalSyncRoomRedactionEvent, SyncRoomRedactionEvent}, tag::{TagName, Tags}, + AnySyncStateEvent, MessageLikeEvent, }, presence::PresenceState, @@ -72,7 +74,7 @@ use modalkit::{ ApplicationStore, ApplicationWindowId, }, - completion::{complete_path, CompletionMap}, + completion::{complete_path, Completer, CompletionMap}, context::EditContext, cursor::Cursor, rope::EditRope, @@ -90,6 +92,7 @@ use modalkit::{ use crate::config::ImagePreviewProtocolValues; use crate::message::ImageStatus; +use crate::notifications::NotificationHandle; use crate::preview::{source_from_event, spawn_insert_preview}; use crate::{ message::{Message, MessageEvent, MessageKey, MessageTimeStamp, Messages}, @@ -177,6 +180,19 @@ pub enum MessageAction { Unreact(Option, bool), } +/// An action taken in the currently selected space. +#[derive(Clone, Debug, Eq, PartialEq)] +pub enum SpaceAction { + /// Add a room or update metadata. + /// + /// The [`Option`] argument is the order parameter. + /// The [`bool`] argument indicates whether the room is suggested. + SetChild(OwnedRoomId, Option, bool), + + /// Remove the selected room. + RemoveChild, +} + /// The type of room being created. #[derive(Clone, Debug, Eq, PartialEq)] pub enum CreateRoomType { @@ -243,6 +259,9 @@ pub enum SortFieldRoom { /// Sort rooms by the timestamps of their most recent messages. Recent, + + /// Sort rooms by whether they are invites. + Invite, } /// Fields that users can be sorted by. @@ -277,7 +296,7 @@ impl<'de> Deserialize<'de> for SortColumn { /// [serde] visitor for deserializing [SortColumn] for rooms and spaces. struct SortRoomVisitor; -impl<'de> Visitor<'de> for SortRoomVisitor { +impl Visitor<'_> for SortRoomVisitor { type Value = SortColumn; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -307,6 +326,7 @@ impl<'de> Visitor<'de> for SortRoomVisitor { "name" => SortFieldRoom::Name, "alias" => SortFieldRoom::Alias, "id" => SortFieldRoom::RoomId, + "invite" => SortFieldRoom::Invite, _ => { let msg = format!("Unknown sort field: {value:?}"); return Err(E::custom(msg)); @@ -329,7 +349,7 @@ impl<'de> Deserialize<'de> for SortColumn { /// [serde] visitor for deserializing [SortColumn] for users. struct SortUserVisitor; -impl<'de> Visitor<'de> for SortUserVisitor { +impl Visitor<'_> for SortUserVisitor { type Value = SortColumn; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -375,6 +395,9 @@ pub enum RoomField { /// The room name. Name, + /// The room id. + Id, + /// A room tag. Tag(TagName), @@ -493,6 +516,9 @@ pub enum IambAction { /// Perform an action on the currently selected message. Message(MessageAction), + /// Perform an action on the current space. + Space(SpaceAction), + /// Open a URL. OpenLink(String), @@ -534,6 +560,12 @@ impl From for IambAction { } } +impl From for IambAction { + fn from(act: SpaceAction) -> Self { + IambAction::Space(act) + } +} + impl From for IambAction { fn from(act: RoomAction) -> Self { IambAction::Room(act) @@ -553,6 +585,7 @@ impl ApplicationAction for IambAction { IambAction::Homeserver(..) => SequenceStatus::Break, IambAction::Keys(..) => SequenceStatus::Break, IambAction::Message(..) => SequenceStatus::Break, + IambAction::Space(..) => SequenceStatus::Break, IambAction::Room(..) => SequenceStatus::Break, IambAction::OpenLink(..) => SequenceStatus::Break, IambAction::Send(..) => SequenceStatus::Break, @@ -568,6 +601,7 @@ impl ApplicationAction for IambAction { IambAction::Homeserver(..) => SequenceStatus::Atom, IambAction::Keys(..) => SequenceStatus::Atom, IambAction::Message(..) => SequenceStatus::Atom, + IambAction::Space(..) => SequenceStatus::Atom, IambAction::OpenLink(..) => SequenceStatus::Atom, IambAction::Room(..) => SequenceStatus::Atom, IambAction::Send(..) => SequenceStatus::Atom, @@ -583,6 +617,7 @@ impl ApplicationAction for IambAction { IambAction::Homeserver(..) => SequenceStatus::Ignore, IambAction::Keys(..) => SequenceStatus::Ignore, IambAction::Message(..) => SequenceStatus::Ignore, + IambAction::Space(..) => SequenceStatus::Ignore, IambAction::Room(..) => SequenceStatus::Ignore, IambAction::OpenLink(..) => SequenceStatus::Ignore, IambAction::Send(..) => SequenceStatus::Ignore, @@ -597,6 +632,7 @@ impl ApplicationAction for IambAction { IambAction::ClearUnreads => false, IambAction::Homeserver(..) => false, IambAction::Message(..) => false, + IambAction::Space(..) => false, IambAction::Room(..) => false, IambAction::Keys(..) => false, IambAction::Send(..) => false, @@ -614,6 +650,12 @@ impl From for ProgramAction { } } +impl From for ProgramAction { + fn from(act: SpaceAction) -> Self { + IambAction::from(act).into() + } +} + impl From for ProgramAction { fn from(act: IambAction) -> Self { Action::Application(act) @@ -709,10 +751,22 @@ pub enum IambError { #[error("Current window is not a room or space")] NoSelectedRoomOrSpace, + /// A failure due to not having a room or space item selected in a list. + #[error("No room or space currently selected in list")] + NoSelectedRoomOrSpaceItem, + /// A failure due to not having a room selected. #[error("Current window is not a room")] NoSelectedRoom, + /// A failure due to not having a space selected. + #[error("Current window is not a space")] + NoSelectedSpace, + + /// A failure due to not having sufficient permission to perform an action in a room. + #[error("You do not have the permission to do that")] + InsufficientPermission, + /// A failure due to not having an outstanding room invitation. #[error("You do not have a current invitation to this room")] NotInvited, @@ -785,6 +839,9 @@ pub enum EventLocation { /// The [EventId] belongs to a reaction to the given event. Reaction(OwnedEventId), + + /// The [EventId] belongs to a state event in the main timeline of the room. + State(MessageKey), } impl EventLocation { @@ -814,7 +871,6 @@ impl UnreadInfo { } /// Information about room's the user's joined. -#[derive(Default)] pub struct RoomInfo { /// The display name for this room. pub name: Option, @@ -829,15 +885,13 @@ pub struct RoomInfo { messages: Messages, /// A map of read markers to display on different events. - pub event_receipts: HashMap>, - + pub event_receipts: HashMap>>, /// A map of the most recent read marker for each user. /// - /// Every receipt in this map should also have an entry in [`event_receipts`], + /// Every receipt in this map should also have an entry in [`event_receipts`](`Self::event_receipts`), /// however not every user has an entry. If a user's most recent receipt is /// older than the oldest loaded event, that user will not be included. - pub user_receipts: HashMap, - + pub user_receipts: HashMap>, /// A map of message identifiers to a map of reaction events. pub reactions: HashMap, @@ -863,6 +917,28 @@ pub struct RoomInfo { pub draw_last: Option, } +impl Default for RoomInfo { + fn default() -> Self { + Self { + messages: Messages::new(ReceiptThread::Main), + + name: Default::default(), + tags: Default::default(), + keys: Default::default(), + event_receipts: Default::default(), + user_receipts: Default::default(), + reactions: Default::default(), + threads: Default::default(), + fetching: Default::default(), + fetch_id: Default::default(), + fetch_last: Default::default(), + users_typing: Default::default(), + display_names: Default::default(), + draw_last: Default::default(), + } + } +} + impl RoomInfo { pub fn get_thread(&self, root: Option<&EventId>) -> Option<&Messages> { if let Some(thread_root) = root { @@ -874,7 +950,9 @@ impl RoomInfo { pub fn get_thread_mut(&mut self, root: Option) -> &mut Messages { if let Some(thread_root) = root { - self.threads.entry(thread_root).or_default() + self.threads + .entry(thread_root.clone()) + .or_insert_with(|| Messages::thread(thread_root)) } else { &mut self.messages } @@ -952,6 +1030,12 @@ impl RoomInfo { match self.keys.get(redacts) { None => return, + Some(EventLocation::State(key)) => { + if let Some(msg) = self.messages.get_mut(key) { + let ev = SyncRoomRedactionEvent::Original(ev); + msg.redact(ev, room_version); + } + }, Some(EventLocation::Message(None, key)) => { if let Some(msg) = self.messages.get_mut(key) { let ev = SyncRoomRedactionEvent::Original(ev); @@ -1008,7 +1092,9 @@ impl RoomInfo { }; let source = if let Some(thread) = thread { - self.threads.entry(thread.clone()).or_default() + self.threads + .entry(thread.clone()) + .or_insert_with(|| Messages::thread(thread.clone())) } else { &mut self.messages }; @@ -1025,6 +1111,7 @@ impl RoomInfo { content.apply_replacement(new_msgtype); }, MessageEvent::Redacted(_) | + MessageEvent::State(_) | MessageEvent::EncryptedOriginal(_) | MessageEvent::EncryptedRedacted(_) => { return; @@ -1034,16 +1121,32 @@ impl RoomInfo { msg.html = msg.event.html(); } + pub fn insert_any_state(&mut self, msg: AnySyncStateEvent) { + let event_id = msg.event_id().to_owned(); + let key = (msg.origin_server_ts().into(), event_id.clone()); + + let loc = EventLocation::State(key.clone()); + self.keys.insert(event_id, loc); + self.messages.insert_message(key, msg); + } + /// Indicates whether this room has unread messages. pub fn unreads(&self, settings: &ApplicationSettings) -> UnreadInfo { let last_message = self.messages.last_key_value(); - let last_receipt = self.get_receipt(&settings.profile.user_id); + let last_receipt = self + .user_receipts + .get(&ReceiptThread::Main) + .and_then(|receipts| receipts.get(&settings.profile.user_id)); match (last_message, last_receipt) { (Some(((ts, recent), _)), Some(last_read)) => { UnreadInfo { unread: last_read != recent, latest: Some(*ts) } }, - (Some(((ts, _), _)), None) => UnreadInfo { unread: false, latest: Some(*ts) }, + (Some(((ts, _), _)), None) => { + // If we've never loaded/generated a room's receipt (example, + // a newly joined but never viewed room), show it as unread. + UnreadInfo { unread: true, latest: Some(*ts) } + }, (None, _) => UnreadInfo::default(), } } @@ -1071,7 +1174,10 @@ impl RoomInfo { let event_id = msg.event_id().to_owned(); let key = (msg.origin_server_ts().into(), event_id.clone()); - let replies = self.threads.entry(thread_root.clone()).or_default(); + let replies = self + .threads + .entry(thread_root.clone()) + .or_insert_with(|| Messages::thread(thread_root.clone())); let loc = EventLocation::Message(Some(thread_root), key.clone()); self.keys.insert(event_id, loc); replies.insert_message(key, msg); @@ -1131,40 +1237,73 @@ impl RoomInfo { /// Indicates whether we've recently fetched scrollback for this room. pub fn recently_fetched(&self) -> bool { - self.fetch_last.map_or(false, |i| i.elapsed() < ROOM_FETCH_DEBOUNCE) + self.fetch_last.is_some_and(|i| i.elapsed() < ROOM_FETCH_DEBOUNCE) } - fn clear_receipt(&mut self, user_id: &OwnedUserId) -> Option<()> { - let old_event_id = self.user_receipts.get(user_id)?; - let old_receipts = self.event_receipts.get_mut(old_event_id)?; + fn clear_receipt(&mut self, thread: &ReceiptThread, user_id: &OwnedUserId) -> Option<()> { + let old_event_id = + self.user_receipts.get(thread).and_then(|receipts| receipts.get(user_id))?; + let old_thread = self.event_receipts.get_mut(thread)?; + let old_receipts = old_thread.get_mut(old_event_id)?; old_receipts.remove(user_id); if old_receipts.is_empty() { - self.event_receipts.remove(old_event_id); + old_thread.remove(old_event_id); + } + if old_thread.is_empty() { + self.event_receipts.remove(thread); } None } - pub fn set_receipt(&mut self, user_id: OwnedUserId, event_id: OwnedEventId) { - self.clear_receipt(&user_id); + pub fn set_receipt( + &mut self, + thread: ReceiptThread, + user_id: OwnedUserId, + event_id: OwnedEventId, + ) { + self.clear_receipt(&thread, &user_id); self.event_receipts + .entry(thread.clone()) + .or_default() .entry(event_id.clone()) .or_default() .insert(user_id.clone()); - self.user_receipts.insert(user_id, event_id); + self.user_receipts.entry(thread).or_default().insert(user_id, event_id); } - pub fn fully_read(&mut self, user_id: OwnedUserId) { + pub fn fully_read(&mut self, user_id: &UserId) { let Some(((_, event_id), _)) = self.messages.last_key_value() else { return; }; - self.set_receipt(user_id, event_id.clone()); + self.set_receipt(ReceiptThread::Main, user_id.to_owned(), event_id.clone()); + + let newest = self + .threads + .iter() + .filter_map(|(thread_id, messages)| { + let thread = ReceiptThread::Thread(thread_id.to_owned()); + + messages + .last_key_value() + .map(|((_, event_id), _)| (thread, event_id.to_owned())) + }) + .collect::>(); + + for (thread, event_id) in newest.into_iter() { + self.set_receipt(thread, user_id.to_owned(), event_id.clone()); + } } - pub fn get_receipt(&self, user_id: &UserId) -> Option<&OwnedEventId> { - self.user_receipts.get(user_id) + pub fn receipts<'a>( + &'a self, + user_id: &'a UserId, + ) -> impl Iterator + 'a { + self.user_receipts + .iter() + .filter_map(move |(t, rs)| rs.get(user_id).map(|r| (t, r))) } fn get_typers(&self) -> &[OwnedUserId] { @@ -1223,7 +1362,9 @@ impl RoomInfo { } if !settings.tunables.typing_notice_display { - return area; + // still keep one line blank, so `render_jump_to_recent` doesn't immediately hide the + // last line in scrollback + return Rect::new(area.x, area.y, area.width, area.height - 1); } let top = Rect::new(area.x, area.y, area.width, area.height - 1); @@ -1268,7 +1409,7 @@ fn emoji_map() -> CompletionMap { #[cfg(unix)] fn picker_from_termios(protocol_type: Option) -> Option { - let mut picker = match Picker::from_termios() { + let mut picker = match Picker::from_query_stdio() { Ok(picker) => picker, Err(e) => { tracing::error!("Failed to setup image previews: {e}"); @@ -1277,9 +1418,7 @@ fn picker_from_termios(protocol_type: Option) -> Option { }; if let Some(protocol_type) = protocol_type { - picker.protocol_type = protocol_type; - } else { - picker.guess_protocol(); + picker.set_protocol_type(protocol_type); } Some(picker) @@ -1302,8 +1441,8 @@ fn picker_from_settings(settings: &ApplicationSettings) -> Option { }) = image_preview_protocol { // User forced type and font_size: use that. - let mut picker = Picker::new(font_size); - picker.protocol_type = protocol_type; + let mut picker = Picker::from_fontsize(font_size); + picker.set_protocol_type(protocol_type); Some(picker) } else { // Guess, but use type if forced. @@ -1417,6 +1556,12 @@ pub struct ChatStore { /// Whether the application is currently focused pub focused: bool, + + /// Collator for locale-aware text sorting. + pub collator: feruca::Collator, + + /// Notifications that should be dismissed when the user opens the room. + pub open_notifications: HashMap>, } impl ChatStore { @@ -1431,6 +1576,7 @@ impl ChatStore { cmds: crate::commands::setup_commands(), emojis: emoji_map(), + collator: Default::default(), names: Default::default(), rooms: Default::default(), presences: Default::default(), @@ -1440,6 +1586,7 @@ impl ChatStore { draw_curr: None, ring_bell: false, focused: true, + open_notifications: Default::default(), } } @@ -1560,7 +1707,7 @@ impl<'de> Deserialize<'de> for IambId { /// [serde] visitor for deserializing [IambId]. struct IambIdVisitor; -impl<'de> Visitor<'de> for IambIdVisitor { +impl Visitor<'_> for IambIdVisitor { type Value = IambId; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -1697,6 +1844,13 @@ impl RoomFocus { pub fn is_msgbar(&self) -> bool { matches!(self, RoomFocus::MessageBar) } + + pub fn toggle(&mut self) { + *self = match self { + RoomFocus::MessageBar => RoomFocus::Scrollback, + RoomFocus::Scrollback => RoomFocus::MessageBar, + }; + } } /// Identifiers used to track where a mark was placed. @@ -1765,11 +1919,20 @@ impl ApplicationInfo for IambInfo { type WindowId = IambId; type ContentId = IambBufferId; + fn content_of_command(ct: CommandType) -> IambBufferId { + IambBufferId::Command(ct) + } +} + +pub struct IambCompleter; + +impl Completer for IambCompleter { fn complete( + &mut self, text: &EditRope, cursor: &mut Cursor, content: &IambBufferId, - store: &mut ProgramStore, + store: &mut ChatStore, ) -> Vec { match content { IambBufferId::Command(CommandType::Command) => complete_cmdbar(text, cursor, store), @@ -1787,21 +1950,16 @@ impl ApplicationInfo for IambInfo { IambBufferId::UnreadList => vec![], } } - - fn content_of_command(ct: CommandType) -> IambBufferId { - IambBufferId::Command(ct) - } } /// Tab completion for user IDs. -fn complete_users(text: &EditRope, cursor: &mut Cursor, store: &ProgramStore) -> Vec { +fn complete_users(text: &EditRope, cursor: &mut Cursor, store: &ChatStore) -> Vec { let id = text .get_prefix_word_mut(cursor, &MATRIX_ID_WORD) .unwrap_or_else(EditRope::empty); let id = Cow::from(&id); store - .application .presences .complete(id.as_ref()) .into_iter() @@ -1810,7 +1968,7 @@ fn complete_users(text: &EditRope, cursor: &mut Cursor, store: &ProgramStore) -> } /// Tab completion within the message bar. -fn complete_msgbar(text: &EditRope, cursor: &mut Cursor, store: &ProgramStore) -> Vec { +fn complete_msgbar(text: &EditRope, cursor: &mut Cursor, store: &ChatStore) -> Vec { let id = text .get_prefix_word_mut(cursor, &MATRIX_ID_WORD) .unwrap_or_else(EditRope::empty); @@ -1819,13 +1977,12 @@ fn complete_msgbar(text: &EditRope, cursor: &mut Cursor, store: &ProgramStore) - match id.chars().next() { // Complete room aliases. Some('#') => { - return store.application.names.complete(id.as_ref()); + return store.names.complete(id.as_ref()); }, // Complete room identifiers. Some('!') => { return store - .application .rooms .complete(id.as_ref()) .into_iter() @@ -1835,7 +1992,7 @@ fn complete_msgbar(text: &EditRope, cursor: &mut Cursor, store: &ProgramStore) - // Complete Emoji shortcodes. Some(':') => { - let list = store.application.emojis.complete(&id[1..]); + let list = store.emojis.complete(&id[1..]); let iter = list.into_iter().take(200).map(|s| format!(":{}:", s)); return iter.collect(); @@ -1844,7 +2001,6 @@ fn complete_msgbar(text: &EditRope, cursor: &mut Cursor, store: &ProgramStore) - // Complete usernames for @ and empty strings. Some('@') | None => { return store - .application .presences .complete(id.as_ref()) .into_iter() @@ -1858,28 +2014,23 @@ fn complete_msgbar(text: &EditRope, cursor: &mut Cursor, store: &ProgramStore) - } /// Tab completion for Matrix identifiers (usernames, room aliases, etc.) -fn complete_matrix_names( - text: &EditRope, - cursor: &mut Cursor, - store: &ProgramStore, -) -> Vec { +fn complete_matrix_names(text: &EditRope, cursor: &mut Cursor, store: &ChatStore) -> Vec { let id = text .get_prefix_word_mut(cursor, &MATRIX_ID_WORD) .unwrap_or_else(EditRope::empty); let id = Cow::from(&id); - let list = store.application.names.complete(id.as_ref()); + let list = store.names.complete(id.as_ref()); if !list.is_empty() { return list; } - let list = store.application.presences.complete(id.as_ref()); + let list = store.presences.complete(id.as_ref()); if !list.is_empty() { return list.into_iter().map(|i| i.to_string()).collect(); } store - .application .rooms .complete(id.as_ref()) .into_iter() @@ -1888,12 +2039,12 @@ fn complete_matrix_names( } /// Tab completion for Emoji shortcode names. -fn complete_emoji(text: &EditRope, cursor: &mut Cursor, store: &ProgramStore) -> Vec { +fn complete_emoji(text: &EditRope, cursor: &mut Cursor, store: &ChatStore) -> Vec { let sc = text.get_prefix_word_mut(cursor, &WordStyle::Little); let sc = sc.unwrap_or_else(EditRope::empty); let sc = Cow::from(&sc); - store.application.emojis.complete(sc.as_ref()) + store.emojis.complete(sc.as_ref()) } /// Tab completion for command names. @@ -1901,11 +2052,11 @@ fn complete_cmdname( desc: CommandDescription, text: &EditRope, cursor: &mut Cursor, - store: &ProgramStore, + store: &ChatStore, ) -> Vec { // Complete command name and set cursor position. let _ = text.get_prefix_word_mut(cursor, &WordStyle::Little); - store.application.cmds.complete_name(desc.command.as_str()) + store.cmds.complete_name(desc.command.as_str()) } /// Tab completion for command arguments. @@ -1913,9 +2064,9 @@ fn complete_cmdarg( desc: CommandDescription, text: &EditRope, cursor: &mut Cursor, - store: &ProgramStore, + store: &ChatStore, ) -> Vec { - let cmd = match store.application.cmds.get(desc.command.as_str()) { + let cmd = match store.cmds.get(desc.command.as_str()) { Ok(cmd) => cmd, Err(_) => return vec![], }; @@ -1938,12 +2089,7 @@ fn complete_cmdarg( } /// Tab completion for commands. -fn complete_cmd( - cmd: &str, - text: &EditRope, - cursor: &mut Cursor, - store: &ProgramStore, -) -> Vec { +fn complete_cmd(cmd: &str, text: &EditRope, cursor: &mut Cursor, store: &ChatStore) -> Vec { match CommandDescription::from_str(cmd) { Ok(desc) => { if desc.arg.untrimmed.is_empty() { @@ -1960,7 +2106,7 @@ fn complete_cmd( } /// Tab completion for the command bar. -fn complete_cmdbar(text: &EditRope, cursor: &mut Cursor, store: &ProgramStore) -> Vec { +fn complete_cmdbar(text: &EditRope, cursor: &mut Cursor, store: &ChatStore) -> Vec { let eo = text.cursor_to_offset(cursor); let slice = text.slice(..eo); let cow = Cow::from(&slice); @@ -2140,6 +2286,7 @@ pub mod tests { #[tokio::test] async fn test_complete_msgbar() { let store = mock_store().await; + let store = store.application; let text = EditRope::from("going for a walk :walk "); let mut cursor = Cursor::new(0, 22); @@ -2163,6 +2310,7 @@ pub mod tests { #[tokio::test] async fn test_complete_cmdbar() { let store = mock_store().await; + let store = store.application; let users = vec![ "@user1:example.com", "@user2:example.com", diff --git a/src/commands.rs b/src/commands.rs index 4335eb7..c70a2af 100644 --- a/src/commands.rs +++ b/src/commands.rs @@ -2,9 +2,9 @@ //! //! The command-bar commands are set up here, and iamb-specific commands are defined here. See //! [modalkit::env::vim::command] for additional Vim commands we pull in. -use std::convert::TryFrom; +use std::{convert::TryFrom, str::FromStr as _}; -use matrix_sdk::ruma::{events::tag::TagName, OwnedUserId}; +use matrix_sdk::ruma::{events::tag::TagName, OwnedRoomId, OwnedUserId}; use modalkit::{ commands::{CommandError, CommandResult, CommandStep}, @@ -27,6 +27,7 @@ use crate::base::{ RoomAction, RoomField, SendAction, + SpaceAction, VerifyAction, }; @@ -475,10 +476,18 @@ fn iamb_room(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult { ("topic", "unset", None) => RoomAction::Unset(RoomField::Topic).into(), ("topic", "unset", Some(_)) => return Result::Err(CommandError::InvalidArgument), + // :room topic show + ("topic", "show", None) => RoomAction::Show(RoomField::Topic).into(), + ("topic", "show", Some(_)) => return Result::Err(CommandError::InvalidArgument), + // :room tag set ("tag", "set", Some(s)) => RoomAction::Set(RoomField::Tag(tag_name(s)?), "".into()).into(), ("tag", "set", None) => return Result::Err(CommandError::InvalidArgument), + // :room tag unset + ("tag", "unset", Some(s)) => RoomAction::Unset(RoomField::Tag(tag_name(s)?)).into(), + ("tag", "unset", None) => return Result::Err(CommandError::InvalidArgument), + // :room notify set ("notify", "set", Some(s)) => RoomAction::Set(RoomField::NotificationMode, s).into(), ("notify", "set", None) => return Result::Err(CommandError::InvalidArgument), @@ -491,10 +500,6 @@ fn iamb_room(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult { ("notify", "show", None) => RoomAction::Show(RoomField::NotificationMode).into(), ("notify", "show", Some(_)) => return Result::Err(CommandError::InvalidArgument), - // :room tag unset - ("tag", "unset", Some(s)) => RoomAction::Unset(RoomField::Tag(tag_name(s)?)).into(), - ("tag", "unset", None) => return Result::Err(CommandError::InvalidArgument), - // :room aliases show ("alias", "show", None) => RoomAction::Show(RoomField::Aliases).into(), ("alias", "show", Some(_)) => return Result::Err(CommandError::InvalidArgument), @@ -531,6 +536,91 @@ fn iamb_room(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult { return Result::Err(CommandError::InvalidArgument) }, + // :room id show + ("id", "show", None) => RoomAction::Show(RoomField::Id).into(), + ("id", "show", Some(_)) => return Result::Err(CommandError::InvalidArgument), + + _ => return Result::Err(CommandError::InvalidArgument), + }; + + let step = CommandStep::Continue(act.into(), ctx.context.clone()); + + return Ok(step); +} + +fn iamb_space(desc: CommandDescription, ctx: &mut ProgContext) -> ProgResult { + let mut args = desc.arg.options()?; + + if args.len() < 2 { + return Err(CommandError::InvalidArgument); + } + + let OptionType::Positional(field) = args.remove(0) else { + return Err(CommandError::InvalidArgument); + }; + let OptionType::Positional(action) = args.remove(0) else { + return Err(CommandError::InvalidArgument); + }; + + let act: IambAction = match (field.as_str(), action.as_str()) { + // :space child remove + ("child", "remove") => { + if !(args.is_empty()) { + return Err(CommandError::InvalidArgument); + } + SpaceAction::RemoveChild.into() + }, + // :space child set + ("child", "set") => { + let mut order = None; + let mut suggested = false; + let mut raw_child = None; + + for arg in args { + match arg { + OptionType::Flag(name, Some(arg)) => { + match name.as_str() { + "order" => { + if order.is_some() { + let msg = "Multiple ++order arguments are not allowed"; + let err = CommandError::Error(msg.into()); + + return Err(err); + } else { + order = Some(arg); + } + }, + _ => return Err(CommandError::InvalidArgument), + } + }, + OptionType::Flag(name, None) => { + match name.as_str() { + "suggested" => suggested = true, + _ => return Err(CommandError::InvalidArgument), + } + }, + OptionType::Positional(arg) => { + if raw_child.is_some() { + let msg = "Multiple room arguments are not allowed"; + let err = CommandError::Error(msg.into()); + + return Err(err); + } + raw_child = Some(arg); + }, + } + } + + let child = if let Some(child) = raw_child { + OwnedRoomId::from_str(&child) + .map_err(|_| CommandError::Error("Invalid room id specified".into()))? + } else { + let msg = "Must specify a room to add"; + return Err(CommandError::Error(msg.into())); + }; + + SpaceAction::SetChild(child, order, suggested).into() + }, _ => return Result::Err(CommandError::InvalidArgument), }; @@ -667,6 +757,11 @@ fn add_iamb_commands(cmds: &mut ProgramCommands) { f: iamb_rooms, }); cmds.add_command(ProgramCommand { name: "room".into(), aliases: vec![], f: iamb_room }); + cmds.add_command(ProgramCommand { + name: "space".into(), + aliases: vec![], + f: iamb_space, + }); cmds.add_command(ProgramCommand { name: "spaces".into(), aliases: vec![], @@ -721,7 +816,7 @@ pub fn setup_commands() -> ProgramCommands { #[cfg(test)] mod tests { use super::*; - use matrix_sdk::ruma::user_id; + use matrix_sdk::ruma::{room_id, user_id}; use modalkit::actions::WindowAction; use modalkit::editing::context::EditContext; @@ -1047,22 +1142,119 @@ mod tests { let mut cmds = setup_commands(); let ctx = EditContext::default(); - let cmd = format!("room notify set mute"); - let res = cmds.input_cmd(&cmd, ctx.clone()).unwrap(); + let cmd = "room notify set mute"; + let res = cmds.input_cmd(cmd, ctx.clone()).unwrap(); let act = RoomAction::Set(RoomField::NotificationMode, "mute".into()); assert_eq!(res, vec![(act.into(), ctx.clone())]); - let cmd = format!("room notify unset"); - let res = cmds.input_cmd(&cmd, ctx.clone()).unwrap(); + let cmd = "room notify unset"; + let res = cmds.input_cmd(cmd, ctx.clone()).unwrap(); let act = RoomAction::Unset(RoomField::NotificationMode); assert_eq!(res, vec![(act.into(), ctx.clone())]); - let cmd = format!("room notify show"); - let res = cmds.input_cmd(&cmd, ctx.clone()).unwrap(); + let cmd = "room notify show"; + let res = cmds.input_cmd(cmd, ctx.clone()).unwrap(); let act = RoomAction::Show(RoomField::NotificationMode); assert_eq!(res, vec![(act.into(), ctx.clone())]); } + #[test] + fn test_cmd_room_id_show() { + let mut cmds = setup_commands(); + let ctx = EditContext::default(); + + let res = cmds.input_cmd("room id show", ctx.clone()).unwrap(); + let act = RoomAction::Show(RoomField::Id); + assert_eq!(res, vec![(act.into(), ctx.clone())]); + + let res = cmds.input_cmd("room id show foo", ctx.clone()); + assert_eq!(res, Err(CommandError::InvalidArgument)); + } + + #[test] + fn test_cmd_space_child() { + let mut cmds = setup_commands(); + let ctx = EditContext::default(); + + let cmd = "space"; + let res = cmds.input_cmd(cmd, ctx.clone()); + assert_eq!(res, Err(CommandError::InvalidArgument)); + + let cmd = "space ++foo bar baz"; + let res = cmds.input_cmd(cmd, ctx.clone()); + assert_eq!(res, Err(CommandError::InvalidArgument)); + + let cmd = "space child foo"; + let res = cmds.input_cmd(cmd, ctx.clone()); + assert_eq!(res, Err(CommandError::InvalidArgument)); + } + + #[test] + fn test_cmd_space_child_set() { + let mut cmds = setup_commands(); + let ctx = EditContext::default(); + + let cmd = "space child set !roomid:example.org"; + let res = cmds.input_cmd(cmd, ctx.clone()).unwrap(); + let act = SpaceAction::SetChild(room_id!("!roomid:example.org").to_owned(), None, false); + assert_eq!(res, vec![(act.into(), ctx.clone())]); + + let cmd = "space child set ++order=abcd ++suggested !roomid:example.org"; + let res = cmds.input_cmd(cmd, ctx.clone()).unwrap(); + let act = SpaceAction::SetChild( + room_id!("!roomid:example.org").to_owned(), + Some("abcd".into()), + true, + ); + assert_eq!(res, vec![(act.into(), ctx.clone())]); + + let cmd = "space child set ++order=abcd ++order=1234 !roomid:example.org"; + let res = cmds.input_cmd(cmd, ctx.clone()); + assert_eq!( + res, + Err(CommandError::Error("Multiple ++order arguments are not allowed".into())) + ); + + let cmd = "space child set !roomid:example.org !otherroom:example.org"; + let res = cmds.input_cmd(cmd, ctx.clone()); + assert_eq!(res, Err(CommandError::Error("Multiple room arguments are not allowed".into()))); + + let cmd = "space child set ++foo=abcd !roomid:example.org"; + let res = cmds.input_cmd(cmd, ctx.clone()); + assert_eq!(res, Err(CommandError::InvalidArgument)); + + let cmd = "space child set ++foo !roomid:example.org"; + let res = cmds.input_cmd(cmd, ctx.clone()); + assert_eq!(res, Err(CommandError::InvalidArgument)); + + let cmd = "space child ++order=abcd ++suggested set !roomid:example.org"; + let res = cmds.input_cmd(cmd, ctx.clone()); + assert_eq!(res, Err(CommandError::InvalidArgument)); + + let cmd = "space child set foo"; + let res = cmds.input_cmd(cmd, ctx.clone()); + assert_eq!(res, Err(CommandError::Error("Invalid room id specified".into()))); + + let cmd = "space child set"; + let res = cmds.input_cmd(cmd, ctx.clone()); + assert_eq!(res, Err(CommandError::Error("Must specify a room to add".into()))); + } + + #[test] + fn test_cmd_space_child_remove() { + let mut cmds = setup_commands(); + let ctx = EditContext::default(); + + let cmd = "space child remove"; + let res = cmds.input_cmd(cmd, ctx.clone()).unwrap(); + let act = SpaceAction::RemoveChild; + assert_eq!(res, vec![(act.into(), ctx.clone())]); + + let cmd = "space child remove foo"; + let res = cmds.input_cmd(cmd, ctx.clone()); + assert_eq!(res, Err(CommandError::InvalidArgument)); + } + #[test] fn test_cmd_invite() { let mut cmds = setup_commands(); diff --git a/src/config.rs b/src/config.rs index 584ac9c..5084dcb 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,16 +1,17 @@ //! # Logic for loading and validating application configuration use std::borrow::Cow; use std::collections::hash_map::DefaultHasher; -use std::collections::HashMap; +use std::collections::{BTreeMap, HashMap}; +use std::env; use std::fmt; use std::fs::File; use std::hash::{Hash, Hasher}; -use std::io::{BufReader, BufWriter}; +use std::io::{BufReader, BufWriter, Write}; use std::path::{Path, PathBuf}; use std::process; use clap::Parser; -use matrix_sdk::matrix_auth::MatrixSession; +use matrix_sdk::authentication::matrix::MatrixSession; use matrix_sdk::ruma::{OwnedDeviceId, OwnedRoomAliasId, OwnedRoomId, OwnedUserId, UserId}; use ratatui::style::{Color, Modifier as StyleModifier, Style}; use ratatui::text::Span; @@ -45,8 +46,9 @@ const DEFAULT_MEMBERS_SORT: [SortColumn; 2] = [ SortColumn(SortFieldUser::UserId, SortOrder::Ascending), ]; -const DEFAULT_ROOM_SORT: [SortColumn; 4] = [ +const DEFAULT_ROOM_SORT: [SortColumn; 5] = [ SortColumn(SortFieldRoom::Favorite, SortOrder::Ascending), + SortColumn(SortFieldRoom::Invite, SortOrder::Ascending), SortColumn(SortFieldRoom::LowPriority, SortOrder::Ascending), SortColumn(SortFieldRoom::Unread, SortOrder::Ascending), SortColumn(SortFieldRoom::Name, SortOrder::Ascending), @@ -97,14 +99,14 @@ fn validate_profile_name(name: &str) -> bool { let mut chars = name.chars(); - if !chars.next().map_or(false, |c| c.is_ascii_alphanumeric()) { + if !chars.next().is_some_and(|c| c.is_ascii_alphanumeric()) { return false; } name.chars().all(is_profile_char) } -fn validate_profile_names(names: &HashMap) { +fn validate_profile_names(names: &BTreeMap) { for name in names.keys() { if validate_profile_name(name.as_str()) { continue; @@ -151,7 +153,7 @@ pub enum ConfigError { pub struct Keys(pub Vec, pub String); pub struct KeysVisitor; -impl<'de> Visitor<'de> for KeysVisitor { +impl Visitor<'_> for KeysVisitor { type Value = Keys; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -182,7 +184,7 @@ impl<'de> Deserialize<'de> for Keys { pub struct VimModes(pub Vec); pub struct VimModesVisitor; -impl<'de> Visitor<'de> for VimModesVisitor { +impl Visitor<'_> for VimModesVisitor { type Value = VimModes; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -232,7 +234,7 @@ impl From for Level { } } -impl<'de> Visitor<'de> for LogLevelVisitor { +impl Visitor<'_> for LogLevelVisitor { type Value = LogLevel; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -267,7 +269,7 @@ impl<'de> Deserialize<'de> for LogLevel { pub struct UserColor(pub Color); pub struct UserColorVisitor; -impl<'de> Visitor<'de> for UserColorVisitor { +impl Visitor<'_> for UserColorVisitor { type Value = UserColor; fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { @@ -321,7 +323,7 @@ pub struct Session { impl From for MatrixSession { fn from(session: Session) -> Self { MatrixSession { - tokens: matrix_sdk::matrix_auth::MatrixSessionTokens { + tokens: matrix_sdk::authentication::matrix::MatrixSessionTokens { access_token: session.access_token, refresh_token: session.refresh_token, }, @@ -352,29 +354,31 @@ pub struct UserDisplayTunables { pub type UserOverrides = HashMap; -fn merge_sorts(a: SortOverrides, b: SortOverrides) -> SortOverrides { +fn merge_sorts(profile: SortOverrides, global: SortOverrides) -> SortOverrides { SortOverrides { - chats: b.chats.or(a.chats), - dms: b.dms.or(a.dms), - rooms: b.rooms.or(a.rooms), - spaces: b.spaces.or(a.spaces), - members: b.members.or(a.members), + chats: profile.chats.or(global.chats), + dms: profile.dms.or(global.dms), + rooms: profile.rooms.or(global.rooms), + spaces: profile.spaces.or(global.spaces), + members: profile.members.or(global.members), } } -fn merge_maps(a: Option>, b: Option>) -> Option> +fn merge_maps( + profile: Option>, + global: Option>, +) -> Option> where K: Eq + Hash, { - match (a, b) { - (Some(a), None) => Some(a), - (None, Some(b)) => Some(b), - (Some(mut a), Some(b)) => { - for (k, v) in b { - a.insert(k, v); + match (global, profile) { + (Some(m), None) | (None, Some(m)) => Some(m), + (Some(mut global), Some(profile)) => { + for (k, v) in profile { + global.insert(k, v); } - Some(a) + Some(global) }, (None, None) => None, } @@ -396,28 +400,84 @@ pub enum UserDisplayStyle { // it can wind up being the Matrix username if there are display name collisions in the room, // in order to avoid any confusion. DisplayName, + + // Acts like Username, except when the username matches given regex, then acts like DisplayName + Regex, } -#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)] -#[serde(rename_all = "lowercase")] -pub enum NotifyVia { +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct NotifyVia { /// Deliver notifications via terminal bell. - Bell, + pub bell: bool, /// Deliver notifications via desktop mechanism. #[cfg(feature = "desktop")] - Desktop, + pub desktop: bool, } +pub struct NotifyViaVisitor; impl Default for NotifyVia { fn default() -> Self { - #[cfg(not(feature = "desktop"))] - return NotifyVia::Bell; - - #[cfg(feature = "desktop")] - return NotifyVia::Desktop; + Self { + bell: cfg!(not(feature = "desktop")), + #[cfg(feature = "desktop")] + desktop: true, + } } } +impl Visitor<'_> for NotifyViaVisitor { + type Value = NotifyVia; + + fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result { + formatter.write_str("a valid notify destination (e.g. \"bell\" or \"desktop\")") + } + + fn visit_str(self, value: &str) -> Result + where + E: SerdeError, + { + let mut via = NotifyVia { + bell: false, + #[cfg(feature = "desktop")] + desktop: false, + }; + + for value in value.split('|') { + match value.to_ascii_lowercase().as_str() { + "bell" => { + via.bell = true; + }, + #[cfg(feature = "desktop")] + "desktop" => { + via.desktop = true; + }, + #[cfg(not(feature = "desktop"))] + "desktop" => { + return Err(E::custom("desktop notification support was compiled out")) + }, + _ => return Err(E::custom("could not parse into a notify destination")), + }; + } + + Ok(via) + } +} + +impl<'de> Deserialize<'de> for NotifyVia { + fn deserialize(deserializer: D) -> Result + where + D: Deserializer<'de>, + { + deserializer.deserialize_str(NotifyViaVisitor) + } +} + +#[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)] +pub struct Mouse { + #[serde(default)] + pub enabled: bool, +} + #[derive(Clone, Debug, Default, Deserialize, Eq, PartialEq)] pub struct Notifications { #[serde(default)] @@ -501,29 +561,35 @@ impl SortOverrides { pub struct TunableValues { pub log_level: Level, pub message_shortcode_display: bool, + pub normal_after_send: bool, pub reaction_display: bool, pub reaction_shortcode_display: bool, pub read_receipt_send: bool, pub read_receipt_display: bool, pub request_timeout: u64, pub sort: SortValues, + pub state_event_display: bool, pub typing_notice_send: bool, pub typing_notice_display: bool, pub users: UserOverrides, pub username_display: UserDisplayStyle, + pub username_display_regex: Option, pub message_user_color: bool, pub default_room: Option, pub open_command: Option>, + pub mouse: Mouse, pub notifications: Notifications, pub image_preview: Option, pub user_gutter_width: usize, pub external_edit_file_suffix: String, + pub tabstop: usize, } #[derive(Clone, Default, Deserialize)] pub struct Tunables { pub log_level: Option, pub message_shortcode_display: Option, + pub normal_after_send: Option, pub reaction_display: Option, pub reaction_shortcode_display: Option, pub read_receipt_send: Option, @@ -531,17 +597,21 @@ pub struct Tunables { pub request_timeout: Option, #[serde(default)] pub sort: SortOverrides, + pub state_event_display: Option, pub typing_notice_send: Option, pub typing_notice_display: Option, pub users: Option, pub username_display: Option, + pub username_display_regex: Option, pub message_user_color: Option, pub default_room: Option, pub open_command: Option>, + pub mouse: Option, pub notifications: Option, pub image_preview: Option, pub user_gutter_width: Option, pub external_edit_file_suffix: Option, + pub tabstop: Option, } impl Tunables { @@ -551,6 +621,7 @@ impl Tunables { message_shortcode_display: self .message_shortcode_display .or(other.message_shortcode_display), + normal_after_send: self.normal_after_send.or(other.normal_after_send), reaction_display: self.reaction_display.or(other.reaction_display), reaction_shortcode_display: self .reaction_shortcode_display @@ -559,19 +630,23 @@ impl Tunables { read_receipt_display: self.read_receipt_display.or(other.read_receipt_display), request_timeout: self.request_timeout.or(other.request_timeout), sort: merge_sorts(self.sort, other.sort), + state_event_display: self.state_event_display.or(other.state_event_display), typing_notice_send: self.typing_notice_send.or(other.typing_notice_send), typing_notice_display: self.typing_notice_display.or(other.typing_notice_display), users: merge_maps(self.users, other.users), username_display: self.username_display.or(other.username_display), + username_display_regex: self.username_display_regex.or(other.username_display_regex), message_user_color: self.message_user_color.or(other.message_user_color), default_room: self.default_room.or(other.default_room), open_command: self.open_command.or(other.open_command), + mouse: self.mouse.or(other.mouse), notifications: self.notifications.or(other.notifications), image_preview: self.image_preview.or(other.image_preview), user_gutter_width: self.user_gutter_width.or(other.user_gutter_width), external_edit_file_suffix: self .external_edit_file_suffix .or(other.external_edit_file_suffix), + tabstop: self.tabstop.or(other.tabstop), } } @@ -579,25 +654,30 @@ impl Tunables { TunableValues { log_level: self.log_level.map(Level::from).unwrap_or(Level::INFO), message_shortcode_display: self.message_shortcode_display.unwrap_or(false), + normal_after_send: self.normal_after_send.unwrap_or(false), reaction_display: self.reaction_display.unwrap_or(true), reaction_shortcode_display: self.reaction_shortcode_display.unwrap_or(false), read_receipt_send: self.read_receipt_send.unwrap_or(true), read_receipt_display: self.read_receipt_display.unwrap_or(true), request_timeout: self.request_timeout.unwrap_or(DEFAULT_REQ_TIMEOUT), sort: self.sort.values(), + state_event_display: self.state_event_display.unwrap_or(true), typing_notice_send: self.typing_notice_send.unwrap_or(true), typing_notice_display: self.typing_notice_display.unwrap_or(true), users: self.users.unwrap_or_default(), username_display: self.username_display.unwrap_or_default(), + username_display_regex: self.username_display_regex, message_user_color: self.message_user_color.unwrap_or(false), default_room: self.default_room, open_command: self.open_command, + mouse: self.mouse.unwrap_or_default(), notifications: self.notifications.unwrap_or_default(), image_preview: self.image_preview.map(ImagePreview::values), user_gutter_width: self.user_gutter_width.unwrap_or(30), external_edit_file_suffix: self .external_edit_file_suffix .unwrap_or_else(|| ".md".to_string()), + tabstop: self.tabstop.unwrap_or(4), } } } @@ -729,7 +809,7 @@ pub struct ProfileConfig { #[derive(Clone, Deserialize)] pub struct IambConfig { - pub profiles: HashMap, + pub profiles: BTreeMap, pub default_profile: Option, pub settings: Option, pub dirs: Option, @@ -769,14 +849,22 @@ pub struct ApplicationSettings { } impl ApplicationSettings { + fn get_xdg_config_home() -> Option { + env::var("XDG_CONFIG_HOME").ok().map(PathBuf::from) + } + pub fn load(cli: Iamb) -> Result> { - let mut config_dir = cli.config_directory.or_else(dirs::config_dir).unwrap_or_else(|| { - usage!( - "No user configuration directory found;\ - please specify one via -C.\n\n - For more information try '--help'" - ); - }); + let mut config_dir = cli + .config_directory + .or_else(Self::get_xdg_config_home) + .or_else(dirs::config_dir) + .unwrap_or_else(|| { + usage!( + "No user configuration directory found;\ + please specify one via -C.\n\n + For more information try '--help'" + ); + }); config_dir.push("iamb"); let config_json = config_dir.join("config.json"); @@ -816,14 +904,36 @@ impl ApplicationSettings { } else if profiles.len() == 1 { profiles.into_iter().next().unwrap() } else { - usage!( - "No profile specified. \ - Please use -P or add \"default_profile\" to your configuration.\n\n\ - For more information try '--help'", - ); + loop { + println!("\nNo profile specified. Available profiles:"); + profiles + .keys() + .enumerate() + .for_each(|(i, name)| println!("{}: {}", i, name)); + + print!("Select a number or 'q' to quit: "); + let _ = std::io::stdout().flush(); + + let mut input = String::new(); + let _ = std::io::stdin().read_line(&mut input); + + if input.trim() == "q" { + usage!( + "No profile specified. \ + Please use -P or add \"default_profile\" to your configuration.\n\n\ + For more information try '--help'", + ); + } + if let Ok(i) = input.trim().parse::() { + if i < profiles.len() { + break profiles.into_iter().nth(i).unwrap(); + } + } + println!("\nInvalid index."); + } }; - let macros = merge_maps(macros, profile.macros.take()).unwrap_or_default(); + let macros = merge_maps(profile.macros.take(), macros).unwrap_or_default(); let layout = profile.layout.take().or(layout).unwrap_or_default(); let tunables = global.unwrap_or_default(); @@ -898,7 +1008,7 @@ impl ApplicationSettings { Ok(()) } - pub fn get_user_char_span<'a>(&self, user_id: &'a UserId) -> Span<'a> { + pub fn get_user_char_span(&self, user_id: &UserId) -> Span { let (color, c) = self .tunables .users @@ -958,6 +1068,20 @@ impl ApplicationSettings { Cow::Borrowed(user_id.as_str()) } }, + (None, UserDisplayStyle::Regex) => { + let re = regex::Regex::new( + &self.tunables.username_display_regex.clone().unwrap_or("*".into()), + ) + .unwrap(); + + if !re.is_match(user_id.as_str()) { + Cow::Borrowed(user_id.as_str()) + } else if let Some(display) = info.display_names.get(user_id) { + Cow::Borrowed(display.as_str()) + } else { + Cow::Borrowed(user_id.as_str()) + } + }, }; Span::styled(name, style) @@ -1022,10 +1146,10 @@ mod tests { assert_eq!(res, Some(b.clone())); let res = merge_maps(Some(b.clone()), Some(c.clone())); - assert_eq!(res, Some(c.clone())); + assert_eq!(res, Some(b.clone())); let res = merge_maps(Some(c.clone()), Some(b.clone())); - assert_eq!(res, Some(b.clone())); + assert_eq!(res, Some(c.clone())); } #[test] @@ -1074,6 +1198,13 @@ mod tests { let res: Tunables = serde_json::from_str("{\"username_display\": \"displayname\"}").unwrap(); assert_eq!(res.username_display, Some(UserDisplayStyle::DisplayName)); + + let res: Tunables = serde_json::from_str( + "{\"username_display\": \"regex\",\n\"username_display_regex\": \"foo\"}", + ) + .unwrap(); + assert_eq!(res.username_display, Some(UserDisplayStyle::Regex)); + assert_eq!(res.username_display_regex.unwrap_or("FAILED".into()), "foo".to_string()); } #[test] @@ -1189,6 +1320,29 @@ mod tests { assert_eq!(run, &exp); } + #[test] + fn test_parse_notify_via() { + assert_eq!(NotifyVia { bell: false, desktop: true }, NotifyVia::default()); + assert_eq!( + NotifyVia { bell: false, desktop: true }, + serde_json::from_str(r#""desktop""#).unwrap() + ); + assert_eq!( + NotifyVia { bell: true, desktop: false }, + serde_json::from_str(r#""bell""#).unwrap() + ); + assert_eq!( + NotifyVia { bell: true, desktop: true }, + serde_json::from_str(r#""bell|desktop""#).unwrap() + ); + assert_eq!( + NotifyVia { bell: true, desktop: true }, + serde_json::from_str(r#""desktop|bell""#).unwrap() + ); + assert!(serde_json::from_str::(r#""other""#).is_err()); + assert!(serde_json::from_str::(r#""""#).is_err()); + } + #[test] fn test_load_example_config_toml() { let path = PathBuf::from("config.example.toml"); diff --git a/src/main.rs b/src/main.rs index d0b265c..cee2247 100644 --- a/src/main.rs +++ b/src/main.rs @@ -44,11 +44,14 @@ use modalkit::crossterm::{ read, DisableBracketedPaste, DisableFocusChange, + DisableMouseCapture, EnableBracketedPaste, EnableFocusChange, + EnableMouseCapture, Event, KeyEventKind, KeyboardEnhancementFlags, + MouseEventKind, PopKeyboardEnhancementFlags, PushKeyboardEnhancementFlags, }, @@ -59,7 +62,7 @@ use modalkit::crossterm::{ use ratatui::{ backend::CrosstermBackend, layout::Rect, - style::{Color, Style}, + style::{Color, Modifier, Style}, text::Span, widgets::Paragraph, Terminal, @@ -86,6 +89,7 @@ use crate::{ ChatStore, HomeserverAction, IambAction, + IambCompleter, IambError, IambId, IambInfo, @@ -310,7 +314,7 @@ impl Application { } term.draw(|f| { - let area = f.size(); + let area = f.area(); let modestr = bindings.show_mode(); let cursor = bindings.get_cursor_indicator(); @@ -324,6 +328,9 @@ impl Application { .show_dialog(dialogstr) .show_mode(modestr) .borders(true) + .border_style(Style::default().add_modifier(Modifier::DIM)) + .tab_style(Style::default().add_modifier(Modifier::DIM)) + .tab_style_focused(Style::default().remove_modifier(Modifier::DIM)) .focus(focused); f.render_stateful_widget(screen, area, sstate); @@ -339,7 +346,7 @@ impl Application { let inner = Rect::new(cx, cy, 1, 1); f.render_widget(para, inner) } - f.set_cursor(cx, cy); + f.set_cursor_position((cx, cy)); } })?; @@ -364,8 +371,30 @@ impl Application { return Ok(ke.into()); }, - Event::Mouse(_) => { - // Do nothing for now. + Event::Mouse(me) => { + let dir = match me.kind { + MouseEventKind::ScrollUp => MoveDir2D::Up, + MouseEventKind::ScrollDown => MoveDir2D::Down, + MouseEventKind::ScrollLeft => MoveDir2D::Left, + MouseEventKind::ScrollRight => MoveDir2D::Right, + _ => continue, + }; + + let size = ScrollSize::Cell; + let style = ScrollStyle::Direction2D(dir, size, 1.into()); + let ctx = ProgramContext::default(); + let mut store = self.store.lock().await; + + match self.screen.scroll(&style, &ctx, store.deref_mut()) { + Ok(None) => {}, + Ok(Some(info)) => { + drop(store); + self.handle_info(info); + }, + Err(e) => { + self.screen.push_error(e); + }, + } }, Event::FocusGained => { let mut store = self.store.lock().await; @@ -504,7 +533,7 @@ impl Application { }, // Unimplemented. - Action::KeywordLookup => { + Action::KeywordLookup(_) => { // XXX: implement None }, @@ -532,9 +561,12 @@ impl Application { IambAction::ClearUnreads => { let user_id = &store.application.settings.profile.user_id; + // Clear any notifications we displayed: + store.application.open_notifications.clear(); + for room_id in store.application.sync_info.chats() { if let Some(room) = store.application.rooms.get_mut(room_id) { - room.fully_read(user_id.clone()); + room.fully_read(user_id); } } @@ -557,6 +589,9 @@ impl Application { IambAction::Message(act) => { self.screen.current_window_mut()?.message_command(act, ctx, store).await? }, + IambAction::Space(act) => { + self.screen.current_window_mut()?.space_command(act, ctx, store).await? + }, IambAction::Room(act) => { let acts = self.screen.current_window_mut()?.room_command(act, ctx, store).await?; self.action_prepend(acts); @@ -564,6 +599,9 @@ impl Application { None }, IambAction::Send(act) => { + if store.application.settings.tunables.normal_after_send { + self.bindings.reset_mode(); + } self.screen.current_window_mut()?.send_command(act, ctx, store).await? }, @@ -847,7 +885,7 @@ async fn check_import_keys( let encrypted = match encrypt_room_key_export(&keys, &passphrase, 500000) { Ok(encrypted) => encrypted, Err(e) => { - format!("* Failed to encrypt room keys during export: {e}"); + println!("* Failed to encrypt room keys during export: {e}"); process::exit(2); }, }; @@ -929,8 +967,8 @@ async fn login_normal( } /// Set up the terminal for drawing the TUI, and getting additional info. -fn setup_tty(title: &str, enable_enhanced_keys: bool) -> std::io::Result<()> { - let title = format!("iamb ({})", title); +fn setup_tty(settings: &ApplicationSettings, enable_enhanced_keys: bool) -> std::io::Result<()> { + let title = format!("iamb ({})", settings.profile.user_id.as_str()); // Enable raw mode and enter the alternate screen. crossterm::terminal::enable_raw_mode()?; @@ -944,15 +982,23 @@ fn setup_tty(title: &str, enable_enhanced_keys: bool) -> std::io::Result<()> { )?; } + if settings.tunables.mouse.enabled { + crossterm::execute!(stdout(), EnableMouseCapture)?; + } + crossterm::execute!(stdout(), EnableBracketedPaste, EnableFocusChange, SetTitle(title)) } // Do our best to reverse what we did in setup_tty() when we exit or crash. -fn restore_tty(enable_enhanced_keys: bool) { +fn restore_tty(enable_enhanced_keys: bool, enable_mouse: bool) { if enable_enhanced_keys { let _ = crossterm::queue!(stdout(), PopKeyboardEnhancementFlags); } + if enable_mouse { + let _ = crossterm::queue!(stdout(), DisableMouseCapture); + } + let _ = crossterm::execute!( stdout(), DisableBracketedPaste, @@ -975,7 +1021,9 @@ async fn run(settings: ApplicationSettings) -> IambResult<()> { // Set up the async worker thread and global store. let worker = ClientWorker::spawn(client.clone(), settings.clone()).await; let store = ChatStore::new(worker.clone(), settings.clone()); - let store = Store::new(store); + let mut store = Store::new(store); + store.completer = Box::new(IambCompleter); + let store = Arc::new(AsyncMutex::new(store)); worker.init(store.clone()); @@ -1006,11 +1054,12 @@ async fn run(settings: ApplicationSettings) -> IambResult<()> { false }, }; - setup_tty(settings.profile.user_id.as_str(), enable_enhanced_keys)?; + setup_tty(&settings, enable_enhanced_keys)?; let orig_hook = std::panic::take_hook(); + let enable_mouse = settings.tunables.mouse.enabled; std::panic::set_hook(Box::new(move |panic_info| { - restore_tty(enable_enhanced_keys); + restore_tty(enable_enhanced_keys, enable_mouse); orig_hook(panic_info); process::exit(1); })); @@ -1020,7 +1069,7 @@ async fn run(settings: ApplicationSettings) -> IambResult<()> { application.run().await?; // Clean up the terminal on exit. - restore_tty(enable_enhanced_keys); + restore_tty(enable_enhanced_keys, enable_mouse); Ok(()) } diff --git a/src/message/html.rs b/src/message/html.rs index ff62aae..d2b40ae 100644 --- a/src/message/html.rs +++ b/src/message/html.rs @@ -10,10 +10,12 @@ //! //! This isn't as important for iamb, since it isn't a browser environment, but we do still map //! input onto an enum of the safe list of tags to keep it easy to understand and process. +use std::borrow::Cow; use std::ops::Deref; use css_color_parser::Color as CssColor; use markup5ever_rcdom::{Handle, NodeData, RcDom}; +use matrix_sdk::ruma::{OwnedRoomAliasId, OwnedRoomId, OwnedUserId}; use unicode_segmentation::UnicodeSegmentation; use url::Url; @@ -34,10 +36,13 @@ use ratatui::{ }; use crate::{ + config::ApplicationSettings, message::printer::TextPrinter, util::{join_cell_text, space_text}, }; +const QUOTE_COLOR: Color = Color::Indexed(236); + /// Generate bullet points from a [ListStyle]. pub struct BulletIterator { style: ListStyle, @@ -148,7 +153,12 @@ impl Table { } } - fn to_text(&self, width: usize, style: Style, emoji_shortcodes: bool) -> Text { + fn to_text<'a>( + &'a self, + width: usize, + style: Style, + settings: &'a ApplicationSettings, + ) -> Text<'a> { let mut text = Text::default(); let columns = self.columns(); let cell_total = width.saturating_sub(columns).saturating_sub(1); @@ -167,7 +177,7 @@ impl Table { if let Some(caption) = &self.caption { let subw = width.saturating_sub(6); let mut printer = - TextPrinter::new(subw, style, true, emoji_shortcodes).align(Alignment::Center); + TextPrinter::new(subw, style, true, settings).align(Alignment::Center); caption.print(&mut printer, style); for mut line in printer.finish().lines { @@ -214,7 +224,7 @@ impl Table { CellType::Data => style, }; - cell.to_text(*w, style, emoji_shortcodes) + cell.to_text(*w, style, settings) } else { space_text(*w, style) }; @@ -271,13 +281,22 @@ pub enum StyleTreeNode { Ruler, Style(Box, Style), Table(Table), - Text(String), + Text(Cow<'static, str>), Sequence(StyleTreeChildren), + RoomAlias(OwnedRoomAliasId), + RoomId(OwnedRoomId), + UserId(OwnedUserId), + DisplayName(String, OwnedUserId), } impl StyleTreeNode { - pub fn to_text(&self, width: usize, style: Style, emoji_shortcodes: bool) -> Text { - let mut printer = TextPrinter::new(width, style, true, emoji_shortcodes); + pub fn to_text<'a>( + &'a self, + width: usize, + style: Style, + settings: &'a ApplicationSettings, + ) -> Text<'a> { + let mut printer = TextPrinter::new(width, style, true, settings); self.print(&mut printer, style); printer.finish() } @@ -312,6 +331,12 @@ impl StyleTreeNode { StyleTreeNode::Ruler => {}, StyleTreeNode::Text(_) => {}, StyleTreeNode::Break => {}, + + // TODO: eventually these should turn into internal links: + StyleTreeNode::UserId(_) => {}, + StyleTreeNode::RoomId(_) => {}, + StyleTreeNode::RoomAlias(_) => {}, + StyleTreeNode::DisplayName(_, _) => {}, } } @@ -328,11 +353,14 @@ impl StyleTreeNode { printer.push_span_nobreak(span); }, StyleTreeNode::Blockquote(child) => { - let mut subp = printer.sub(4); + let mut subp = printer.sub(3); child.print(&mut subp, style); for mut line in subp.finish() { - line.spans.insert(0, Span::styled(" ", style)); + line.spans.insert(0, Span::styled(" ", style)); + line.spans + .insert(0, Span::styled(line::THICK_VERTICAL, style.fg(QUOTE_COLOR))); + line.spans.insert(0, Span::styled(" ", style)); printer.push_line(line); } }, @@ -430,14 +458,14 @@ impl StyleTreeNode { } }, StyleTreeNode::Table(table) => { - let text = table.to_text(width, style, printer.emoji_shortcodes()); + let text = table.to_text(width, style, printer.settings); printer.push_text(text); }, StyleTreeNode::Break => { printer.push_break(); }, StyleTreeNode::Text(s) => { - printer.push_str(s.as_str(), style); + printer.push_str(s.as_ref(), style); }, StyleTreeNode::Style(child, patch) => child.print(printer, style.patch(*patch)), @@ -446,13 +474,30 @@ impl StyleTreeNode { child.print(printer, style); } }, + + StyleTreeNode::UserId(user_id) => { + let style = printer.settings().get_user_style(user_id); + printer.push_str(user_id.as_str(), style); + }, + StyleTreeNode::DisplayName(display_name, user_id) => { + let style = printer.settings().get_user_style(user_id); + printer.push_str(display_name.as_str(), style); + }, + StyleTreeNode::RoomId(room_id) => { + let bold = style.add_modifier(StyleModifier::BOLD); + printer.push_str(room_id.as_str(), bold); + }, + StyleTreeNode::RoomAlias(alias) => { + let bold = style.add_modifier(StyleModifier::BOLD); + printer.push_str(alias.as_str(), bold); + }, } } } /// A processed HTML document. pub struct StyleTree { - children: StyleTreeChildren, + pub(super) children: StyleTreeChildren, } impl StyleTree { @@ -466,14 +511,14 @@ impl StyleTree { return links; } - pub fn to_text( - &self, + pub fn to_text<'a>( + &'a self, width: usize, style: Style, hide_reply: bool, - emoji_shortcodes: bool, - ) -> Text<'_> { - let mut printer = TextPrinter::new(width, style, hide_reply, emoji_shortcodes); + settings: &'a ApplicationSettings, + ) -> Text<'a> { + let mut printer = TextPrinter::new(width, style, hide_reply, settings); for child in self.children.iter() { child.print(&mut printer, style); @@ -484,11 +529,11 @@ impl StyleTree { } pub struct TreeGenState { - link_num: u8, + pub link_num: u8, } impl TreeGenState { - fn next_link_char(&mut self) -> Option { + pub fn next_link_char(&mut self) -> Option { let num = self.link_num; if num < 62 { @@ -661,7 +706,7 @@ fn h2t(hdl: &Handle, state: &mut TreeGenState) -> StyleTreeChildren { let tree = match &node.data { NodeData::Document => *c2t(node.children.borrow().as_slice(), state), - NodeData::Text { contents } => StyleTreeNode::Text(contents.borrow().to_string()), + NodeData::Text { contents } => StyleTreeNode::Text(contents.borrow().to_string().into()), NodeData::Element { name, attrs, .. } => { match name.local.as_ref() { // Message that this one replies to. @@ -708,7 +753,7 @@ fn h2t(hdl: &Handle, state: &mut TreeGenState) -> StyleTreeChildren { StyleTreeNode::Style(c, s) }, - "del" | "strike" => { + "del" | "s" | "strike" => { let c = c2t(&node.children.borrow(), state); let s = Style::default().add_modifier(StyleModifier::CROSSED_OUT); @@ -811,17 +856,19 @@ pub fn parse_matrix_html(s: &str) -> StyleTree { #[cfg(test)] pub mod tests { use super::*; + use crate::tests::mock_settings; use crate::util::space_span; use pretty_assertions::assert_eq; use unicode_width::UnicodeWidthStr; #[test] fn test_header() { + let settings = mock_settings(); let bold = Style::default().add_modifier(StyleModifier::BOLD); let s = "

Header 1

"; let tree = parse_matrix_html(s); - let text = tree.to_text(20, Style::default(), false, false); + let text = tree.to_text(20, Style::default(), false, &settings); assert_eq!(text.lines, vec![Line::from(vec![ Span::styled("#", bold), Span::styled(" ", bold), @@ -833,7 +880,7 @@ pub mod tests { let s = "

Header 2

"; let tree = parse_matrix_html(s); - let text = tree.to_text(20, Style::default(), false, false); + let text = tree.to_text(20, Style::default(), false, &settings); assert_eq!(text.lines, vec![Line::from(vec![ Span::styled("#", bold), Span::styled("#", bold), @@ -846,7 +893,7 @@ pub mod tests { let s = "

Header 3

"; let tree = parse_matrix_html(s); - let text = tree.to_text(20, Style::default(), false, false); + let text = tree.to_text(20, Style::default(), false, &settings); assert_eq!(text.lines, vec![Line::from(vec![ Span::styled("#", bold), Span::styled("#", bold), @@ -860,7 +907,7 @@ pub mod tests { let s = "

Header 4

"; let tree = parse_matrix_html(s); - let text = tree.to_text(20, Style::default(), false, false); + let text = tree.to_text(20, Style::default(), false, &settings); assert_eq!(text.lines, vec![Line::from(vec![ Span::styled("#", bold), Span::styled("#", bold), @@ -875,7 +922,7 @@ pub mod tests { let s = "
Header 5
"; let tree = parse_matrix_html(s); - let text = tree.to_text(20, Style::default(), false, false); + let text = tree.to_text(20, Style::default(), false, &settings); assert_eq!(text.lines, vec![Line::from(vec![ Span::styled("#", bold), Span::styled("#", bold), @@ -891,7 +938,7 @@ pub mod tests { let s = "
Header 6
"; let tree = parse_matrix_html(s); - let text = tree.to_text(20, Style::default(), false, false); + let text = tree.to_text(20, Style::default(), false, &settings); assert_eq!(text.lines, vec![Line::from(vec![ Span::styled("#", bold), Span::styled("#", bold), @@ -909,6 +956,7 @@ pub mod tests { #[test] fn test_style() { + let settings = mock_settings(); let def = Style::default(); let bold = def.add_modifier(StyleModifier::BOLD); let italic = def.add_modifier(StyleModifier::ITALIC); @@ -918,7 +966,7 @@ pub mod tests { let s = "Bold!"; let tree = parse_matrix_html(s); - let text = tree.to_text(20, Style::default(), false, false); + let text = tree.to_text(20, Style::default(), false, &settings); assert_eq!(text.lines, vec![Line::from(vec![ Span::styled("Bold", bold), Span::styled("!", bold), @@ -927,7 +975,7 @@ pub mod tests { let s = "Bold!"; let tree = parse_matrix_html(s); - let text = tree.to_text(20, Style::default(), false, false); + let text = tree.to_text(20, Style::default(), false, &settings); assert_eq!(text.lines, vec![Line::from(vec![ Span::styled("Bold", bold), Span::styled("!", bold), @@ -936,7 +984,7 @@ pub mod tests { let s = "Italic!"; let tree = parse_matrix_html(s); - let text = tree.to_text(20, Style::default(), false, false); + let text = tree.to_text(20, Style::default(), false, &settings); assert_eq!(text.lines, vec![Line::from(vec![ Span::styled("Italic", italic), Span::styled("!", italic), @@ -945,7 +993,7 @@ pub mod tests { let s = "Italic!"; let tree = parse_matrix_html(s); - let text = tree.to_text(20, Style::default(), false, false); + let text = tree.to_text(20, Style::default(), false, &settings); assert_eq!(text.lines, vec![Line::from(vec![ Span::styled("Italic", italic), Span::styled("!", italic), @@ -954,7 +1002,7 @@ pub mod tests { let s = "Strikethrough!"; let tree = parse_matrix_html(s); - let text = tree.to_text(20, Style::default(), false, false); + let text = tree.to_text(20, Style::default(), false, &settings); assert_eq!(text.lines, vec![Line::from(vec![ Span::styled("Strikethrough", strike), Span::styled("!", strike), @@ -963,7 +1011,7 @@ pub mod tests { let s = "Strikethrough!"; let tree = parse_matrix_html(s); - let text = tree.to_text(20, Style::default(), false, false); + let text = tree.to_text(20, Style::default(), false, &settings); assert_eq!(text.lines, vec![Line::from(vec![ Span::styled("Strikethrough", strike), Span::styled("!", strike), @@ -972,7 +1020,7 @@ pub mod tests { let s = "Underline!"; let tree = parse_matrix_html(s); - let text = tree.to_text(20, Style::default(), false, false); + let text = tree.to_text(20, Style::default(), false, &settings); assert_eq!(text.lines, vec![Line::from(vec![ Span::styled("Underline", underl), Span::styled("!", underl), @@ -981,7 +1029,7 @@ pub mod tests { let s = "Red!"; let tree = parse_matrix_html(s); - let text = tree.to_text(20, Style::default(), false, false); + let text = tree.to_text(20, Style::default(), false, &settings); assert_eq!(text.lines, vec![Line::from(vec![ Span::styled("Red", red), Span::styled("!", red), @@ -990,7 +1038,7 @@ pub mod tests { let s = "Red!"; let tree = parse_matrix_html(s); - let text = tree.to_text(20, Style::default(), false, false); + let text = tree.to_text(20, Style::default(), false, &settings); assert_eq!(text.lines, vec![Line::from(vec![ Span::styled("Red", red), Span::styled("!", red), @@ -1000,9 +1048,10 @@ pub mod tests { #[test] fn test_paragraph() { + let settings = mock_settings(); let s = "

Hello world!

Content

Goodbye world!

"; let tree = parse_matrix_html(s); - let text = tree.to_text(10, Style::default(), false, false); + let text = tree.to_text(10, Style::default(), false, &settings); assert_eq!(text.lines.len(), 7); assert_eq!( text.lines[0], @@ -1027,25 +1076,42 @@ pub mod tests { #[test] fn test_blockquote() { + let settings = mock_settings(); let s = "
Hello world!
"; let tree = parse_matrix_html(s); - let text = tree.to_text(10, Style::default(), false, false); + let text = tree.to_text(10, Style::default(), false, &settings); + let style = Style::new().fg(QUOTE_COLOR); assert_eq!(text.lines.len(), 2); assert_eq!( text.lines[0], - Line::from(vec![Span::raw(" "), Span::raw("Hello"), Span::raw(" ")]) + Line::from(vec![ + Span::raw(" "), + Span::styled(line::THICK_VERTICAL, style), + Span::raw(" "), + Span::raw("Hello"), + Span::raw(" "), + Span::raw(" "), + ]) ); assert_eq!( text.lines[1], - Line::from(vec![Span::raw(" "), Span::raw("world"), Span::raw("!")]) + Line::from(vec![ + Span::raw(" "), + Span::styled(line::THICK_VERTICAL, style), + Span::raw(" "), + Span::raw("world"), + Span::raw("!"), + Span::raw(" "), + ]) ); } #[test] fn test_list_unordered() { + let settings = mock_settings(); let s = "
  • List Item 1
  • List Item 2
  • List Item 3
"; let tree = parse_matrix_html(s); - let text = tree.to_text(8, Style::default(), false, false); + let text = tree.to_text(8, Style::default(), false, &settings); assert_eq!(text.lines.len(), 6); assert_eq!( text.lines[0], @@ -1105,9 +1171,10 @@ pub mod tests { #[test] fn test_list_ordered() { + let settings = mock_settings(); let s = "
  1. List Item 1
  2. List Item 2
  3. List Item 3
"; let tree = parse_matrix_html(s); - let text = tree.to_text(9, Style::default(), false, false); + let text = tree.to_text(9, Style::default(), false, &settings); assert_eq!(text.lines.len(), 6); assert_eq!( text.lines[0], @@ -1167,6 +1234,7 @@ pub mod tests { #[test] fn test_table() { + let settings = mock_settings(); let s = "\ \ @@ -1177,7 +1245,7 @@ pub mod tests { \
Column 1Column 2Column 3
abc
"; let tree = parse_matrix_html(s); - let text = tree.to_text(15, Style::default(), false, false); + let text = tree.to_text(15, Style::default(), false, &settings); let bold = Style::default().add_modifier(StyleModifier::BOLD); assert_eq!(text.lines.len(), 11); @@ -1267,10 +1335,11 @@ pub mod tests { #[test] fn test_matrix_reply() { + let settings = mock_settings(); let s = "This was replied toThis is the reply"; let tree = parse_matrix_html(s); - let text = tree.to_text(10, Style::default(), false, false); + let text = tree.to_text(10, Style::default(), false, &settings); assert_eq!(text.lines.len(), 4); assert_eq!( text.lines[0], @@ -1307,7 +1376,7 @@ pub mod tests { ); let tree = parse_matrix_html(s); - let text = tree.to_text(10, Style::default(), true, false); + let text = tree.to_text(10, Style::default(), true, &settings); assert_eq!(text.lines.len(), 2); assert_eq!( text.lines[0], @@ -1332,9 +1401,10 @@ pub mod tests { #[test] fn test_self_closing() { + let settings = mock_settings(); let s = "Hello
World
Goodbye"; let tree = parse_matrix_html(s); - let text = tree.to_text(7, Style::default(), true, false); + let text = tree.to_text(7, Style::default(), true, &settings); assert_eq!(text.lines.len(), 3); assert_eq!(text.lines[0], Line::from(vec![Span::raw("Hello"), Span::raw(" "),])); assert_eq!(text.lines[1], Line::from(vec![Span::raw("World"), Span::raw(" "),])); @@ -1343,9 +1413,10 @@ pub mod tests { #[test] fn test_embedded_newline() { + let settings = mock_settings(); let s = "

Hello\nWorld

"; let tree = parse_matrix_html(s); - let text = tree.to_text(15, Style::default(), true, false); + let text = tree.to_text(15, Style::default(), true, &settings); assert_eq!(text.lines.len(), 1); assert_eq!( text.lines[0], @@ -1360,16 +1431,18 @@ pub mod tests { #[test] fn test_pre_tag() { + let settings = mock_settings(); let s = concat!( "
",
             "fn hello() -> usize {\n",
+            " \t// weired\n",
             "    return 5;\n",
             "}\n",
             "
\n" ); let tree = parse_matrix_html(s); - let text = tree.to_text(25, Style::default(), true, false); - assert_eq!(text.lines.len(), 5); + let text = tree.to_text(25, Style::default(), true, &settings); + assert_eq!(text.lines.len(), 6); assert_eq!( text.lines[0], Line::from(vec![ @@ -1400,6 +1473,20 @@ pub mod tests { ); assert_eq!( text.lines[2], + Line::from(vec![ + Span::raw(line::VERTICAL), + Span::raw(" "), + Span::raw(" "), + Span::raw("/"), + Span::raw("/"), + Span::raw(" "), + Span::raw("weired"), + Span::raw(" "), + Span::raw(line::VERTICAL) + ]) + ); + assert_eq!( + text.lines[3], Line::from(vec![ Span::raw(line::VERTICAL), Span::raw(" "), @@ -1412,7 +1499,7 @@ pub mod tests { ]) ); assert_eq!( - text.lines[3], + text.lines[4], Line::from(vec![ Span::raw(line::VERTICAL), Span::raw("}"), @@ -1421,7 +1508,7 @@ pub mod tests { ]) ); assert_eq!( - text.lines[4], + text.lines[5], Line::from(vec![ Span::raw(line::BOTTOM_LEFT), Span::raw(line::HORIZONTAL.repeat(23)), @@ -1432,6 +1519,11 @@ pub mod tests { #[test] fn test_emoji_shortcodes() { + let mut enabled = mock_settings(); + enabled.tunables.message_shortcode_display = true; + let mut disabled = mock_settings(); + disabled.tunables.message_shortcode_display = false; + for shortcode in ["exploding_head", "polar_bear", "canada"] { let emoji = emojis::get_by_shortcode(shortcode).unwrap().as_str(); let emoji_width = UnicodeWidthStr::width(emoji); @@ -1440,13 +1532,13 @@ pub mod tests { let s = format!("

{emoji}

"); let tree = parse_matrix_html(s.as_str()); // Test with emojis_shortcodes set to false - let text = tree.to_text(20, Style::default(), false, false); + let text = tree.to_text(20, Style::default(), false, &disabled); assert_eq!(text.lines, vec![Line::from(vec![ Span::raw(emoji), space_span(20 - emoji_width, Style::default()), ]),]); // Test with emojis_shortcodes set to true - let text = tree.to_text(20, Style::default(), false, true); + let text = tree.to_text(20, Style::default(), false, &enabled); assert_eq!(text.lines, vec![Line::from(vec![ Span::raw(replacement.as_str()), space_span(20 - replacement_width, Style::default()), diff --git a/src/message/mod.rs b/src/message/mod.rs index d266a25..34f80b1 100644 --- a/src/message/mod.rs +++ b/src/message/mod.rs @@ -2,15 +2,15 @@ use std::borrow::Cow; use std::cmp::{Ord, Ordering, PartialOrd}; use std::collections::hash_map::DefaultHasher; -use std::collections::hash_set; use std::collections::BTreeMap; use std::convert::{TryFrom, TryInto}; use std::fmt::{self, Display}; use std::hash::{Hash, Hasher}; use std::ops::{Deref, DerefMut}; -use chrono::{DateTime, Local as LocalTz, NaiveDateTime, TimeZone}; +use chrono::{DateTime, Local as LocalTz}; use humansize::{format_size, DECIMAL}; +use matrix_sdk::ruma::events::receipt::ReceiptThread; use serde_json::json; use unicode_width::UnicodeWidthStr; @@ -35,6 +35,7 @@ use matrix_sdk::ruma::{ }, redaction::SyncRoomRedactionEvent, }, + AnySyncStateEvent, RedactContent, RedactedUnsigned, }, @@ -67,13 +68,17 @@ use crate::{ mod compose; mod html; mod printer; +mod state; pub use self::compose::text_to_message; +use self::state::{body_cow_state, html_state}; +pub use html::TreeGenState; + +type ProtocolPreview<'a> = (&'a Protocol, u16, u16); pub type MessageKey = (MessageTimeStamp, OwnedEventId); -#[derive(Default)] -pub struct Messages(BTreeMap); +pub struct Messages(BTreeMap, pub ReceiptThread); impl Deref for Messages { type Target = BTreeMap; @@ -90,6 +95,18 @@ impl DerefMut for Messages { } impl Messages { + pub fn new(thread: ReceiptThread) -> Self { + Self(Default::default(), thread) + } + + pub fn main() -> Self { + Self::new(ReceiptThread::Main) + } + + pub fn thread(root: OwnedEventId) -> Self { + Self::new(ReceiptThread::Thread(root)) + } + pub fn insert_message(&mut self, key: MessageKey, msg: impl Into) { let event_id = key.1.clone(); let msg = msg.into(); @@ -160,7 +177,9 @@ fn placeholder_frame( } let mut placeholder = "\u{230c}".to_string(); placeholder.push_str(&" ".repeat(width - 2)); - placeholder.push_str("\u{230d}\n"); + placeholder.push('\u{230d}'); + placeholder.push_str(&"\n".repeat((height - 1) / 2)); + if *height > 2 { if let Some(text) = text { if text.width() <= width - 2 { @@ -170,7 +189,7 @@ fn placeholder_frame( } } - placeholder.push_str(&"\n".repeat(height - 2)); + placeholder.push_str(&"\n".repeat(height / 2)); placeholder.push('\u{230e}'); placeholder.push_str(&" ".repeat(width - 2)); placeholder.push_str("\u{230f}\n"); @@ -180,9 +199,8 @@ fn placeholder_frame( #[inline] fn millis_to_datetime(ms: UInt) -> DateTime { let time = i64::from(ms) / 1000; - let time = NaiveDateTime::from_timestamp_opt(time, 0).unwrap_or_default(); - - LocalTz.from_utc_datetime(&time) + let time = DateTime::from_timestamp(time, 0).unwrap_or_default(); + time.into() } #[derive(thiserror::Error, Debug)] @@ -426,6 +444,7 @@ pub enum MessageEvent { EncryptedRedacted(Box), Original(Box), Redacted(Box), + State(Box), Local(OwnedEventId, Box), } @@ -436,6 +455,7 @@ impl MessageEvent { MessageEvent::EncryptedRedacted(ev) => ev.event_id.as_ref(), MessageEvent::Original(ev) => ev.event_id.as_ref(), MessageEvent::Redacted(ev) => ev.event_id.as_ref(), + MessageEvent::State(ev) => ev.event_id(), MessageEvent::Local(event_id, _) => event_id.as_ref(), } } @@ -446,6 +466,7 @@ impl MessageEvent { MessageEvent::Original(ev) => Some(&ev.content), MessageEvent::EncryptedRedacted(_) => None, MessageEvent::Redacted(_) => None, + MessageEvent::State(_) => None, MessageEvent::Local(_, content) => Some(content), } } @@ -463,6 +484,7 @@ impl MessageEvent { MessageEvent::Original(ev) => body_cow_content(&ev.content), MessageEvent::EncryptedRedacted(ev) => body_cow_reason(&ev.unsigned), MessageEvent::Redacted(ev) => body_cow_reason(&ev.unsigned), + MessageEvent::State(ev) => body_cow_state(ev), MessageEvent::Local(_, content) => body_cow_content(content), } } @@ -473,6 +495,7 @@ impl MessageEvent { MessageEvent::EncryptedRedacted(_) => return None, MessageEvent::Original(ev) => &ev.content, MessageEvent::Redacted(_) => return None, + MessageEvent::State(ev) => return Some(html_state(ev)), MessageEvent::Local(_, content) => content, }; @@ -492,6 +515,7 @@ impl MessageEvent { MessageEvent::EncryptedOriginal(_) => return, MessageEvent::EncryptedRedacted(_) => return, MessageEvent::Redacted(_) => return, + MessageEvent::State(_) => return, MessageEvent::Local(_, _) => return, MessageEvent::Original(ev) => { let redacted = RedactedRoomMessageEvent { @@ -623,8 +647,8 @@ struct MessageFormatter<'a> { /// The date the message was sent. date: Option>, - /// Iterator over the users who have read up to this message. - read: Option>, + /// The users who have read up to this message. + read: Vec, } impl<'a> MessageFormatter<'a> { @@ -657,13 +681,11 @@ impl<'a> MessageFormatter<'a> { line.push(time); // Show read receipts. - let user_char = - |user: &'a OwnedUserId| -> Span<'a> { settings.get_user_char_span(user) }; - let mut read = self.read.iter_mut().flatten(); + let user_char = |user: OwnedUserId| -> Span { settings.get_user_char_span(&user) }; - let a = read.next().map(user_char).unwrap_or_else(|| Span::raw(" ")); - let b = read.next().map(user_char).unwrap_or_else(|| Span::raw(" ")); - let c = read.next().map(user_char).unwrap_or_else(|| Span::raw(" ")); + let a = self.read.pop().map(user_char).unwrap_or_else(|| Span::raw(" ")); + let b = self.read.pop().map(user_char).unwrap_or_else(|| Span::raw(" ")); + let c = self.read.pop().map(user_char).unwrap_or_else(|| Span::raw(" ")); line.push(Span::raw(" ")); line.push(c); @@ -716,11 +738,11 @@ impl<'a> MessageFormatter<'a> { style: Style, text: &mut Text<'a>, info: &'a RoomInfo, - ) { + settings: &'a ApplicationSettings, + ) -> Option> { let width = self.width(); let w = width.saturating_sub(2); - let shortcodes = self.settings.tunables.message_shortcode_display; - let (mut replied, _) = msg.show_msg(w, style, true, shortcodes); + let (mut replied, proto) = msg.show_msg(w, style, true, settings); let mut sender = msg.sender_span(info, self.settings); let sender_width = UnicodeWidthStr::width(sender.content.as_ref()); let trailing = w.saturating_sub(sender_width + 1); @@ -739,16 +761,26 @@ impl<'a> MessageFormatter<'a> { text, ); + // Determine the image offset of the reply header, taking into account the formatting + let proto = proto.map(|p| { + let y_off = text.lines.len() as u16; + // Adjust x_off by 2 to account for the vertical line and indent + let x_off = self.cols.user_gutter_width(settings) + 2; + (p, x_off, y_off) + }); + for line in replied.lines.iter_mut() { line.spans.insert(0, Span::styled(THICK_VERTICAL, style)); line.spans.insert(0, Span::styled(" ", style)); } self.push_text(replied, style, text); + + proto } fn push_reactions(&mut self, counts: Vec<(&'a str, usize)>, style: Style, text: &mut Text<'a>) { - let mut emojis = printer::TextPrinter::new(self.width(), style, false, false); + let mut emojis = printer::TextPrinter::new(self.width(), style, false, self.settings); let mut reactions = 0; for (key, count) in counts { @@ -797,7 +829,7 @@ impl<'a> MessageFormatter<'a> { let plural = len != 1; let style = Style::default(); let mut threaded = - printer::TextPrinter::new(self.width(), style, false, false).literal(true); + printer::TextPrinter::new(self.width(), style, false, self.settings).literal(true); let len = Span::styled(len.to_string(), style.add_modifier(StyleModifier::BOLD)); threaded.push_str(" \u{2937} ", style); threaded.push_span_nobreak(len); @@ -814,7 +846,7 @@ impl<'a> MessageFormatter<'a> { pub enum ImageStatus { None, Downloading(ImagePreviewSize), - Loaded(Box), + Loaded(Protocol), Error(String), } @@ -849,6 +881,7 @@ impl Message { MessageEvent::Local(_, content) => content, MessageEvent::Original(ev) => &ev.content, MessageEvent::Redacted(_) => return None, + MessageEvent::State(_) => return None, }; match &content.relates_to { @@ -869,6 +902,7 @@ impl Message { MessageEvent::Local(_, content) => content, MessageEvent::Original(ev) => &ev.content, MessageEvent::Redacted(_) => return None, + MessageEvent::State(_) => return None, }; match &content.relates_to { @@ -922,7 +956,13 @@ impl Message { let fill = width - user_gutter - TIME_GUTTER - READ_GUTTER; let user = self.show_sender(prev, true, info, settings); let time = self.timestamp.show_time(); - let read = info.event_receipts.get(self.event.event_id()).map(|read| read.iter()); + let read = info + .event_receipts + .values() + .filter_map(|receipts| receipts.get(self.event.event_id())) + .flat_map(|read| read.iter()) + .map(|user_id| user_id.to_owned()) + .collect(); MessageFormatter { settings, cols, orig, fill, user, date, time, read } } else if user_gutter + TIME_GUTTER + MIN_MSG_LEN <= width { @@ -930,7 +970,7 @@ impl Message { let fill = width - user_gutter - TIME_GUTTER; let user = self.show_sender(prev, true, info, settings); let time = self.timestamp.show_time(); - let read = None; + let read = Vec::new(); MessageFormatter { settings, cols, orig, fill, user, date, time, read } } else if user_gutter + MIN_MSG_LEN <= width { @@ -938,7 +978,7 @@ impl Message { let fill = width - user_gutter; let user = self.show_sender(prev, true, info, settings); let time = None; - let read = None; + let read = Vec::new(); MessageFormatter { settings, cols, orig, fill, user, date, time, read } } else { @@ -946,7 +986,7 @@ impl Message { let fill = width.saturating_sub(2); let user = self.show_sender(prev, false, info, settings); let time = None; - let read = None; + let read = Vec::new(); MessageFormatter { settings, cols, orig, fill, user, date, time, read } } @@ -962,7 +1002,7 @@ impl Message { vwctx: &ViewportContext, info: &'a RoomInfo, settings: &'a ApplicationSettings, - ) -> (Text<'a>, Option<(&dyn Protocol, u16, u16)>) { + ) -> (Text<'a>, [Option>; 2]) { let width = vwctx.get_width(); let style = self.get_render_style(selected, settings); @@ -975,24 +1015,20 @@ impl Message { .reply_to() .or_else(|| self.thread_root()) .and_then(|e| info.get_event(&e)); - - if let Some(r) = &reply { - fmt.push_in_reply(r, style, &mut text, info); - } + let proto_reply = reply.as_ref().and_then(|r| { + // Format the reply header, push it into the `Text` buffer, and get any image. + fmt.push_in_reply(r, style, &mut text, info, settings) + }); // Now show the message contents, and the inlined reply if we couldn't find it above. - let (msg, proto) = self.show_msg( - width, - style, - reply.is_some(), - settings.tunables.message_shortcode_display, - ); + let (msg, proto) = self.show_msg(width, style, reply.is_some(), settings); // Given our text so far, determine the image offset. - let proto = proto.map(|p| { + let proto_main = proto.map(|p| { let y_off = text.lines.len() as u16; let x_off = fmt.cols.user_gutter_width(settings); - // Adjust y_off by 1 if a date was printed before the message to account for the extra line. + // Adjust y_off by 1 if a date was printed before the message to account for + // the extra line we're going to print. let y_off = if fmt.date.is_some() { y_off + 1 } else { y_off }; (p, x_off, y_off) }); @@ -1013,7 +1049,7 @@ impl Message { fmt.push_thread_reply_count(thread.len(), &mut text); } - (text, proto) + (text, [proto_main, proto_reply]) } pub fn show<'a>( @@ -1027,18 +1063,18 @@ impl Message { self.show_with_preview(prev, selected, vwctx, info, settings).0 } - fn show_msg( - &self, + fn show_msg<'a>( + &'a self, width: usize, style: Style, hide_reply: bool, - emoji_shortcodes: bool, - ) -> (Text, Option<&dyn Protocol>) { + settings: &'a ApplicationSettings, + ) -> (Text<'a>, Option<&'a Protocol>) { if let Some(html) = &self.html { - (html.to_text(width, style, hide_reply, emoji_shortcodes), None) + (html.to_text(width, style, hide_reply, settings), None) } else { let mut msg = self.event.body(); - if emoji_shortcodes { + if settings.tunables.message_shortcode_display { msg = Cow::Owned(replace_emojis_in_str(msg.as_ref())); } @@ -1053,8 +1089,8 @@ impl Message { placeholder_frame(Some("Downloading..."), width, image_preview_size) }, ImageStatus::Loaded(backend) => { - proto = Some(backend.as_ref()); - placeholder_frame(None, width, &backend.rect().into()) + proto = Some(backend); + placeholder_frame(Some("Cut off..."), width, &backend.area().into()) }, ImageStatus::Error(err) => Some(format!("[Image error: {err}]\n")), }; @@ -1097,9 +1133,9 @@ impl Message { let padding = user_gutter - 2 - width; let sender = if align_right { - space(padding) + &truncated + " " + format!("{}{} ", space(padding), truncated) } else { - truncated.into_owned() + &space(padding) + " " + format!("{}{} ", truncated, space(padding)) }; Span::styled(sender, style).into() @@ -1108,6 +1144,8 @@ impl Message { pub fn redact(&mut self, redaction: SyncRoomRedactionEvent, version: &RoomVersionId) { self.event.redact(redaction, version); self.html = None; + self.downloaded = false; + self.image_preview = ImageStatus::None; } } @@ -1153,6 +1191,16 @@ impl From for Message { } } +impl From for Message { + fn from(event: AnySyncStateEvent) -> Self { + let timestamp = event.origin_server_ts().into(); + let user_id = event.sender().to_owned(); + let event = MessageEvent::State(event.into()); + + Message::new(event, user_id, timestamp) + } +} + impl Display for Message { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.event.body()) @@ -1251,7 +1299,7 @@ pub mod tests { assert_eq!(k6, &MSG1_KEY.clone()); // MessageCursor::latest() fails to convert for a room w/o messages. - let messages_empty = Messages::default(); + let messages_empty = Messages::new(ReceiptThread::Main); assert_eq!(mc6.to_key(&messages_empty), None); } @@ -1313,6 +1361,33 @@ pub mod tests { OK ⌎ ⌏ +"# + ) + ); + assert_eq!( + placeholder_frame(Some("OK"), 6, &ImagePreviewSize { width: 6, height: 6 }), + pretty_frame_test( + r#" +⌌ ⌍ + + OK + + +⌎ ⌏ +"# + ) + ); + assert_eq!( + placeholder_frame(Some("OK"), 6, &ImagePreviewSize { width: 6, height: 7 }), + pretty_frame_test( + r#" +⌌ ⌍ + + + OK + + +⌎ ⌏ "# ) ); diff --git a/src/message/printer.rs b/src/message/printer.rs index 12d03be..3418752 100644 --- a/src/message/printer.rs +++ b/src/message/printer.rs @@ -11,6 +11,7 @@ use ratatui::text::{Line, Span, Text}; use unicode_segmentation::UnicodeSegmentation; use unicode_width::UnicodeWidthStr; +use crate::config::{ApplicationSettings, TunableValues}; use crate::util::{ replace_emojis_in_line, replace_emojis_in_span, @@ -25,28 +26,34 @@ pub struct TextPrinter<'a> { width: usize, base_style: Style, hide_reply: bool, - emoji_shortcodes: bool, alignment: Alignment, curr_spans: Vec>, curr_width: usize, literal: bool, + + pub(super) settings: &'a ApplicationSettings, } impl<'a> TextPrinter<'a> { /// Create a new printer. - pub fn new(width: usize, base_style: Style, hide_reply: bool, emoji_shortcodes: bool) -> Self { + pub fn new( + width: usize, + base_style: Style, + hide_reply: bool, + settings: &'a ApplicationSettings, + ) -> Self { TextPrinter { text: Text::default(), width, base_style, hide_reply, - emoji_shortcodes, alignment: Alignment::Left, curr_spans: vec![], curr_width: 0, literal: false, + settings, } } @@ -69,7 +76,15 @@ impl<'a> TextPrinter<'a> { /// Indicates whether emojis should be replaced by shortcodes pub fn emoji_shortcodes(&self) -> bool { - self.emoji_shortcodes + self.tunables().message_shortcode_display + } + + pub fn settings(&self) -> &ApplicationSettings { + self.settings + } + + pub fn tunables(&self) -> &TunableValues { + &self.settings.tunables } /// Indicates the current printer's width. @@ -84,12 +99,12 @@ impl<'a> TextPrinter<'a> { width: self.width.saturating_sub(indent), base_style: self.base_style, hide_reply: self.hide_reply, - emoji_shortcodes: self.emoji_shortcodes, alignment: self.alignment, curr_spans: vec![], curr_width: 0, literal: self.literal, + settings: self.settings, } } @@ -179,7 +194,7 @@ impl<'a> TextPrinter<'a> { /// Push a [Span] that isn't allowed to break across lines. pub fn push_span_nobreak(&mut self, mut span: Span<'a>) { - if self.emoji_shortcodes { + if self.emoji_shortcodes() { replace_emojis_in_span(&mut span); } let sw = UnicodeWidthStr::width(span.content.as_ref()); @@ -201,6 +216,8 @@ impl<'a> TextPrinter<'a> { return; } + let tabstop = self.settings().tunables.tabstop; + for mut word in UnicodeSegmentation::split_word_bounds(s) { if let "\n" | "\r\n" = word { if self.literal { @@ -217,11 +234,17 @@ impl<'a> TextPrinter<'a> { continue; } - let cow = if self.emoji_shortcodes { + let mut cow = if self.emoji_shortcodes() { Cow::Owned(replace_emojis_in_str(word)) } else { Cow::Borrowed(word) }; + + if cow == "\t" { + let tablen = tabstop - (self.curr_width % tabstop); + cow = Cow::Owned(" ".repeat(tablen)); + } + let sw = UnicodeWidthStr::width(cow.as_ref()); if sw > self.width { @@ -253,7 +276,7 @@ impl<'a> TextPrinter<'a> { /// Push a [Line] into the printer. pub fn push_line(&mut self, mut line: Line<'a>) { self.commit(); - if self.emoji_shortcodes { + if self.emoji_shortcodes() { replace_emojis_in_line(&mut line); } self.text.lines.push(line); @@ -262,7 +285,7 @@ impl<'a> TextPrinter<'a> { /// Push multiline [Text] into the printer. pub fn push_text(&mut self, mut text: Text<'a>) { self.commit(); - if self.emoji_shortcodes { + if self.emoji_shortcodes() { for line in &mut text.lines { replace_emojis_in_line(line); } @@ -280,10 +303,12 @@ impl<'a> TextPrinter<'a> { #[cfg(test)] pub mod tests { use super::*; + use crate::tests::mock_settings; #[test] fn test_push_nobreak() { - let mut printer = TextPrinter::new(5, Style::default(), false, false); + let settings = mock_settings(); + let mut printer = TextPrinter::new(5, Style::default(), false, &settings); printer.push_span_nobreak("hello world".into()); let text = printer.finish(); assert_eq!(text.lines.len(), 1); diff --git a/src/message/state.rs b/src/message/state.rs new file mode 100644 index 0000000..53885c4 --- /dev/null +++ b/src/message/state.rs @@ -0,0 +1,956 @@ +//! Code for displaying state events. +use std::borrow::Cow; +use std::str::FromStr; + +use matrix_sdk::ruma::{ + events::{ + room::member::MembershipChange, + AnyFullStateEventContent, + AnySyncStateEvent, + FullStateEventContent, + }, + OwnedRoomId, + UserId, +}; + +use super::html::{StyleTree, StyleTreeNode}; +use ratatui::style::{Modifier as StyleModifier, Style}; + +fn bold(s: impl Into>) -> StyleTreeNode { + let bold = Style::default().add_modifier(StyleModifier::BOLD); + let text = StyleTreeNode::Text(s.into()); + StyleTreeNode::Style(Box::new(text), bold) +} + +pub fn body_cow_state(ev: &AnySyncStateEvent) -> Cow<'static, str> { + let event = match ev.content() { + AnyFullStateEventContent::PolicyRuleRoom(FullStateEventContent::Original { + content, + .. + }) => { + let mut m = format!( + "* updated the room policy rule for {:?} to {:?}", + content.0.entity, + content.0.recommendation.as_str() + ); + + if !content.0.reason.is_empty() { + m.push_str(" (reason: "); + m.push_str(&content.0.reason); + m.push(')'); + } + + m + }, + AnyFullStateEventContent::PolicyRuleServer(FullStateEventContent::Original { + content, + .. + }) => { + let mut m = format!( + "* updated the server policy rule for {:?} to {:?}", + content.0.entity, + content.0.recommendation.as_str() + ); + + if !content.0.reason.is_empty() { + m.push_str(" (reason: "); + m.push_str(&content.0.reason); + m.push(')'); + } + + m + }, + AnyFullStateEventContent::PolicyRuleUser(FullStateEventContent::Original { + content, + .. + }) => { + let mut m = format!( + "* updated the user policy rule for {:?} to {:?}", + content.0.entity, + content.0.recommendation.as_str() + ); + + if !content.0.reason.is_empty() { + m.push_str(" (reason: "); + m.push_str(&content.0.reason); + m.push(')'); + } + + m + }, + AnyFullStateEventContent::RoomAliases(FullStateEventContent::Original { + content, .. + }) => { + let mut m = String::from("* set the room aliases to: "); + + for (i, alias) in content.aliases.iter().enumerate() { + if i != 0 { + m.push_str(", "); + } + + m.push_str(alias.as_str()); + } + + m + }, + AnyFullStateEventContent::RoomAvatar(FullStateEventContent::Original { + content, + prev_content, + }) => { + let prev_url = prev_content.as_ref().and_then(|p| p.url.as_ref()); + + match (prev_url, content.url) { + (None, Some(_)) => return Cow::Borrowed("* added a room avatar"), + (Some(old), Some(new)) => { + if old != &new { + return Cow::Borrowed("* replaced the room avatar"); + } + + return Cow::Borrowed("* updated the room avatar state"); + }, + (Some(_), None) => return Cow::Borrowed("* removed the room avatar"), + (None, None) => return Cow::Borrowed("* updated the room avatar state"), + } + }, + AnyFullStateEventContent::RoomCanonicalAlias(FullStateEventContent::Original { + content, + prev_content, + }) => { + let old_canon = prev_content.as_ref().and_then(|p| p.alias.as_ref()); + let new_canon = content.alias.as_ref(); + + match (old_canon, new_canon) { + (None, Some(canon)) => { + format!("* updated the canonical alias for the room to: {}", canon) + }, + (Some(old), Some(new)) => { + if old != new { + format!("* updated the canonical alias for the room to: {}", new) + } else { + return Cow::Borrowed("* removed the canonical alias for the room"); + } + }, + (Some(_), None) => { + return Cow::Borrowed("* removed the canonical alias for the room"); + }, + (None, None) => { + return Cow::Borrowed("* did not change the canonical alias"); + }, + } + }, + AnyFullStateEventContent::RoomCreate(FullStateEventContent::Original { + content, .. + }) => { + if content.federate { + return Cow::Borrowed("* created a federated room"); + } else { + return Cow::Borrowed("* created a non-federated room"); + } + }, + AnyFullStateEventContent::RoomEncryption(FullStateEventContent::Original { .. }) => { + return Cow::Borrowed("* updated the encryption settings for the room"); + }, + AnyFullStateEventContent::RoomGuestAccess(FullStateEventContent::Original { + content, + .. + }) => { + format!("* set guest access for the room to {:?}", content.guest_access.as_str()) + }, + AnyFullStateEventContent::RoomHistoryVisibility(FullStateEventContent::Original { + content, + .. + }) => { + format!( + "* updated history visibility for the room to {:?}", + content.history_visibility.as_str() + ) + }, + AnyFullStateEventContent::RoomJoinRules(FullStateEventContent::Original { + content, + .. + }) => { + format!("* update the join rules for the room to {:?}", content.join_rule.as_str()) + }, + AnyFullStateEventContent::RoomMember(FullStateEventContent::Original { + content, + prev_content, + }) => { + let Ok(state_key) = UserId::parse(ev.state_key()) else { + return Cow::Owned(format!( + "* failed to calculate membership change for {:?}", + ev.state_key() + )); + }; + + let prev_details = prev_content.as_ref().map(|p| p.details()); + let change = content.membership_change(prev_details, ev.sender(), &state_key); + + match change { + MembershipChange::None => { + format!("* did nothing to {}", state_key) + }, + MembershipChange::Error => { + format!("* failed to calculate membership change to {}", state_key) + }, + MembershipChange::Joined => { + return Cow::Borrowed("* joined the room"); + }, + MembershipChange::Left => { + return Cow::Borrowed("* left the room"); + }, + MembershipChange::Banned => { + format!("* banned {} from the room", state_key) + }, + MembershipChange::Unbanned => { + format!("* unbanned {} from the room", state_key) + }, + MembershipChange::Kicked => { + format!("* kicked {} from the room", state_key) + }, + MembershipChange::Invited => { + format!("* invited {} to the room", state_key) + }, + MembershipChange::KickedAndBanned => { + format!("* kicked and banned {} from the room", state_key) + }, + MembershipChange::InvitationAccepted => { + return Cow::Borrowed("* accepted an invitation to join the room"); + }, + MembershipChange::InvitationRejected => { + return Cow::Borrowed("* rejected an invitation to join the room"); + }, + MembershipChange::InvitationRevoked => { + format!("* revoked an invitation for {} to join the room", state_key) + }, + MembershipChange::Knocked => { + return Cow::Borrowed("* would like to join the room"); + }, + MembershipChange::KnockAccepted => { + format!("* accepted the room knock from {}", state_key) + }, + MembershipChange::KnockRetracted => { + return Cow::Borrowed("* retracted their room knock"); + }, + MembershipChange::KnockDenied => { + format!("* rejected the room knock from {}", state_key) + }, + MembershipChange::ProfileChanged { displayname_change, avatar_url_change } => { + match (displayname_change, avatar_url_change) { + (Some(change), avatar_change) => { + let mut m = match (change.old, change.new) { + (None, Some(new)) => { + format!("* set their display name to {:?}", new) + }, + (Some(old), Some(new)) => { + format!("* changed their display name from {old} to {new}") + }, + (Some(_), None) => "* unset their display name".to_string(), + (None, None) => { + "* made an unknown change to their display name".to_string() + }, + }; + + if avatar_change.is_some() { + m.push_str(" and changed their user avatar"); + } + + m + }, + (None, Some(change)) => { + match (change.old, change.new) { + (None, Some(_)) => { + return Cow::Borrowed("* added a user avatar"); + }, + (Some(_), Some(_)) => { + return Cow::Borrowed("* changed their user avatar"); + }, + (Some(_), None) => { + return Cow::Borrowed("* removed their user avatar"); + }, + (None, None) => { + return Cow::Borrowed( + "* made an unknown change to their user avatar", + ); + }, + } + }, + (None, None) => { + return Cow::Borrowed("* changed their user profile"); + }, + } + }, + ev => { + format!("* made an unknown membership change to {}: {:?}", state_key, ev) + }, + } + }, + AnyFullStateEventContent::RoomName(FullStateEventContent::Original { content, .. }) => { + format!("* updated the room name to {:?}", content.name) + }, + AnyFullStateEventContent::RoomPinnedEvents(FullStateEventContent::Original { .. }) => { + return Cow::Borrowed("* updated the pinned events for the room"); + }, + AnyFullStateEventContent::RoomPowerLevels(FullStateEventContent::Original { .. }) => { + return Cow::Borrowed("* updated the power levels for the room"); + }, + AnyFullStateEventContent::RoomServerAcl(FullStateEventContent::Original { .. }) => { + return Cow::Borrowed("* updated the room's server ACLs"); + }, + AnyFullStateEventContent::RoomThirdPartyInvite(FullStateEventContent::Original { + content, + .. + }) => { + format!("* sent a third-party invite to {:?}", content.display_name) + }, + AnyFullStateEventContent::RoomTombstone(FullStateEventContent::Original { + content, + .. + }) => { + format!( + "* upgraded the room; replacement room is {}", + content.replacement_room.as_str() + ) + }, + AnyFullStateEventContent::RoomTopic(FullStateEventContent::Original { + content, .. + }) => { + format!("* set the room topic to {:?}", content.topic) + }, + AnyFullStateEventContent::SpaceChild(FullStateEventContent::Original { .. }) => { + format!("* added a space child: {}", ev.state_key()) + }, + AnyFullStateEventContent::SpaceParent(FullStateEventContent::Original { + content, .. + }) => { + if content.canonical { + format!("* added a canonical parent space: {}", ev.state_key()) + } else { + format!("* added a parent space: {}", ev.state_key()) + } + }, + AnyFullStateEventContent::BeaconInfo(FullStateEventContent::Original { .. }) => { + return Cow::Borrowed("* shared beacon information"); + }, + AnyFullStateEventContent::CallMember(FullStateEventContent::Original { .. }) => { + return Cow::Borrowed("* updated membership for room call"); + }, + AnyFullStateEventContent::MemberHints(FullStateEventContent::Original { + content, .. + }) => { + let mut m = String::from("* updated the list of service members in the room hints: "); + + for (i, member) in content.service_members.iter().enumerate() { + if i != 0 { + m.push_str(", "); + } + + m.push_str(member.as_str()); + } + + m + }, + + // Redacted variants of state events: + AnyFullStateEventContent::PolicyRuleRoom(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("* updated a room policy rule (redacted)"); + }, + AnyFullStateEventContent::PolicyRuleServer(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("* updated a server policy rule (redacted)"); + }, + AnyFullStateEventContent::PolicyRuleUser(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("* updated a user policy rule (redacted)"); + }, + AnyFullStateEventContent::RoomAliases(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("* updated the room aliases for the room (redacted)"); + }, + AnyFullStateEventContent::RoomAvatar(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("* updated the room avatar (redacted)"); + }, + AnyFullStateEventContent::RoomCanonicalAlias(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("* updated the canonical alias for the room (redacted)"); + }, + AnyFullStateEventContent::RoomCreate(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("* created the room (redacted)"); + }, + AnyFullStateEventContent::RoomEncryption(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("* updated the encryption settings for the room (redacted)"); + }, + AnyFullStateEventContent::RoomGuestAccess(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed( + "* updated the guest access configuration for the room (redacted)", + ); + }, + AnyFullStateEventContent::RoomHistoryVisibility(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("* updated history visilibity for the room (redacted)"); + }, + AnyFullStateEventContent::RoomJoinRules(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("* updated the join rules for the room (redacted)"); + }, + AnyFullStateEventContent::RoomMember(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("* updated the room membership (redacted)"); + }, + AnyFullStateEventContent::RoomName(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("* updated the room name (redacted)"); + }, + AnyFullStateEventContent::RoomPinnedEvents(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("* updated the pinned events for the room (redacted)"); + }, + AnyFullStateEventContent::RoomPowerLevels(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("* updated the power levels for the room (redacted)"); + }, + AnyFullStateEventContent::RoomServerAcl(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("* updated the room's server ACLs (redacted)"); + }, + AnyFullStateEventContent::RoomThirdPartyInvite(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("* sent a third-party invite (redacted)"); + }, + AnyFullStateEventContent::RoomTombstone(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("* upgraded the room (redacted)"); + }, + AnyFullStateEventContent::RoomTopic(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("* updated the room topic (redacted)"); + }, + AnyFullStateEventContent::SpaceChild(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("* added a space child (redacted)"); + }, + AnyFullStateEventContent::SpaceParent(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("* added a parent space (redacted)"); + }, + AnyFullStateEventContent::BeaconInfo(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("* shared beacon information (redacted)"); + }, + AnyFullStateEventContent::CallMember(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("Call membership changed"); + }, + AnyFullStateEventContent::MemberHints(FullStateEventContent::Redacted(_)) => { + return Cow::Borrowed("Member hints changed"); + }, + + // Handle unknown events: + e => { + format!("* sent an unknown state event: {:?}", e.event_type()) + }, + }; + + return Cow::Owned(event); +} + +pub fn html_state(ev: &AnySyncStateEvent) -> StyleTree { + let children = match ev.content() { + AnyFullStateEventContent::PolicyRuleRoom(FullStateEventContent::Original { + content, + .. + }) => { + let prefix = StyleTreeNode::Text("* updated the room policy rule for ".into()); + let entity = bold(format!("{:?}", content.0.entity)); + let middle = StyleTreeNode::Text(" to ".into()); + let rec = + StyleTreeNode::Text(format!("{:?}", content.0.recommendation.as_str()).into()); + let mut cs = vec![prefix, entity, middle, rec]; + + if !content.0.reason.is_empty() { + let reason = format!(" (reason: {})", content.0.reason); + cs.push(StyleTreeNode::Text(reason.into())); + } + + cs + }, + AnyFullStateEventContent::PolicyRuleServer(FullStateEventContent::Original { + content, + .. + }) => { + let prefix = StyleTreeNode::Text("* updated the server policy rule for ".into()); + let entity = bold(format!("{:?}", content.0.entity)); + let middle = StyleTreeNode::Text(" to ".into()); + let rec = + StyleTreeNode::Text(format!("{:?}", content.0.recommendation.as_str()).into()); + let mut cs = vec![prefix, entity, middle, rec]; + + if !content.0.reason.is_empty() { + let reason = format!(" (reason: {})", content.0.reason); + cs.push(StyleTreeNode::Text(reason.into())); + } + + cs + }, + AnyFullStateEventContent::PolicyRuleUser(FullStateEventContent::Original { + content, + .. + }) => { + let prefix = StyleTreeNode::Text("* updated the user policy rule for ".into()); + let entity = bold(format!("{:?}", content.0.entity)); + let middle = StyleTreeNode::Text(" to ".into()); + let rec = + StyleTreeNode::Text(format!("{:?}", content.0.recommendation.as_str()).into()); + let mut cs = vec![prefix, entity, middle, rec]; + + if !content.0.reason.is_empty() { + let reason = format!(" (reason: {})", content.0.reason); + cs.push(StyleTreeNode::Text(reason.into())); + } + + cs + }, + AnyFullStateEventContent::RoomAliases(FullStateEventContent::Original { + content, .. + }) => { + let prefix = StyleTreeNode::Text("* set the room aliases to: ".into()); + let mut cs = vec![prefix]; + + for (i, alias) in content.aliases.iter().enumerate() { + if i != 0 { + cs.push(StyleTreeNode::Text(", ".into())); + } + + cs.push(StyleTreeNode::RoomAlias(alias.clone())); + } + + cs + }, + AnyFullStateEventContent::RoomAvatar(FullStateEventContent::Original { + content, + prev_content, + }) => { + let prev_url = prev_content.as_ref().and_then(|p| p.url.as_ref()); + + let node = match (prev_url, content.url) { + (None, Some(_)) => StyleTreeNode::Text("* added a room avatar".into()), + (Some(old), Some(new)) => { + if old != &new { + StyleTreeNode::Text("* replaced the room avatar".into()) + } else { + StyleTreeNode::Text("* updated the room avatar state".into()) + } + }, + (Some(_), None) => StyleTreeNode::Text("* removed the room avatar".into()), + (None, None) => StyleTreeNode::Text("* updated the room avatar state".into()), + }; + + vec![node] + }, + AnyFullStateEventContent::RoomCanonicalAlias(FullStateEventContent::Original { + content, + .. + }) => { + if let Some(canon) = content.alias.as_ref() { + let canon = bold(canon.to_string()); + let prefix = + StyleTreeNode::Text("* updated the canonical alias for the room to: ".into()); + vec![prefix, canon] + } else { + vec![StyleTreeNode::Text( + "* removed the canonical alias for the room".into(), + )] + } + }, + AnyFullStateEventContent::RoomCreate(FullStateEventContent::Original { + content, .. + }) => { + if content.federate { + vec![StyleTreeNode::Text("* created a federated room".into())] + } else { + vec![StyleTreeNode::Text("* created a non-federated room".into())] + } + }, + AnyFullStateEventContent::RoomEncryption(FullStateEventContent::Original { .. }) => { + vec![StyleTreeNode::Text( + "* updated the encryption settings for the room".into(), + )] + }, + AnyFullStateEventContent::RoomGuestAccess(FullStateEventContent::Original { + content, + .. + }) => { + let access = bold(format!("{:?}", content.guest_access.as_str())); + let prefix = StyleTreeNode::Text("* set guest access for the room to ".into()); + vec![prefix, access] + }, + AnyFullStateEventContent::RoomHistoryVisibility(FullStateEventContent::Original { + content, + .. + }) => { + let prefix = + StyleTreeNode::Text("* updated history visibility for the room to ".into()); + let vis = bold(format!("{:?}", content.history_visibility.as_str())); + vec![prefix, vis] + }, + AnyFullStateEventContent::RoomJoinRules(FullStateEventContent::Original { + content, + .. + }) => { + let prefix = StyleTreeNode::Text("* update the join rules for the room to ".into()); + let rule = bold(format!("{:?}", content.join_rule.as_str())); + vec![prefix, rule] + }, + AnyFullStateEventContent::RoomMember(FullStateEventContent::Original { + content, + prev_content, + }) => { + let Ok(state_key) = UserId::parse(ev.state_key()) else { + let prefix = + StyleTreeNode::Text("* failed to calculate membership change for ".into()); + let user_id = bold(format!("{:?}", ev.state_key())); + let children = vec![prefix, user_id]; + + return StyleTree { children }; + }; + + let prev_details = prev_content.as_ref().map(|p| p.details()); + let change = content.membership_change(prev_details, ev.sender(), &state_key); + let user_id = StyleTreeNode::UserId(state_key.clone()); + + match change { + MembershipChange::None => { + let prefix = StyleTreeNode::Text("* did nothing to ".into()); + vec![prefix, user_id] + }, + MembershipChange::Error => { + let prefix = + StyleTreeNode::Text("* failed to calculate membership change to ".into()); + vec![prefix, user_id] + }, + MembershipChange::Joined => { + vec![StyleTreeNode::Text("* joined the room".into())] + }, + MembershipChange::Left => { + vec![StyleTreeNode::Text("* left the room".into())] + }, + MembershipChange::Banned => { + let prefix = StyleTreeNode::Text("* banned ".into()); + let suffix = StyleTreeNode::Text(" from the room".into()); + vec![prefix, user_id, suffix] + }, + MembershipChange::Unbanned => { + let prefix = StyleTreeNode::Text("* unbanned ".into()); + let suffix = StyleTreeNode::Text(" from the room".into()); + vec![prefix, user_id, suffix] + }, + MembershipChange::Kicked => { + let prefix = StyleTreeNode::Text("* kicked ".into()); + let suffix = StyleTreeNode::Text(" from the room".into()); + vec![prefix, user_id, suffix] + }, + MembershipChange::Invited => { + let prefix = StyleTreeNode::Text("* invited ".into()); + let suffix = StyleTreeNode::Text(" to the room".into()); + vec![prefix, user_id, suffix] + }, + MembershipChange::KickedAndBanned => { + let prefix = StyleTreeNode::Text("* kicked and banned ".into()); + let suffix = StyleTreeNode::Text(" from the room".into()); + vec![prefix, user_id, suffix] + }, + MembershipChange::InvitationAccepted => { + vec![StyleTreeNode::Text( + "* accepted an invitation to join the room".into(), + )] + }, + MembershipChange::InvitationRejected => { + vec![StyleTreeNode::Text( + "* rejected an invitation to join the room".into(), + )] + }, + MembershipChange::InvitationRevoked => { + let prefix = StyleTreeNode::Text("* revoked an invitation for ".into()); + let suffix = StyleTreeNode::Text(" to join the room".into()); + vec![prefix, user_id, suffix] + }, + MembershipChange::Knocked => { + vec![StyleTreeNode::Text("* would like to join the room".into())] + }, + MembershipChange::KnockAccepted => { + let prefix = StyleTreeNode::Text("* accepted the room knock from ".into()); + vec![prefix, user_id] + }, + MembershipChange::KnockRetracted => { + vec![StyleTreeNode::Text("* retracted their room knock".into())] + }, + MembershipChange::KnockDenied => { + let prefix = StyleTreeNode::Text("* rejected the room knock from ".into()); + vec![prefix, user_id] + }, + MembershipChange::ProfileChanged { displayname_change, avatar_url_change } => { + match (displayname_change, avatar_url_change) { + (Some(change), avatar_change) => { + let mut m = match (change.old, change.new) { + (None, Some(new)) => { + vec![ + StyleTreeNode::Text("* set their display name to ".into()), + StyleTreeNode::DisplayName(new.into(), state_key), + ] + }, + (Some(old), Some(new)) => { + vec![ + StyleTreeNode::Text( + "* changed their display name from ".into(), + ), + StyleTreeNode::DisplayName(old.into(), state_key.clone()), + StyleTreeNode::Text(" to ".into()), + StyleTreeNode::DisplayName(new.into(), state_key), + ] + }, + (Some(_), None) => { + vec![StyleTreeNode::Text("* unset their display name".into())] + }, + (None, None) => { + vec![StyleTreeNode::Text( + "* made an unknown change to their display name".into(), + )] + }, + }; + + if avatar_change.is_some() { + m.push(StyleTreeNode::Text( + " and changed their user avatar".into(), + )); + } + + m + }, + (None, Some(change)) => { + let m = match (change.old, change.new) { + (None, Some(_)) => Cow::Borrowed("* added a user avatar"), + (Some(_), Some(_)) => Cow::Borrowed("* changed their user avatar"), + (Some(_), None) => Cow::Borrowed("* removed their user avatar"), + (None, None) => { + Cow::Borrowed("* made an unknown change to their user avatar") + }, + }; + + vec![StyleTreeNode::Text(m)] + }, + (None, None) => { + vec![StyleTreeNode::Text("* changed their user profile".into())] + }, + } + }, + ev => { + let prefix = + StyleTreeNode::Text("* made an unknown membership change to ".into()); + let suffix = StyleTreeNode::Text(format!(": {:?}", ev).into()); + vec![prefix, user_id, suffix] + }, + } + }, + AnyFullStateEventContent::RoomName(FullStateEventContent::Original { content, .. }) => { + let prefix = StyleTreeNode::Text("* updated the room name to ".into()); + let name = bold(format!("{:?}", content.name)); + vec![prefix, name] + }, + AnyFullStateEventContent::RoomPinnedEvents(FullStateEventContent::Original { .. }) => { + vec![StyleTreeNode::Text( + "* updated the pinned events for the room".into(), + )] + }, + AnyFullStateEventContent::RoomPowerLevels(FullStateEventContent::Original { .. }) => { + vec![StyleTreeNode::Text( + "* updated the power levels for the room".into(), + )] + }, + AnyFullStateEventContent::RoomServerAcl(FullStateEventContent::Original { .. }) => { + vec![StyleTreeNode::Text( + "* updated the room's server ACLs".into(), + )] + }, + AnyFullStateEventContent::RoomThirdPartyInvite(FullStateEventContent::Original { + content, + .. + }) => { + let prefix = StyleTreeNode::Text("* sent a third-party invite to ".into()); + let name = bold(format!("{:?}", content.display_name)); + vec![prefix, name] + }, + AnyFullStateEventContent::RoomTombstone(FullStateEventContent::Original { + content, + .. + }) => { + let prefix = StyleTreeNode::Text("* upgraded the room; replacement room is ".into()); + let room = StyleTreeNode::RoomId(content.replacement_room.clone()); + vec![prefix, room] + }, + AnyFullStateEventContent::RoomTopic(FullStateEventContent::Original { + content, .. + }) => { + let prefix = StyleTreeNode::Text("* set the room topic to ".into()); + let topic = bold(format!("{:?}", content.topic)); + vec![prefix, topic] + }, + AnyFullStateEventContent::SpaceChild(FullStateEventContent::Original { .. }) => { + let prefix = StyleTreeNode::Text("* added a space child: ".into()); + + let room_id = if let Ok(room_id) = OwnedRoomId::from_str(ev.state_key()) { + StyleTreeNode::RoomId(room_id) + } else { + bold(ev.state_key().to_string()) + }; + + vec![prefix, room_id] + }, + AnyFullStateEventContent::SpaceParent(FullStateEventContent::Original { + content, .. + }) => { + let prefix = if content.canonical { + StyleTreeNode::Text("* added a canonical parent space: ".into()) + } else { + StyleTreeNode::Text("* added a parent space: ".into()) + }; + + let room_id = if let Ok(room_id) = OwnedRoomId::from_str(ev.state_key()) { + StyleTreeNode::RoomId(room_id) + } else { + bold(ev.state_key().to_string()) + }; + + vec![prefix, room_id] + }, + AnyFullStateEventContent::BeaconInfo(FullStateEventContent::Original { .. }) => { + vec![StyleTreeNode::Text("* shared beacon information".into())] + }, + AnyFullStateEventContent::CallMember(FullStateEventContent::Original { .. }) => { + vec![StyleTreeNode::Text( + "* updated membership for room call".into(), + )] + }, + AnyFullStateEventContent::MemberHints(FullStateEventContent::Original { + content, .. + }) => { + let prefix = StyleTreeNode::Text( + "* updated the list of service members in the room hints: ".into(), + ); + let mut cs = vec![prefix]; + + for (i, member) in content.service_members.iter().enumerate() { + if i != 0 { + cs.push(StyleTreeNode::Text(", ".into())); + } + + cs.push(StyleTreeNode::UserId(member.clone())); + } + + cs + }, + + // Redacted variants of state events: + AnyFullStateEventContent::PolicyRuleRoom(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text( + "* updated a room policy rule (redacted)".into(), + )] + }, + AnyFullStateEventContent::PolicyRuleServer(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text( + "* updated a server policy rule (redacted)".into(), + )] + }, + AnyFullStateEventContent::PolicyRuleUser(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text( + "* updated a user policy rule (redacted)".into(), + )] + }, + AnyFullStateEventContent::RoomAliases(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text( + "* updated the room aliases for the room (redacted)".into(), + )] + }, + AnyFullStateEventContent::RoomAvatar(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text( + "* updated the room avatar (redacted)".into(), + )] + }, + AnyFullStateEventContent::RoomCanonicalAlias(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text( + "* updated the canonical alias for the room (redacted)".into(), + )] + }, + AnyFullStateEventContent::RoomCreate(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text("* created the room (redacted)".into())] + }, + AnyFullStateEventContent::RoomEncryption(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text( + "* updated the encryption settings for the room (redacted)".into(), + )] + }, + AnyFullStateEventContent::RoomGuestAccess(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text( + "* updated the guest access configuration for the room (redacted)".into(), + )] + }, + AnyFullStateEventContent::RoomHistoryVisibility(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text( + "* updated history visilibity for the room (redacted)".into(), + )] + }, + AnyFullStateEventContent::RoomJoinRules(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text( + "* updated the join rules for the room (redacted)".into(), + )] + }, + AnyFullStateEventContent::RoomMember(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text( + "* updated the room membership (redacted)".into(), + )] + }, + AnyFullStateEventContent::RoomName(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text( + "* updated the room name (redacted)".into(), + )] + }, + AnyFullStateEventContent::RoomPinnedEvents(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text( + "* updated the pinned events for the room (redacted)".into(), + )] + }, + AnyFullStateEventContent::RoomPowerLevels(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text( + "* updated the power levels for the room (redacted)".into(), + )] + }, + AnyFullStateEventContent::RoomServerAcl(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text( + "* updated the room's server ACLs (redacted)".into(), + )] + }, + AnyFullStateEventContent::RoomThirdPartyInvite(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text( + "* sent a third-party invite (redacted)".into(), + )] + }, + AnyFullStateEventContent::RoomTombstone(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text("* upgraded the room (redacted)".into())] + }, + AnyFullStateEventContent::RoomTopic(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text( + "* updated the room topic (redacted)".into(), + )] + }, + AnyFullStateEventContent::SpaceChild(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text( + "* added a space child (redacted)".into(), + )] + }, + AnyFullStateEventContent::SpaceParent(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text( + "* added a parent space (redacted)".into(), + )] + }, + AnyFullStateEventContent::BeaconInfo(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text( + "* shared beacon information (redacted)".into(), + )] + }, + AnyFullStateEventContent::CallMember(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text("Call membership changed".into())] + }, + AnyFullStateEventContent::MemberHints(FullStateEventContent::Redacted(_)) => { + vec![StyleTreeNode::Text("Member hints changed".into())] + }, + + // Handle unknown events: + e => { + let prefix = StyleTreeNode::Text("* sent an unknown state event: ".into()); + let event = bold(format!("{:?}", e.event_type())); + vec![prefix, event] + }, + }; + + StyleTree { children } +} diff --git a/src/notifications.rs b/src/notifications.rs index f6cf3e3..0462101 100644 --- a/src/notifications.rs +++ b/src/notifications.rs @@ -1,12 +1,14 @@ use std::time::SystemTime; use matrix_sdk::{ + deserialized_responses::RawAnySyncOrStrippedTimelineEvent, notification_settings::{IsEncrypted, IsOneToOne, NotificationSettings, RoomNotificationMode}, room::Room as MatrixRoom, ruma::{ - api::client::push::get_notifications::v3::Notification, events::{room::message::MessageType, AnyMessageLikeEventContent, AnySyncTimelineEvent}, + serde::Raw, MilliSecondsSinceUnixEpoch, + OwnedRoomId, RoomId, }, Client, @@ -23,6 +25,21 @@ const IAMB_XDG_NAME: &str = match option_env!("IAMB_XDG_NAME") { Some(iamb) => iamb, }; +/// Handle for an open notification that should be closed when the user views it. +pub struct NotificationHandle( + #[cfg(all(feature = "desktop", unix, not(target_os = "macos")))] + Option, +); + +impl Drop for NotificationHandle { + fn drop(&mut self) { + #[cfg(all(feature = "desktop", unix, not(target_os = "macos")))] + if let Some(handle) = self.0.take() { + handle.close(); + } + } +} + pub async fn register_notifications( client: &Client, settings: &ApplicationSettings, @@ -53,51 +70,103 @@ pub async fn register_notifications( return; } - match parse_notification(notification, room, show_message).await { - Ok((summary, body, server_ts)) => { - if server_ts < startup_ts { - return; - } + let room_id = room.room_id().to_owned(); + match notification.event { + RawAnySyncOrStrippedTimelineEvent::Sync(e) => { + match parse_full_notification(e, room, show_message).await { + Ok((summary, body, server_ts)) => { + if server_ts < startup_ts { + return; + } - if is_missing_mention(&body, mode, &client) { - return; - } + if is_missing_mention(&body, mode, &client) { + return; + } - match notify_via { - #[cfg(feature = "desktop")] - NotifyVia::Desktop => send_notification_desktop(summary, body), - NotifyVia::Bell => send_notification_bell(&store).await, + send_notification( + ¬ify_via, + &summary, + body.as_deref(), + room_id, + &store, + ) + .await; + }, + Err(err) => { + tracing::error!("Failed to extract notification data: {err}") + }, } }, - Err(err) => { - tracing::error!("Failed to extract notification data: {err}") - }, + // Stripped events may be dropped silently because they're + // only relevant if we're not in a room, and we presumably + // don't want notifications for rooms we're not in. + RawAnySyncOrStrippedTimelineEvent::Stripped(_) => (), } } }) .await; } +async fn send_notification( + via: &NotifyVia, + summary: &str, + body: Option<&str>, + room_id: OwnedRoomId, + store: &AsyncProgramStore, +) { + #[cfg(feature = "desktop")] + if via.desktop { + send_notification_desktop(summary, body, room_id, store).await; + } + #[cfg(not(feature = "desktop"))] + { + let _ = (summary, body, IAMB_XDG_NAME); + } + + if via.bell { + send_notification_bell(store).await; + } +} + async fn send_notification_bell(store: &AsyncProgramStore) { let mut locked = store.lock().await; locked.application.ring_bell = true; } #[cfg(feature = "desktop")] -fn send_notification_desktop(summary: String, body: Option) { +async fn send_notification_desktop( + summary: &str, + body: Option<&str>, + room_id: OwnedRoomId, + _store: &AsyncProgramStore, +) { let mut desktop_notification = notify_rust::Notification::new(); desktop_notification - .summary(&summary) + .summary(summary) .appname(IAMB_XDG_NAME) .icon(IAMB_XDG_NAME) .action("default", "default"); + #[cfg(all(unix, not(target_os = "macos")))] + desktop_notification.urgency(notify_rust::Urgency::Normal); + if let Some(body) = body { - desktop_notification.body(&body); + desktop_notification.body(body); } - if let Err(err) = desktop_notification.show() { - tracing::error!("Failed to send notification: {err}") + match desktop_notification.show() { + Err(err) => tracing::error!("Failed to send notification: {err}"), + Ok(handle) => { + #[cfg(all(unix, not(target_os = "macos")))] + _store + .lock() + .await + .application + .open_notifications + .entry(room_id) + .or_default() + .push(NotificationHandle(Some(handle))); + }, } } @@ -155,12 +224,12 @@ async fn is_visible_room(store: &AsyncProgramStore, room_id: &RoomId) -> bool { is_focused(&locked) && is_open(&mut locked, room_id) } -pub async fn parse_notification( - notification: Notification, +pub async fn parse_full_notification( + event: Raw, room: MatrixRoom, show_body: bool, ) -> IambResult<(String, Option, MilliSecondsSinceUnixEpoch)> { - let event = notification.event.deserialize().map_err(IambError::from)?; + let event = event.deserialize().map_err(IambError::from)?; let server_ts = event.origin_server_ts(); @@ -172,19 +241,19 @@ pub async fn parse_notification( .and_then(|m| m.display_name()) .unwrap_or_else(|| sender_id.localpart()); - let summary = if let Ok(room_name) = room.display_name().await { - format!("{sender_name} in {room_name}") + let summary = if let Some(room_name) = room.cached_display_name() { + if room.is_direct().await.map_err(IambError::from)? && sender_name == room_name.to_string() + { + sender_name.to_string() + } else { + format!("{sender_name} in {room_name}") + } } else { sender_name.to_string() }; let body = if show_body { - event_notification_body( - &event, - sender_name, - room.is_direct().await.map_err(IambError::from)?, - ) - .map(truncate) + event_notification_body(&event, sender_name).map(truncate) } else { None }; @@ -192,11 +261,7 @@ pub async fn parse_notification( return Ok((summary, body, server_ts)); } -pub fn event_notification_body( - event: &AnySyncTimelineEvent, - sender_name: &str, - is_direct: bool, -) -> Option { +pub fn event_notification_body(event: &AnySyncTimelineEvent, sender_name: &str) -> Option { let AnySyncTimelineEvent::MessageLike(event) = event else { return None; }; @@ -207,10 +272,7 @@ pub fn event_notification_body( MessageType::Audio(_) => { format!("{sender_name} sent an audio file.") }, - MessageType::Emote(content) => { - let message = &content.body; - format!("{sender_name}: {message}") - }, + MessageType::Emote(content) => content.body, MessageType::File(_) => { format!("{sender_name} sent a file.") }, @@ -220,22 +282,9 @@ pub fn event_notification_body( MessageType::Location(_) => { format!("{sender_name} sent their location.") }, - MessageType::Notice(content) => { - let message = &content.body; - format!("{sender_name}: {message}") - }, - MessageType::ServerNotice(content) => { - let message = &content.body; - format!("{sender_name}: {message}") - }, - MessageType::Text(content) => { - if is_direct { - content.body - } else { - let message = &content.body; - format!("{sender_name}: {message}") - } - }, + MessageType::Notice(content) => content.body, + MessageType::ServerNotice(content) => content.body, + MessageType::Text(content) => content.body, MessageType::Video(_) => { format!("{sender_name} sent a video.") }, @@ -254,7 +303,7 @@ pub fn event_notification_body( } fn truncate(s: String) -> String { - static MAX_LENGTH: usize = 100; + static MAX_LENGTH: usize = 5000; if s.graphemes(true).count() > MAX_LENGTH { let truncated: String = s.graphemes(true).take(MAX_LENGTH).collect(); truncated + "..." diff --git a/src/preview.rs b/src/preview.rs index 932eb39..f2c620a 100644 --- a/src/preview.rs +++ b/src/preview.rs @@ -5,7 +5,7 @@ use std::{ }; use matrix_sdk::{ - media::{MediaFormat, MediaRequest}, + media::{MediaFormat, MediaRequestParameters}, ruma::{ events::{ room::{ @@ -63,7 +63,7 @@ pub fn spawn_insert_preview( let img = download_or_load(event_id.to_owned(), source, media, cache_dir) .await .map(std::io::Cursor::new) - .map(image::io::Reader::new) + .map(image::ImageReader::new) .map_err(IambError::Matrix) .and_then(|reader| reader.with_guessed_format().map_err(IambError::IOError)) .and_then(|reader| reader.decode().map_err(IambError::Image)); @@ -157,7 +157,10 @@ async fn download_or_load( }, Err(_) => { media - .get_media_content(&MediaRequest { source, format: MediaFormat::File }, true) + .get_media_content( + &MediaRequestParameters { source, format: MediaFormat::File }, + true, + ) .await .and_then(|buffer| { if let Err(err) = diff --git a/src/tests.rs b/src/tests.rs index b385992..e4f67a9 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -137,7 +137,7 @@ pub fn mock_keys() -> HashMap { } pub fn mock_messages() -> Messages { - let mut messages = Messages::default(); + let mut messages = Messages::main(); messages.insert(MSG1_KEY.clone(), mock_message1()); messages.insert(MSG2_KEY.clone(), mock_message2()); @@ -171,12 +171,14 @@ pub fn mock_tunables() -> TunableValues { default_room: None, log_level: Level::INFO, message_shortcode_display: false, + normal_after_send: true, reaction_display: true, reaction_shortcode_display: false, read_receipt_send: true, read_receipt_display: true, request_timeout: 120, sort: SortOverrides::default().values(), + state_event_display: true, typing_notice_send: true, typing_notice_display: true, users: vec![(TEST_USER5.clone(), UserDisplayTunables { @@ -188,14 +190,17 @@ pub fn mock_tunables() -> TunableValues { open_command: None, external_edit_file_suffix: String::from(".md"), username_display: UserDisplayStyle::Username, + username_display_regex: Some(String::from(".*")), message_user_color: false, + mouse: Default::default(), notifications: Notifications { enabled: false, - via: NotifyVia::Desktop, + via: NotifyVia::default(), show_message: true, }, image_preview: None, user_gutter_width: 30, + tabstop: 4, } } diff --git a/src/windows/mod.rs b/src/windows/mod.rs index c7882be..1228781 100644 --- a/src/windows/mod.rs +++ b/src/windows/mod.rs @@ -5,7 +5,7 @@ //! //! Additionally, some of the iamb commands delegate behaviour to the current UI element. For //! example, [sending messages][crate::base::SendAction] delegate to the [room window][RoomState], -//! where we have the message bar and room ID easily accesible and resetable. +//! where we have the message bar and room ID easily accessible and resettable. use std::cmp::{Ord, Ordering, PartialOrd}; use std::fmt::{self, Display}; use std::ops::Deref; @@ -23,6 +23,7 @@ use matrix_sdk::{ RoomAliasId, RoomId, }, + RoomState as MatrixRoomState, }; use ratatui::{ @@ -75,11 +76,13 @@ use crate::base::{ SortFieldRoom, SortFieldUser, SortOrder, + SpaceAction, UnreadInfo, }; use self::{room::RoomState, welcome::WelcomeState}; use crate::message::MessageTimeStamp; +use feruca::Collator; pub mod room; pub mod welcome; @@ -168,7 +171,12 @@ fn user_cmp(a: &MemberItem, b: &MemberItem, field: &SortFieldUser) -> Ordering { } } -fn room_cmp(a: &T, b: &T, field: &SortFieldRoom) -> Ordering { +fn room_cmp( + a: &T, + b: &T, + field: &SortFieldRoom, + collator: &mut Collator, +) -> Ordering { match field { SortFieldRoom::Favorite => { let fava = a.has_tag(TagName::Favorite); @@ -184,7 +192,7 @@ fn room_cmp(a: &T, b: &T, field: &SortFieldRoom) -> Ordering { // If a has LowPriority and b doesn't, it should sort later in room list. lowa.cmp(&lowb) }, - SortFieldRoom::Name => a.name().cmp(b.name()), + SortFieldRoom::Name => collator.collate(a.name(), b.name()), SortFieldRoom::Alias => some_cmp(a.alias(), b.alias(), Ord::cmp), SortFieldRoom::RoomId => a.room_id().cmp(b.room_id()), SortFieldRoom::Unread => { @@ -195,6 +203,10 @@ fn room_cmp(a: &T, b: &T, field: &SortFieldRoom) -> Ordering { // sort larger timestamps towards the top. some_cmp(a.recent_ts(), b.recent_ts(), |a, b| b.cmp(a)) }, + SortFieldRoom::Invite => { + // sort invites before other rooms. + b.is_invite().cmp(&a.is_invite()) + }, } } @@ -203,9 +215,10 @@ fn room_fields_cmp( a: &T, b: &T, fields: &[SortColumn], + collator: &mut Collator, ) -> Ordering { for SortColumn(field, order) in fields { - match (room_cmp(a, b, field), order) { + match (room_cmp(a, b, field, collator), order) { (Ordering::Equal, _) => continue, (o, SortOrder::Ascending) => return o, (o, SortOrder::Descending) => return o.reverse(), @@ -213,7 +226,7 @@ fn room_fields_cmp( } // Break ties on ascending room id. - room_cmp(a, b, &SortFieldRoom::RoomId) + room_cmp(a, b, &SortFieldRoom::RoomId, collator) } fn user_fields_cmp( @@ -273,6 +286,7 @@ trait RoomLikeItem { fn recent_ts(&self) -> Option<&MessageTimeStamp>; fn alias(&self) -> Option<&RoomAliasId>; fn name(&self) -> &str; + fn is_invite(&self) -> bool; } #[inline] @@ -354,6 +368,19 @@ impl IambWindow { } } + pub async fn space_command( + &mut self, + act: SpaceAction, + ctx: ProgramContext, + store: &mut ProgramStore, + ) -> IambResult { + if let IambWindow::Room(w) = self { + w.space_command(act, ctx, store).await + } else { + return Err(IambError::NoSelectedRoom.into()); + } + } + pub async fn room_command( &mut self, act: RoomAction, @@ -496,7 +523,8 @@ impl WindowOps for IambWindow { .map(|room_info| DirectItem::new(room_info, store)) .collect::>(); let fields = &store.application.settings.tunables.sort.dms; - items.sort_by(|a, b| room_fields_cmp(a, b, fields)); + let collator = &mut store.application.collator; + items.sort_by(|a, b| room_fields_cmp(a, b, fields, collator)); state.set(items); @@ -541,7 +569,8 @@ impl WindowOps for IambWindow { .map(|room_info| RoomItem::new(room_info, store)) .collect::>(); let fields = &store.application.settings.tunables.sort.rooms; - items.sort_by(|a, b| room_fields_cmp(a, b, fields)); + let collator = &mut store.application.collator; + items.sort_by(|a, b| room_fields_cmp(a, b, fields, collator)); state.set(items); @@ -572,7 +601,8 @@ impl WindowOps for IambWindow { items.extend(dms); let fields = &store.application.settings.tunables.sort.chats; - items.sort_by(|a, b| room_fields_cmp(a, b, fields)); + let collator = &mut store.application.collator; + items.sort_by(|a, b| room_fields_cmp(a, b, fields, collator)); state.set(items); @@ -605,7 +635,8 @@ impl WindowOps for IambWindow { items.extend(dms); let fields = &store.application.settings.tunables.sort.chats; - items.sort_by(|a, b| room_fields_cmp(a, b, fields)); + let collator = &mut store.application.collator; + items.sort_by(|a, b| room_fields_cmp(a, b, fields, collator)); state.set(items); @@ -625,7 +656,8 @@ impl WindowOps for IambWindow { .map(|room| SpaceItem::new(room, store)) .collect::>(); let fields = &store.application.settings.tunables.sort.spaces; - items.sort_by(|a, b| room_fields_cmp(a, b, fields)); + let collator = &mut store.application.collator; + items.sort_by(|a, b| room_fields_cmp(a, b, fields, collator)); state.set(items); @@ -914,6 +946,10 @@ impl RoomLikeItem for GenericChatItem { fn is_unread(&self) -> bool { self.unread.is_unread() } + + fn is_invite(&self) -> bool { + self.room().state() == MatrixRoomState::Invited + } } impl Display for GenericChatItem { @@ -1024,11 +1060,15 @@ impl RoomLikeItem for RoomItem { fn is_unread(&self) -> bool { self.unread.is_unread() } + + fn is_invite(&self) -> bool { + self.room().state() == MatrixRoomState::Invited + } } impl Display for RoomItem { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, ":verify request {}", self.name) + write!(f, "{}", self.name) } } @@ -1124,6 +1164,10 @@ impl RoomLikeItem for DirectItem { fn is_unread(&self) -> bool { self.unread.is_unread() } + + fn is_invite(&self) -> bool { + self.room().state() == MatrixRoomState::Invited + } } impl Display for DirectItem { @@ -1223,11 +1267,15 @@ impl RoomLikeItem for SpaceItem { // XXX: this needs to check whether the space contains rooms with unread messages false } + + fn is_invite(&self) -> bool { + self.room().state() == MatrixRoomState::Invited + } } impl Display for SpaceItem { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, ":verify request {}", self.room_id()) + write!(f, "{}", self.name) } } @@ -1556,6 +1604,7 @@ mod tests { alias: Option, name: &'static str, unread: UnreadInfo, + invite: bool, } impl RoomLikeItem for &TestRoomItem { @@ -1582,10 +1631,16 @@ mod tests { fn is_unread(&self) -> bool { self.unread.is_unread() } + + fn is_invite(&self) -> bool { + self.invite + } } #[test] fn test_sort_rooms() { + let mut collator = Collator::default(); + let collator = &mut collator; let server = server_name!("example.com"); let room1 = TestRoomItem { @@ -1594,6 +1649,7 @@ mod tests { alias: Some(room_alias_id!("#room1:example.com").to_owned()), name: "Z", unread: UnreadInfo::default(), + invite: false, }; let room2 = TestRoomItem { @@ -1602,6 +1658,7 @@ mod tests { alias: Some(room_alias_id!("#a:example.com").to_owned()), name: "Unnamed Room", unread: UnreadInfo::default(), + invite: false, }; let room3 = TestRoomItem { @@ -1610,18 +1667,19 @@ mod tests { alias: None, name: "Cool Room", unread: UnreadInfo::default(), + invite: false, }; // Sort by Name ascending. let mut rooms = vec![&room1, &room2, &room3]; let fields = &[SortColumn(SortFieldRoom::Name, SortOrder::Ascending)]; - rooms.sort_by(|a, b| room_fields_cmp(a, b, fields)); + rooms.sort_by(|a, b| room_fields_cmp(a, b, fields, collator)); assert_eq!(rooms, vec![&room3, &room2, &room1]); // Sort by Name descending. let mut rooms = vec![&room1, &room2, &room3]; let fields = &[SortColumn(SortFieldRoom::Name, SortOrder::Descending)]; - rooms.sort_by(|a, b| room_fields_cmp(a, b, fields)); + rooms.sort_by(|a, b| room_fields_cmp(a, b, fields, collator)); assert_eq!(rooms, vec![&room1, &room2, &room3]); // Sort by Favorite and Alias before Name to show order matters. @@ -1631,7 +1689,7 @@ mod tests { SortColumn(SortFieldRoom::Alias, SortOrder::Ascending), SortColumn(SortFieldRoom::Name, SortOrder::Ascending), ]; - rooms.sort_by(|a, b| room_fields_cmp(a, b, fields)); + rooms.sort_by(|a, b| room_fields_cmp(a, b, fields, collator)); assert_eq!(rooms, vec![&room1, &room2, &room3]); // Now flip order of Favorite with Descending @@ -1641,12 +1699,14 @@ mod tests { SortColumn(SortFieldRoom::Alias, SortOrder::Ascending), SortColumn(SortFieldRoom::Name, SortOrder::Ascending), ]; - rooms.sort_by(|a, b| room_fields_cmp(a, b, fields)); + rooms.sort_by(|a, b| room_fields_cmp(a, b, fields, collator)); assert_eq!(rooms, vec![&room2, &room3, &room1]); } #[test] fn test_sort_room_recents() { + let mut collator = Collator::default(); + let collator = &mut collator; let server = server_name!("example.com"); let room1 = TestRoomItem { @@ -1655,6 +1715,7 @@ mod tests { alias: None, name: "Room 1", unread: UnreadInfo { unread: false, latest: None }, + invite: false, }; let room2 = TestRoomItem { @@ -1666,6 +1727,7 @@ mod tests { unread: false, latest: Some(MessageTimeStamp::OriginServer(40u32.into())), }, + invite: false, }; let room3 = TestRoomItem { @@ -1677,18 +1739,71 @@ mod tests { unread: false, latest: Some(MessageTimeStamp::OriginServer(20u32.into())), }, + invite: false, }; // Sort by Recent ascending. let mut rooms = vec![&room1, &room2, &room3]; let fields = &[SortColumn(SortFieldRoom::Recent, SortOrder::Ascending)]; - rooms.sort_by(|a, b| room_fields_cmp(a, b, fields)); + rooms.sort_by(|a, b| room_fields_cmp(a, b, fields, collator)); assert_eq!(rooms, vec![&room2, &room3, &room1]); // Sort by Recent descending. let mut rooms = vec![&room1, &room2, &room3]; let fields = &[SortColumn(SortFieldRoom::Recent, SortOrder::Descending)]; - rooms.sort_by(|a, b| room_fields_cmp(a, b, fields)); + rooms.sort_by(|a, b| room_fields_cmp(a, b, fields, collator)); assert_eq!(rooms, vec![&room1, &room3, &room2]); } + + #[test] + fn test_sort_room_invites() { + let mut collator = Collator::default(); + let collator = &mut collator; + let server = server_name!("example.com"); + + let room1 = TestRoomItem { + room_id: RoomId::new(server).to_owned(), + tags: vec![], + alias: None, + name: "Old room 1", + unread: UnreadInfo::default(), + invite: false, + }; + + let room2 = TestRoomItem { + room_id: RoomId::new(server).to_owned(), + tags: vec![], + alias: None, + name: "Old room 2", + unread: UnreadInfo::default(), + invite: false, + }; + + let room3 = TestRoomItem { + room_id: RoomId::new(server).to_owned(), + tags: vec![], + alias: None, + name: "New Fancy Room", + unread: UnreadInfo::default(), + invite: true, + }; + + // Sort invites first + let mut rooms = vec![&room1, &room2, &room3]; + let fields = &[ + SortColumn(SortFieldRoom::Invite, SortOrder::Ascending), + SortColumn(SortFieldRoom::Name, SortOrder::Ascending), + ]; + rooms.sort_by(|a, b| room_fields_cmp(a, b, fields, collator)); + assert_eq!(rooms, vec![&room3, &room1, &room2]); + + // Sort invites after + let mut rooms = vec![&room1, &room2, &room3]; + let fields = &[ + SortColumn(SortFieldRoom::Invite, SortOrder::Descending), + SortColumn(SortFieldRoom::Name, SortOrder::Ascending), + ]; + rooms.sort_by(|a, b| room_fields_cmp(a, b, fields, collator)); + assert_eq!(rooms, vec![&room1, &room2, &room3]); + } } diff --git a/src/windows/room/chat.rs b/src/windows/room/chat.rs index 427a2df..8a01a0a 100644 --- a/src/windows/room/chat.rs +++ b/src/windows/room/chat.rs @@ -14,7 +14,7 @@ use url::Url; use matrix_sdk::{ attachment::AttachmentConfig, - media::{MediaFormat, MediaRequest}, + media::{MediaFormat, MediaRequestParameters}, room::Room as MatrixRoom, ruma::{ events::reaction::ReactionEventContent, @@ -86,7 +86,14 @@ use crate::base::{ SendAction, }; -use crate::message::{text_to_message, Message, MessageEvent, MessageKey, MessageTimeStamp}; +use crate::message::{ + text_to_message, + Message, + MessageEvent, + MessageKey, + MessageTimeStamp, + TreeGenState, +}; use crate::worker::Requester; use super::scrollback::{Scrollback, ScrollbackState}; @@ -226,10 +233,14 @@ impl ChatState { let links = if let Some(html) = &msg.html { html.get_links() - } else if let Ok(url) = Url::parse(&msg.event.body()) { - vec![('0', url)] } else { - vec![] + linkify::LinkFinder::new() + .links(&msg.event.body()) + .filter_map(|u| Url::parse(u.as_str()).ok()) + .scan(TreeGenState { link_num: 0 }, |state, u| { + state.next_link_char().map(|c| (c, u)) + }) + .collect() }; if links.is_empty() { @@ -276,7 +287,7 @@ impl ChatState { } if !filename.exists() || flags.contains(DownloadFlags::FORCE) { - let req = MediaRequest { source, format: MediaFormat::File }; + let req = MediaRequestParameters { source, format: MediaFormat::File }; let bytes = media.get_media_content(&req, true).await.map_err(IambError::from)?; @@ -380,6 +391,7 @@ impl ChatState { MessageEvent::EncryptedRedacted(ev) => ev.event_id.clone(), MessageEvent::Original(ev) => ev.event_id.clone(), MessageEvent::Local(event_id, _) => event_id.clone(), + MessageEvent::State(ev) => ev.event_id().to_owned(), MessageEvent::Redacted(_) => { let msg = "Cannot react to a redacted message"; let err = UIError::Failure(msg.into()); @@ -417,6 +429,7 @@ impl ChatState { MessageEvent::EncryptedRedacted(ev) => ev.event_id.clone(), MessageEvent::Original(ev) => ev.event_id.clone(), MessageEvent::Local(event_id, _) => event_id.clone(), + MessageEvent::State(ev) => ev.event_id().to_owned(), MessageEvent::Redacted(_) => { let msg = "Cannot redact already redacted message"; let err = UIError::Failure(msg.into()); @@ -464,6 +477,7 @@ impl ChatState { MessageEvent::EncryptedRedacted(ev) => ev.event_id.clone(), MessageEvent::Original(ev) => ev.event_id.clone(), MessageEvent::Local(event_id, _) => event_id.clone(), + MessageEvent::State(ev) => ev.event_id().to_owned(), MessageEvent::Redacted(_) => { let msg = "Cannot unreact to a redacted message"; let err = UIError::Failure(msg.into()); @@ -596,7 +610,7 @@ impl ChatState { let dynimage = image::DynamicImage::ImageRgba8(imagebuf); let bytes = Vec::::new(); let mut buff = std::io::Cursor::new(bytes); - dynimage.write_to(&mut buff, image::ImageOutputFormat::Png)?; + dynimage.write_to(&mut buff, image::ImageFormat::Png)?; Ok(buff.into_inner()) }) .map_err(IambError::from)?; @@ -606,7 +620,7 @@ impl ChatState { let config = AttachmentConfig::new(); let resp = room - .send_attachment(name.as_ref(), &mime, bytes, config) + .send_attachment(name, &mime, bytes, config) .await .map_err(IambError::from)?; @@ -635,10 +649,7 @@ impl ChatState { } pub fn focus_toggle(&mut self) { - self.focus = match self.focus { - RoomFocus::Scrollback => RoomFocus::MessageBar, - RoomFocus::MessageBar => RoomFocus::Scrollback, - }; + self.focus.toggle(); } pub fn room(&self) -> &MatrixRoom { @@ -649,6 +660,14 @@ impl ChatState { &self.room_id } + pub fn auto_toggle_focus( + &mut self, + act: &EditorAction, + ctx: &ProgramContext, + ) -> Option { + auto_toggle_focus(&mut self.focus, act, ctx, &self.scrollback, &mut self.tbox) + } + pub fn typing_notice( &self, act: &EditorAction, @@ -751,8 +770,15 @@ impl Editable for ChatState { ctx: &ProgramContext, store: &mut ProgramStore, ) -> EditResult { + // Check whether we should automatically switch between the message bar + // or message scrollback, and use an adjusted action if we do so. + let adjusted = self.auto_toggle_focus(act, ctx); + let act = adjusted.as_ref().unwrap_or(act); + + // Send typing notice if needed. self.typing_notice(act, ctx, store); + // And now we can finally run the editor command. match delegate!(self, w => w.editor_command(act, ctx, store)) { res @ Ok(_) => res, Err(EditError::WrongBuffer(IambBufferId::Room(room_id, thread, focus))) @@ -849,16 +875,16 @@ impl PromptActions for ChatState { fn recall( &mut self, + filter: &RecallFilter, dir: &MoveDir1D, count: &Count, - prefixed: bool, ctx: &ProgramContext, _: &mut ProgramStore, ) -> EditResult, IambInfo> { let count = ctx.resolve(count); let rope = self.tbox.get(); - let text = self.sent.recall(&rope, &mut self.sent_scrollback, *dir, prefixed, count); + let text = self.sent.recall(&rope, &mut self.sent_scrollback, filter, *dir, count); if let Some(text) = text { self.tbox.set_text(text); @@ -882,9 +908,7 @@ impl Promptable for ChatState { match act { PromptAction::Submit => self.submit(ctx, store), PromptAction::Abort(empty) => self.abort(*empty, ctx, store), - PromptAction::Recall(dir, count, prefixed) => { - self.recall(dir, count, *prefixed, ctx, store) - }, + PromptAction::Recall(filter, dir, count) => self.recall(filter, dir, count, ctx, store), } } } @@ -906,7 +930,7 @@ impl<'a> Chat<'a> { } } -impl<'a> StatefulWidget for Chat<'a> { +impl StatefulWidget for Chat<'_> { type State = ChatState; fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { @@ -990,3 +1014,158 @@ fn cmd(open_command: &Vec) -> Option { } None } + +pub fn auto_toggle_focus( + focus: &mut RoomFocus, + act: &EditorAction, + ctx: &ProgramContext, + scrollback: &ScrollbackState, + tbox: &mut TextBoxState, +) -> Option { + let is_insert = ctx.get_insert_style().is_some(); + + match (focus, act) { + (f @ RoomFocus::Scrollback, _) if is_insert => { + // Insert mode commands should switch focus. + f.toggle(); + None + }, + (f @ RoomFocus::Scrollback, EditorAction::InsertText(_)) => { + // Pasting or otherwise inserting text should switch. + f.toggle(); + None + }, + ( + f @ RoomFocus::Scrollback, + EditorAction::Edit( + op, + EditTarget::Motion(mov @ MoveType::Line(MoveDir1D::Next), count), + ), + ) if ctx.resolve(op).is_motion() => { + let count = ctx.resolve(count); + + if count > 0 && scrollback.is_latest() { + // Trying to move down a line when already at the end of room history should + // switch. + f.toggle(); + + // And decrement the count for the action. + let count = count.saturating_sub(1).into(); + let target = EditTarget::Motion(mov.clone(), count); + let dec = EditorAction::Edit(op.clone(), target); + + Some(dec) + } else { + None + } + }, + ( + f @ RoomFocus::MessageBar, + EditorAction::Edit( + op, + EditTarget::Motion(mov @ MoveType::Line(MoveDir1D::Previous), count), + ), + ) if !is_insert && ctx.resolve(op).is_motion() => { + let count = ctx.resolve(count); + + if count > 0 && tbox.get_cursor().y == 0 { + // Trying to move up a line when already at the top of the msgbar should + // switch as long as we're not in Insert mode. + f.toggle(); + + // And decrement the count for the action. + let count = count.saturating_sub(1).into(); + let target = EditTarget::Motion(mov.clone(), count); + let dec = EditorAction::Edit(op.clone(), target); + + Some(dec) + } else { + None + } + }, + (RoomFocus::Scrollback, _) | (RoomFocus::MessageBar, _) => { + // Do not switch. + None + }, + } +} + +#[cfg(test)] +mod tests { + use super::*; + + use modalkit::actions::{EditAction, InsertTextAction}; + + use crate::tests::{mock_store, TEST_ROOM1_ID}; + + macro_rules! move_line { + ($dir: expr, $count: expr) => { + EditorAction::Edit( + EditAction::Motion.into(), + EditTarget::Motion(MoveType::Line($dir), $count.into()), + ) + }; + } + + #[tokio::test] + async fn test_auto_focus() { + let mut store = mock_store().await; + let ctx = ProgramContext::default(); + + let room_id = TEST_ROOM1_ID.clone(); + let scrollback = ScrollbackState::new(room_id.clone(), None); + + let id = IambBufferId::Room(room_id, None, RoomFocus::MessageBar); + let ebuf = store.load_buffer(id); + let mut tbox = TextBoxState::new(ebuf); + + // Start out focused on the scrollback. + let mut focused = RoomFocus::Scrollback; + + // Inserting text toggles: + let act = EditorAction::InsertText(InsertTextAction::Type( + Char::from('a').into(), + MoveDir1D::Next, + 1.into(), + )); + let res = auto_toggle_focus(&mut focused, &act, &ctx, &scrollback, &mut tbox); + assert_eq!(focused, RoomFocus::MessageBar); + assert!(res.is_none()); + + // Going down in message bar doesn't toggle: + let act = move_line!(MoveDir1D::Next, 1); + let res = auto_toggle_focus(&mut focused, &act, &ctx, &scrollback, &mut tbox); + assert_eq!(focused, RoomFocus::MessageBar); + assert!(res.is_none()); + + // But going up will: + let act = move_line!(MoveDir1D::Previous, 1); + let res = auto_toggle_focus(&mut focused, &act, &ctx, &scrollback, &mut tbox); + assert_eq!(focused, RoomFocus::Scrollback); + assert_eq!(res, Some(move_line!(MoveDir1D::Previous, 0))); + + // Going up in scrollback doesn't toggle: + let act = move_line!(MoveDir1D::Previous, 1); + let res = auto_toggle_focus(&mut focused, &act, &ctx, &scrollback, &mut tbox); + assert_eq!(focused, RoomFocus::Scrollback); + assert_eq!(res, None); + + // And then go back down: + let act = move_line!(MoveDir1D::Next, 1); + let res = auto_toggle_focus(&mut focused, &act, &ctx, &scrollback, &mut tbox); + assert_eq!(focused, RoomFocus::MessageBar); + assert_eq!(res, Some(move_line!(MoveDir1D::Next, 0))); + + // Go up 2 will go up 1 in scrollback: + let act = move_line!(MoveDir1D::Previous, 2); + let res = auto_toggle_focus(&mut focused, &act, &ctx, &scrollback, &mut tbox); + assert_eq!(focused, RoomFocus::Scrollback); + assert_eq!(res, Some(move_line!(MoveDir1D::Previous, 1))); + + // Go down 3 will go down 2 in messagebar: + let act = move_line!(MoveDir1D::Next, 3); + let res = auto_toggle_focus(&mut focused, &act, &ctx, &scrollback, &mut tbox); + assert_eq!(focused, RoomFocus::MessageBar); + assert_eq!(res, Some(move_line!(MoveDir1D::Next, 2))); + } +} diff --git a/src/windows/room/mod.rs b/src/windows/room/mod.rs index f959172..adb3a9c 100644 --- a/src/windows/room/mod.rs +++ b/src/windows/room/mod.rs @@ -26,7 +26,7 @@ use matrix_sdk::{ OwnedUserId, RoomId, }, - DisplayName, + RoomDisplayName, RoomState as MatrixRoomState, }; @@ -66,6 +66,7 @@ use crate::base::{ RoomAction, RoomField, SendAction, + SpaceAction, }; use self::chat::ChatState; @@ -139,7 +140,7 @@ impl RoomState { pub fn new( room: MatrixRoom, thread: Option, - name: DisplayName, + name: RoomDisplayName, tags: Option, store: &mut ProgramStore, ) -> Self { @@ -214,6 +215,18 @@ impl RoomState { } } + pub async fn space_command( + &mut self, + act: SpaceAction, + ctx: ProgramContext, + store: &mut ProgramStore, + ) -> IambResult { + match self { + RoomState::Space(space) => space.space_command(act, ctx, store).await, + RoomState::Chat(_) => Err(IambError::NoSelectedSpace.into()), + } + } + pub async fn send_command( &mut self, act: SendAction, @@ -406,7 +419,7 @@ impl RoomState { // Try creating the room alias on the server. let alias_create_req = CreateAliasRequest::new(orai.clone(), room.room_id().into()); - if let Err(e) = client.send(alias_create_req, None).await { + if let Err(e) = client.send(alias_create_req).await { if let Some(ClientApiErrorKind::Unknown) = e.client_api_error_kind() { // Ignore when it already exists. } else { @@ -447,7 +460,7 @@ impl RoomState { // If the room alias does not exist on the server, create it let alias_create_req = CreateAliasRequest::new(orai, room.room_id().into()); - if let Err(e) = client.send(alias_create_req, None).await { + if let Err(e) = client.send(alias_create_req).await { if let Some(ClientApiErrorKind::Unknown) = e.client_api_error_kind() { // Ignore when it already exists. } else { @@ -464,6 +477,9 @@ impl RoomState { RoomField::Aliases => { // This never happens, aliases is only used for showing }, + RoomField::Id => { + // This never happens, id is only used for showing + }, } Ok(vec![]) @@ -519,7 +535,7 @@ impl RoomState { .application .worker .client - .send(del_req, None) + .send(del_req) .await .map_err(IambError::from)?; }, @@ -552,13 +568,16 @@ impl RoomState { .application .worker .client - .send(del_req, None) + .send(del_req) .await .map_err(IambError::from)?; }, RoomField::Aliases => { // This will not happen, you cannot unset all aliases }, + RoomField::Id => { + // This never happens, id is only used for showing + }, } Ok(vec![]) @@ -572,7 +591,12 @@ impl RoomState { let msg = match field { RoomField::History => { let visibility = room.history_visibility(); - format!("Room history visibility: {visibility}") + let visibility = visibility.as_ref().map(|v| v.as_str()); + format!("Room history visibility: {}", visibility.unwrap_or("")) + }, + RoomField::Id => { + let id = room.room_id(); + format!("Room identifier: {id}") }, RoomField::Name => { match room.name() { diff --git a/src/windows/room/scrollback.rs b/src/windows/room/scrollback.rs index eeb6c2d..5f32897 100644 --- a/src/windows/room/scrollback.rs +++ b/src/windows/room/scrollback.rs @@ -79,14 +79,20 @@ fn nth_key_before(pos: MessageKey, n: usize, thread: &Messages) -> MessageKey { } fn nth_before(pos: MessageKey, n: usize, thread: &Messages) -> MessageCursor { - nth_key_before(pos, n, thread).into() + let key = nth_key_before(pos, n, thread); + + if matches!(thread.last_key_value(), Some((last, _)) if &key == last) { + MessageCursor::latest() + } else { + MessageCursor::from(key) + } } -fn nth_key_after(pos: MessageKey, n: usize, thread: &Messages) -> MessageKey { +fn nth_key_after(pos: MessageKey, n: usize, thread: &Messages) -> Option { let mut end = &pos; - let iter = thread.range(&pos..).enumerate(); + let mut iter = thread.range(&pos..).enumerate(); - for (i, (key, _)) in iter { + for (i, (key, _)) in iter.by_ref() { end = key; if i >= n { @@ -94,11 +100,12 @@ fn nth_key_after(pos: MessageKey, n: usize, thread: &Messages) -> MessageKey { } } - end.clone() + // Avoid returning the key if it's at the end. + iter.next().map(|_| end.clone()) } fn nth_after(pos: MessageKey, n: usize, thread: &Messages) -> MessageCursor { - nth_key_after(pos, n, thread).into() + nth_key_after(pos, n, thread).map(MessageCursor::from).unwrap_or_default() } fn prevmsg<'a>(key: &MessageKey, thread: &'a Messages) -> Option<&'a Message> { @@ -150,6 +157,10 @@ impl ScrollbackState { } } + pub fn is_latest(&self) -> bool { + self.cursor.timestamp.is_none() + } + pub fn goto_latest(&mut self) { self.cursor = MessageCursor::latest(); } @@ -829,8 +840,8 @@ impl EditorActions for ScrollbackState { fn complete( &mut self, + _: &CompletionStyle, _: &CompletionType, - _: &CompletionSelection, _: &CompletionDisplay, _: &ProgramContext, _: &mut ProgramStore, @@ -1284,7 +1295,7 @@ impl<'a> Scrollback<'a> { } } -impl<'a> StatefulWidget for Scrollback<'a> { +impl StatefulWidget for Scrollback<'_> { type State = ScrollbackState; fn render(self, area: Rect, buf: &mut Buffer, state: &mut Self::State) { @@ -1340,7 +1351,7 @@ impl<'a> StatefulWidget for Scrollback<'a> { for (key, item) in thread.range(&corner_key..) { let sel = key == cursor_key; - let (txt, mut msg_preview) = + let (txt, [mut msg_preview, mut reply_preview]) = item.show_with_preview(prev, foc && sel, &state.viewctx, info, settings); let incomplete_ok = !full || !sel; @@ -1357,11 +1368,17 @@ impl<'a> StatefulWidget for Scrollback<'a> { continue; } + // Only take the preview into the matching row number. + // `reply` and `msg` previews are on rows, + // so an `or` works to pick the one that matches (if any) let line_preview = match msg_preview { - // Only take the preview into the matching row number. Some((_, _, y)) if y as usize == row => msg_preview.take(), _ => None, - }; + } + .or(match reply_preview { + Some((_, _, y)) if y as usize == row => reply_preview.take(), + _ => None, + }); lines.push((key, row, line, line_preview)); sawit |= sel; @@ -1396,7 +1413,7 @@ impl<'a> StatefulWidget for Scrollback<'a> { // line. for (x, y, backend) in image_previews { let image_widget = Image::new(backend); - let mut rect = backend.rect(); + let mut rect = backend.area(); rect.x = x; rect.y = y; // Don't render outside of scrollback area @@ -1411,7 +1428,7 @@ impl<'a> StatefulWidget for Scrollback<'a> { { // If the cursor is at the last message, then update the read marker. if let Some((k, _)) = thread.last_key_value() { - info.set_receipt(settings.profile.user_id.clone(), k.1.clone()); + info.set_receipt(thread.1.clone(), settings.profile.user_id.clone(), k.1.clone()); } } @@ -1518,8 +1535,9 @@ mod tests { scrollback.edit(&EditAction::Motion, &next(1), &ctx, &mut store).unwrap(); assert_eq!(scrollback.cursor, MSG5_KEY.clone().into()); + // And one more becomes "latest" cursor: scrollback.edit(&EditAction::Motion, &next(1), &ctx, &mut store).unwrap(); - assert_eq!(scrollback.cursor, MSG1_KEY.clone().into()); + assert_eq!(scrollback.cursor, MessageCursor::latest()); } #[tokio::test] @@ -1553,7 +1571,7 @@ mod tests { // MSG1: | XXXday, Month NN 20XX | // | @user1:example.com writhe | // |------------------------------------------------------------| - let area = Rect::new(0, 0, 60, 4); + let area = Rect::new(0, 0, 60, 5); let mut buffer = Buffer::empty(area); scrollback.draw(area, &mut buffer, true, &mut store); diff --git a/src/windows/room/space.rs b/src/windows/room/space.rs index 7b6f967..912d313 100644 --- a/src/windows/room/space.rs +++ b/src/windows/room/space.rs @@ -2,11 +2,14 @@ use std::ops::{Deref, DerefMut}; use std::time::{Duration, Instant}; +use matrix_sdk::ruma::events::space::child::SpaceChildEventContent; +use matrix_sdk::ruma::events::StateEventType; use matrix_sdk::{ room::Room as MatrixRoom, ruma::{OwnedRoomId, RoomId}, }; +use modalkit::prelude::{EditInfo, InfoMessage}; use ratatui::{ buffer::Buffer, layout::Rect, @@ -22,9 +25,18 @@ use modalkit_ratatui::{ WindowOps, }; -use crate::base::{IambBufferId, IambInfo, ProgramStore, RoomFocus}; +use crate::base::{ + IambBufferId, + IambError, + IambInfo, + IambResult, + ProgramContext, + ProgramStore, + RoomFocus, + SpaceAction, +}; -use crate::windows::{room_fields_cmp, RoomItem}; +use crate::windows::{room_fields_cmp, RoomItem, RoomLikeItem}; const SPACE_HIERARCHY_DEBOUNCE: Duration = Duration::from_secs(5); @@ -68,6 +80,71 @@ impl SpaceState { last_fetch: self.last_fetch, } } + + pub async fn space_command( + &mut self, + act: SpaceAction, + _: ProgramContext, + store: &mut ProgramStore, + ) -> IambResult { + match act { + SpaceAction::SetChild(child_id, order, suggested) => { + if !self + .room + .can_user_send_state( + &store.application.settings.profile.user_id, + StateEventType::SpaceChild, + ) + .await + .map_err(IambError::from)? + { + return Err(IambError::InsufficientPermission.into()); + } + + let via = self.room.route().await.map_err(IambError::from)?; + let mut ev = SpaceChildEventContent::new(via); + ev.order = order; + ev.suggested = suggested; + let _ = self + .room + .send_state_event_for_key(&child_id, ev) + .await + .map_err(IambError::from)?; + + Ok(InfoMessage::from("Space updated").into()) + }, + SpaceAction::RemoveChild => { + let space = self.list.get().ok_or(IambError::NoSelectedRoomOrSpaceItem)?; + if !self + .room + .can_user_send_state( + &store.application.settings.profile.user_id, + StateEventType::SpaceChild, + ) + .await + .map_err(IambError::from)? + { + return Err(IambError::InsufficientPermission.into()); + } + + let ev = SpaceChildEventContent::new(vec![]); + let event_id = self + .room + .send_state_event_for_key(&space.room_id().to_owned(), ev) + .await + .map_err(IambError::from)?; + + // Fix for element (see https://github.com/element-hq/element-web/issues/29606) + let _ = self + .room + .redact(&event_id.event_id, Some("workaround for element bug"), None) + .await + .map_err(IambError::from)?; + + Ok(InfoMessage::from("Room removed").into()) + }, + } + } } impl TerminalCursor for SpaceState { @@ -107,7 +184,7 @@ impl<'a> Space<'a> { } } -impl<'a> StatefulWidget for Space<'a> { +impl StatefulWidget for Space<'_> { type State = SpaceState; fn render(self, area: Rect, buffer: &mut Buffer, state: &mut Self::State) { @@ -137,7 +214,8 @@ impl<'a> StatefulWidget for Space<'a> { }) .collect::>(); let fields = &self.store.application.settings.tunables.sort.rooms; - items.sort_by(|a, b| room_fields_cmp(a, b, fields)); + let collator = &mut self.store.application.collator; + items.sort_by(|a, b| room_fields_cmp(a, b, fields, collator)); state.list.set(items); state.last_fetch = Some(Instant::now()); diff --git a/src/worker.rs b/src/worker.rs index 9ca830c..d0392d1 100644 --- a/src/worker.rs +++ b/src/worker.rs @@ -20,11 +20,12 @@ use tracing::{error, warn}; use url::Url; use matrix_sdk::{ + authentication::matrix::MatrixSession, config::{RequestConfig, SyncSettings}, + deserialized_responses::DisplayName, encryption::verification::{SasVerification, Verification}, encryption::{BackupDownloadStrategy, EncryptionSettings}, event_handler::Ctx, - matrix_auth::MatrixSession, reqwest, room::{Messages, MessagesOptions, Room as MatrixRoom, RoomMember}, ruma::{ @@ -58,6 +59,7 @@ use matrix_sdk::{ typing::SyncTypingEvent, AnyInitialStateEvent, AnyMessageLikeEvent, + AnySyncStateEvent, AnyTimelineEvent, EmptyStateKey, InitialStateEvent, @@ -78,8 +80,8 @@ use matrix_sdk::{ }, Client, ClientBuildError, - DisplayName, Error as MatrixError, + RoomDisplayName, RoomMemberships, }; @@ -114,8 +116,7 @@ const IAMB_DEVICE_NAME: &str = "iamb"; const IAMB_USER_AGENT: &str = "iamb"; const MIN_MSG_LOAD: u32 = 50; -type MessageFetchResult = - IambResult<(Option, Vec<(AnyMessageLikeEvent, Vec)>)>; +type MessageFetchResult = IambResult<(Option, Vec<(AnyTimelineEvent, Vec)>)>; fn initial_devname() -> String { format!("{} on {}", IAMB_DEVICE_NAME, gethostname().to_string_lossy()) @@ -209,7 +210,7 @@ async fn update_event_receipts(info: &mut RoomInfo, room: &MatrixRoom, event_id: }; for (user_id, _) in receipts { - info.set_receipt(user_id, event_id.to_owned()); + info.set_receipt(ReceiptThread::Main, user_id, event_id.to_owned()); } } @@ -293,10 +294,8 @@ async fn load_older_one( let mut msgs = vec![]; for ev in chunk.into_iter() { - let msg = match ev.event.deserialize() { - Ok(AnyTimelineEvent::MessageLike(msg)) => msg, - Ok(AnyTimelineEvent::State(_)) => continue, - Err(_) => continue, + let Ok(msg) = ev.into_raw().deserialize() else { + continue; }; let event_id = msg.event_id(); @@ -311,6 +310,7 @@ async fn load_older_one( }, }; + let msg = msg.into_full_event(room_id.to_owned()); msgs.push((msg, receipts)); } @@ -338,27 +338,34 @@ fn load_insert( let _ = presences.get_or_default(sender); for user_id in receipts { - info.set_receipt(user_id, msg.event_id().to_owned()); + info.set_receipt(ReceiptThread::Main, user_id, msg.event_id().to_owned()); } match msg { - AnyMessageLikeEvent::RoomEncrypted(msg) => { + AnyTimelineEvent::MessageLike(AnyMessageLikeEvent::RoomEncrypted(msg)) => { info.insert_encrypted(msg); }, - AnyMessageLikeEvent::RoomMessage(msg) => { + AnyTimelineEvent::MessageLike(AnyMessageLikeEvent::RoomMessage(msg)) => { info.insert_with_preview( room_id.clone(), store.clone(), - *picker, + picker.clone(), msg, settings, client.media(), ); }, - AnyMessageLikeEvent::Reaction(ev) => { + AnyTimelineEvent::MessageLike(AnyMessageLikeEvent::Reaction(ev)) => { info.insert_reaction(ev); }, - _ => continue, + AnyTimelineEvent::MessageLike(_) => { + continue; + }, + AnyTimelineEvent::State(msg) => { + if settings.tunables.state_event_display { + info.insert_any_state(msg.into()); + } + }, } } @@ -440,7 +447,7 @@ async fn refresh_rooms(client: &Client, store: &AsyncProgramStore) { let mut dms = vec![]; for room in client.invited_rooms().into_iter() { - let name = room.display_name().await.unwrap_or(DisplayName::Empty).to_string(); + let name = room.cached_display_name().unwrap_or(RoomDisplayName::Empty).to_string(); let tags = room.tags().await.unwrap_or_default(); names.push((room.room_id().to_owned(), name)); @@ -455,7 +462,7 @@ async fn refresh_rooms(client: &Client, store: &AsyncProgramStore) { } for room in client.joined_rooms().into_iter() { - let name = room.display_name().await.unwrap_or(DisplayName::Empty).to_string(); + let name = room.cached_display_name().unwrap_or(RoomDisplayName::Empty).to_string(); let tags = room.tags().await.unwrap_or_default(); names.push((room.room_id().to_owned(), name)); @@ -490,31 +497,36 @@ async fn refresh_rooms_forever(client: &Client, store: &AsyncProgramStore) { async fn send_receipts_forever(client: &Client, store: &AsyncProgramStore) { let mut interval = tokio::time::interval(Duration::from_secs(2)); - let mut sent = HashMap::::default(); + let mut sent: HashMap> = Default::default(); loop { interval.tick().await; - let locked = store.lock().await; - let user_id = &locked.application.settings.profile.user_id; - let updates = client - .joined_rooms() - .into_iter() - .filter_map(|room| { - let room_id = room.room_id().to_owned(); - let info = locked.application.rooms.get(&room_id)?; - let new_receipt = info.get_receipt(user_id)?; - let old_receipt = sent.get(&room_id); - if Some(new_receipt) != old_receipt { - Some((room_id, new_receipt.clone())) - } else { - None + let mut locked = store.lock().await; + let ChatStore { settings, open_notifications, rooms, .. } = &mut locked.application; + let user_id = &settings.profile.user_id; + + let mut updates = Vec::new(); + for room in client.joined_rooms() { + let room_id = room.room_id(); + let Some(info) = rooms.get(room_id) else { + continue; + }; + + let changed = info.receipts(user_id).filter_map(|(thread, new_receipt)| { + let old_receipt = sent.get(room_id).and_then(|ts| ts.get(thread)); + let changed = Some(new_receipt) != old_receipt; + if changed { + open_notifications.remove(room_id); } - }) - .collect::>(); + changed.then(|| (room_id.to_owned(), thread.to_owned(), new_receipt.to_owned())) + }); + + updates.extend(changed); + } drop(locked); - for (room_id, new_receipt) in updates { + for (room_id, thread, new_receipt) in updates { use matrix_sdk::ruma::api::client::receipt::create_receipt::v3::ReceiptType; let Some(room) = client.get_room(&room_id) else { @@ -522,15 +534,11 @@ async fn send_receipts_forever(client: &Client, store: &AsyncProgramStore) { }; match room - .send_single_receipt( - ReceiptType::Read, - ReceiptThread::Unthreaded, - new_receipt.clone(), - ) + .send_single_receipt(ReceiptType::Read, thread.to_owned(), new_receipt.clone()) .await { Ok(()) => { - sent.insert(room_id, new_receipt); + sent.entry(room_id).or_default().insert(thread, new_receipt); }, Err(e) => tracing::warn!(?room_id, "Failed to set read receipt: {e}"), } @@ -603,7 +611,7 @@ fn oneshot() -> (ClientReply, ClientResponse) { return (reply, response); } -pub type FetchedRoom = (MatrixRoom, DisplayName, Option); +pub type FetchedRoom = (MatrixRoom, RoomDisplayName, Option); pub enum WorkerTask { Init(AsyncProgramStore, ClientReply<()>), @@ -1001,7 +1009,7 @@ impl ClientWorker { info.insert_with_preview( room_id.to_owned(), store.clone(), - *picker, + picker.clone(), full_ev, settings, client.media(), @@ -1043,14 +1051,32 @@ impl ClientWorker { let Some(receipts) = receipts.get(&ReceiptType::Read) else { continue; }; - for user_id in receipts.keys() { - info.set_receipt(user_id.to_owned(), event_id.clone()); + for (user_id, rcpt) in receipts.iter() { + info.set_receipt( + rcpt.thread.clone(), + user_id.to_owned(), + event_id.clone(), + ); } } } }, ); + if self.settings.tunables.state_event_display { + let _ = self.client.add_event_handler( + |ev: AnySyncStateEvent, room: MatrixRoom, store: Ctx| { + async move { + let room_id = room.room_id(); + let mut locked = store.lock().await; + + let info = locked.application.get_room_info(room_id.to_owned()); + info.insert_any_state(ev); + } + }, + ); + } + let _ = self.client.add_event_handler( |ev: OriginalSyncRoomRedactionEvent, room: MatrixRoom, @@ -1076,11 +1102,12 @@ impl ClientWorker { let room_id = room.room_id(); let user_id = ev.state_key; - let ambiguous_name = - ev.content.displayname.as_deref().unwrap_or_else(|| user_id.localpart()); + let ambiguous_name = DisplayName::new( + ev.content.displayname.as_deref().unwrap_or_else(|| user_id.as_str()), + ); let ambiguous = client .store() - .get_users_with_display_name(room_id, ambiguous_name) + .get_users_with_display_name(room_id, &ambiguous_name) .await .map(|users| users.len() > 1) .unwrap_or_default(); @@ -1309,7 +1336,7 @@ impl ClientWorker { // Remove the session.json file. std::fs::remove_file(&self.settings.session_json)?; - Ok(Some(InfoMessage::from("Sucessfully logged out"))) + Ok(Some(InfoMessage::from("Successfully logged out"))) } async fn direct_message(&mut self, user: OwnedUserId) -> IambResult { @@ -1346,7 +1373,7 @@ impl ClientWorker { async fn get_room(&mut self, room_id: OwnedRoomId) -> IambResult { if let Some(room) = self.client.get_room(&room_id) { - let name = room.display_name().await.map_err(IambError::from)?; + let name = room.cached_display_name().ok_or_else(|| IambError::UnknownRoom(room_id))?; let tags = room.tags().await.map_err(IambError::from)?; Ok((room, name, tags)) @@ -1389,7 +1416,7 @@ impl ClientWorker { req.limit = Some(1000u32.into()); req.max_depth = Some(1u32.into()); - let resp = self.client.send(req, None).await.map_err(IambError::from)?; + let resp = self.client.send(req).await.map_err(IambError::from)?; let rooms = resp.rooms.into_iter().map(|chunk| chunk.room_id).collect();