diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a3095ad..15bab5d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,4 +1,4 @@ -name: macOS CI +name: CI on: push: @@ -10,13 +10,15 @@ on: permissions: contents: read +env: + RUSTC_WRAPPER: "" + CARGO_TERM_COLOR: always + jobs: - macos: - name: macOS 14 + lint-test: + name: Format, Clippy, Tests runs-on: macos-14 timeout-minutes: 30 - env: - RUSTC_WRAPPER: "" steps: - name: Checkout @@ -36,10 +38,60 @@ jobs: run: cargo fmt --all --check - name: Clippy - run: cargo clippy --all-targets --all-features -- -D warnings + run: cargo clippy --workspace --all-targets --all-features -- -D warnings -D clippy::all -D clippy::pedantic -D clippy::nursery -D clippy::cargo - name: Test - run: cargo test --all-targets --all-features + run: cargo test --workspace --all-features + + build: + name: Build ${{ matrix.label }} + runs-on: ${{ matrix.runner }} + timeout-minutes: 45 + strategy: + fail-fast: false + matrix: + include: + - label: macOS Apple Silicon + runner: macos-14 + target: aarch64-apple-darwin + binary: target/aarch64-apple-darwin/release/bitengine + deps: none + - label: Linux x86_64 + runner: ubuntu-latest + target: x86_64-unknown-linux-gnu + binary: target/x86_64-unknown-linux-gnu/release/bitengine + deps: linux + - label: Linux ARM64 + runner: ubuntu-24.04-arm + target: aarch64-unknown-linux-gnu + binary: target/aarch64-unknown-linux-gnu/release/bitengine + deps: linux + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Linux GUI dependencies + if: matrix.deps == 'linux' + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + libx11-dev libxkbcommon-dev libwayland-dev libegl1-mesa-dev \ + libgtk-3-dev + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Cache Cargo + uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true - name: Release build - run: cargo build --release + run: cargo build --release --target ${{ matrix.target }} + + - name: Verify binary exists + shell: bash + run: test -f "${{ matrix.binary }}" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 70e1dac..a781850 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -9,23 +9,15 @@ on: permissions: contents: write -jobs: - build: - name: Build ${{ matrix.arch }} - strategy: - fail-fast: false - matrix: - include: - - os: macos-14 - target: aarch64-apple-darwin - arch: arm64 - - os: macos-13 - target: x86_64-apple-darwin - arch: x86_64 +env: + RUSTC_WRAPPER: "" + CARGO_TERM_COLOR: always - runs-on: ${{ matrix.os }} - env: - RUSTC_WRAPPER: "" +jobs: + macos-arm64: + name: macOS Apple Silicon + runs-on: macos-14 + timeout-minutes: 45 steps: - name: Checkout @@ -33,6 +25,8 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@stable + with: + targets: aarch64-apple-darwin - name: Cache Cargo uses: Swatinem/rust-cache@v2 @@ -40,50 +34,22 @@ jobs: cache-on-failure: true - name: Build release binary - run: cargo build --release --target ${{ matrix.target }} - - - name: Upload compiled binary - uses: actions/upload-artifact@v4 - with: - name: bitengine-${{ matrix.arch }} - path: target/${{ matrix.target }}/release/bitcoin_node_manager - if-no-files-found: error - - package: - name: Package universal app - runs-on: macos-14 - needs: build - env: - RUSTC_WRAPPER: "" - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Download arm64 binary - uses: actions/download-artifact@v4 - with: - name: bitengine-arm64 - path: /tmp/bitengine-release/arm64 - - - name: Download x86_64 binary - uses: actions/download-artifact@v4 - with: - name: bitengine-x86_64 - path: /tmp/bitengine-release/x86_64 + run: cargo build --release --target aarch64-apple-darwin - name: Assemble app bundle shell: bash run: | set -euo pipefail - APP_ROOT="/tmp/bitengine-release/dist/BitEngine.app" + APP_NAME="BitEngine" + APP_EXECUTABLE="BitEngine" + APP_ROOT="dist/${APP_NAME}.app" MACOS_DIR="$APP_ROOT/Contents/MacOS" RESOURCES_DIR="$APP_ROOT/Contents/Resources" SHORT_VERSION="${GITHUB_REF_NAME#v}" BUILD_VERSION="${GITHUB_RUN_NUMBER}" - mkdir -p "$MACOS_DIR" "$RESOURCES_DIR" + mkdir -p "$MACOS_DIR" "$RESOURCES_DIR" out cat > "$APP_ROOT/Contents/Info.plist" < @@ -95,7 +61,7 @@ jobs: CFBundleDisplayName BitEngine CFBundleExecutable - BitEngine + ${APP_EXECUTABLE} CFBundleIdentifier com.csd113.BitEngine CFBundleInfoDictionaryVersion @@ -104,6 +70,8 @@ jobs: BitEngine CFBundlePackageType APPL + CFBundleIconFile + app-icon CFBundleShortVersionString ${SHORT_VERSION} CFBundleVersion @@ -116,29 +84,117 @@ jobs: EOF - lipo -create \ - /tmp/bitengine-release/arm64/bitcoin_node_manager \ - /tmp/bitengine-release/x86_64/bitcoin_node_manager \ - -output "$MACOS_DIR/BitEngine" + cp target/aarch64-apple-darwin/release/bitengine "$MACOS_DIR/$APP_EXECUTABLE" + chmod 755 "$MACOS_DIR/$APP_EXECUTABLE" + cp app-icon.icns "$RESOURCES_DIR/app-icon.icns" + codesign --deep --force --verify --sign - "$APP_ROOT" + + file "$MACOS_DIR/$APP_EXECUTABLE" | tee out/macos-arm64-file.txt + file "$MACOS_DIR/$APP_EXECUTABLE" | grep -E "arm64|aarch64" + plutil -lint "$APP_ROOT/Contents/Info.plist" + ditto -c -k --keepParent "$APP_ROOT" out/BitEngine-macos-arm64.zip + + - name: Upload macOS artifact + uses: actions/upload-artifact@v4 + with: + name: BitEngine-macos-arm64.zip + path: out/BitEngine-macos-arm64.zip + if-no-files-found: error + + linux: + name: Linux ${{ matrix.label }} + runs-on: ${{ matrix.runner }} + timeout-minutes: 45 + strategy: + fail-fast: false + matrix: + include: + - label: x64 + runner: ubuntu-latest + target: x86_64-unknown-linux-gnu + artifact: BitEngine-linux-x64.tar.gz + - label: arm64 + runner: ubuntu-24.04-arm + target: aarch64-unknown-linux-gnu + artifact: BitEngine-linux-arm64.tar.gz + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Install Linux GUI dependencies + run: | + sudo apt-get update + sudo apt-get install -y --no-install-recommends \ + libx11-dev libxkbcommon-dev libwayland-dev libegl1-mesa-dev \ + libgtk-3-dev + + - name: Install Rust + uses: dtolnay/rust-toolchain@stable + with: + targets: ${{ matrix.target }} + + - name: Cache Cargo + uses: Swatinem/rust-cache@v2 + with: + cache-on-failure: true + + - name: Build release binary + run: cargo build --release --target ${{ matrix.target }} - chmod +x "$MACOS_DIR/BitEngine" - codesign --force --deep --sign "-" "$APP_ROOT" + - name: Package executable + shell: bash + run: | + set -euo pipefail + + mkdir -p "dist/BitEngine-${{ matrix.label }}" out + cp "target/${{ matrix.target }}/release/bitengine" "dist/BitEngine-${{ matrix.label }}/bitengine" + chmod 755 "dist/BitEngine-${{ matrix.label }}/bitengine" + cp README.md "dist/BitEngine-${{ matrix.label }}/README.md" - mkdir -p /tmp/bitengine-release/out - ditto -c -k --sequesterRsrc --keepParent \ - "$APP_ROOT" \ - "/tmp/bitengine-release/out/BitEngine-${SHORT_VERSION}.zip" + file "dist/BitEngine-${{ matrix.label }}/bitengine" | tee "out/${{ matrix.label }}-file.txt" + if [ "${{ matrix.label }}" = "arm64" ]; then + file "dist/BitEngine-${{ matrix.label }}/bitengine" | grep -E "aarch64|ARM aarch64" + else + file "dist/BitEngine-${{ matrix.label }}/bitengine" | grep -E "x86-64|x86_64" + fi - - name: Upload app artifact + tar -czf "out/${{ matrix.artifact }}" -C dist "BitEngine-${{ matrix.label }}" + + - name: Upload Linux artifact uses: actions/upload-artifact@v4 with: - name: BitEngine-app - path: /tmp/bitengine-release/out/BitEngine-*.zip + name: ${{ matrix.artifact }} + path: out/${{ matrix.artifact }} if-no-files-found: error + publish: + name: Publish GitHub release + if: startsWith(github.ref, 'refs/tags/v') + runs-on: ubuntu-latest + needs: + - macos-arm64 + - linux + + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + path: release-artifacts + merge-multiple: true + + - name: Verify artifact names + shell: bash + run: | + set -euo pipefail + test -f release-artifacts/BitEngine-macos-arm64.zip + test -f release-artifacts/BitEngine-linux-x64.tar.gz + test -f release-artifacts/BitEngine-linux-arm64.tar.gz + ! compgen -G "release-artifacts/*macos*x64*" >/dev/null + ! compgen -G "release-artifacts/*universal*" >/dev/null + - name: Publish GitHub release - if: startsWith(github.ref, 'refs/tags/v') uses: softprops/action-gh-release@v2 with: - files: /tmp/bitengine-release/out/BitEngine-*.zip + files: release-artifacts/* generate_release_notes: true diff --git a/CHANGELOG.md b/CHANGELOG.md index 39d687f..ae7ad07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 0.1.2 - 2026-05-15 + +- Renamed the app, config namespace, and packaged binary to `BitEngine` +- Bumped the crate and release version to `0.1.2` +- Added cross-platform support boundaries for macOS Apple Silicon, Linux x86_64, and Linux ARM64 +- Replaced universal/macOS Intel release packaging with supported-platform artifacts only +- Updated documentation for platform config paths, binary names, and release artifacts + ## 0.1.1 - 2026-04-11 - Split the UI into smaller rendering and update modules to reduce the size of `src/ui.rs` diff --git a/Cargo.lock b/Cargo.lock index d4bfd82..3ae3f71 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -18,23 +18,6 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" -[[package]] -name = "adler2" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" - -[[package]] -name = "ahash" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" -dependencies = [ - "getrandom 0.2.17", - "once_cell", - "version_check", -] - [[package]] name = "ahash" version = "0.8.12" @@ -48,31 +31,32 @@ dependencies = [ "zerocopy", ] -[[package]] -name = "allocator-api2" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" - [[package]] name = "android-activity" -version = "0.6.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" +checksum = "0f2a1bb052857d5dd49572219344a7332b31b76405648eabac5bc68978251bcd" dependencies = [ "android-properties", - "bitflags 2.11.0", + "bitflags 2.11.1", "cc", - "cesu8", "jni", - "jni-sys", "libc", "log", "ndk", "ndk-context", - "ndk-sys 0.6.0+11769913", + "ndk-sys", "num_enum", - "thiserror 1.0.69", + "thiserror 2.0.18", +] + +[[package]] +name = "android-build" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cac4c64175d504608cf239756339c07f6384a476f97f20a7043f92920b0b8fd" +dependencies = [ + "windows-sys 0.52.0", ] [[package]] @@ -96,15 +80,6 @@ version = "1.0.102" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" -[[package]] -name = "approx" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cab112f0a86d568ea0e627cc1d6be74a1e9cd55214684db5561995f6dad897c6" -dependencies = [ - "num-traits", -] - [[package]] name = "arrayref" version = "0.3.9" @@ -125,33 +100,11 @@ checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" [[package]] name = "ash" -version = "0.37.3+1.3.251" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e9c3835d686b0a6084ab4234fcd1b07dbf6e4767dce60874b12356a25ecd4a" -dependencies = [ - "libloading 0.7.4", -] - -[[package]] -name = "ashpd" -version = "0.11.1" +version = "0.38.0+1.3.281" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2f3f79755c74fd155000314eb349864caa787c6592eace6c6882dad873d9c39" +checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" dependencies = [ - "async-fs", - "async-net", - "enumflags2", - "futures-channel", - "futures-util", - "rand 0.9.2", - "raw-window-handle", - "serde", - "serde_repr", - "url", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "zbus 5.14.0", + "libloading", ] [[package]] @@ -192,17 +145,6 @@ dependencies = [ "slab", ] -[[package]] -name = "async-fs" -version = "2.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8034a681df4aed8b8edbd7fbe472401ecf009251c8b40556b304567052e294c5" -dependencies = [ - "async-lock", - "blocking", - "futures-lite", -] - [[package]] name = "async-io" version = "2.6.0" @@ -232,17 +174,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "async-net" -version = "2.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b948000fad4873c1c9339d60f2623323a0cfd3816e5181033c6a5cb68b2accf7" -dependencies = [ - "async-io", - "blocking", - "futures-lite", -] - [[package]] name = "async-process" version = "2.5.0" @@ -269,14 +200,14 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] name = "async-signal" -version = "0.2.13" +version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" +checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" dependencies = [ "async-io", "async-lock", @@ -304,7 +235,7 @@ checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -319,6 +250,28 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "aws-lc-rs" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ec2f1fc3ec205783a5da9a7e6c1509cc69dedf09a1949e412c1e18469326d00" +dependencies = [ + "aws-lc-sys", + "zeroize", +] + +[[package]] +name = "aws-lc-sys" +version = "0.41.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a2f9779ce85b93ab6170dd940ad0169b5766ff848247aff13bb788b832fe3f4" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] + [[package]] name = "base64" version = "0.22.1" @@ -327,22 +280,22 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bit-set" -version = "0.5.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0700ddab506f33b20a03b13996eccd309a48e5ff77d0d95926aa0210fb4e95f1" +checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" dependencies = [ "bit-vec", ] [[package]] name = "bit-vec" -version = "0.6.3" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "349f9b6a179ed607305526ca489b34ad0a41aed5f7980fa90eb03160b69598fb" +checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" [[package]] -name = "bitcoin-node-manager" -version = "0.1.1" +name = "bitengine" +version = "0.1.2" dependencies = [ "anyhow", "directories", @@ -354,7 +307,7 @@ dependencies = [ "serde", "serde_json", "tempfile", - "thiserror 1.0.69", + "thiserror 2.0.18", "tokio", ] @@ -366,9 +319,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" [[package]] name = "block" @@ -376,15 +329,6 @@ version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" -[[package]] -name = "block-buffer" -version = "0.10.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" -dependencies = [ - "generic-array", -] - [[package]] name = "block2" version = "0.5.1" @@ -422,12 +366,6 @@ version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" -[[package]] -name = "by_address" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64fa3c856b712db6612c019f14756e64e4bcea13337a6b33b696333a9eaa2d06" - [[package]] name = "bytemuck" version = "1.25.0" @@ -445,7 +383,7 @@ checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -460,7 +398,7 @@ version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "log", "polling", "rustix 0.38.44", @@ -474,7 +412,7 @@ version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "polling", "rustix 1.1.4", "slab", @@ -507,9 +445,9 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.56" +version = "1.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" dependencies = [ "find-msvc-tools", "jobserver", @@ -517,24 +455,12 @@ dependencies = [ "shlex", ] -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" -[[package]] -name = "cfg_aliases" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd16c4719339c4530435d38e511904438d07cce7950afa3718a84ac36c10e89e" - [[package]] name = "cfg_aliases" version = "0.2.1" @@ -581,44 +507,23 @@ dependencies = [ ] [[package]] -name = "codespan-reporting" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3538270d33cc669650c4b093848450d380def10c331d38c768e34cac80576e6e" -dependencies = [ - "termcolor", - "unicode-width", -] - -[[package]] -name = "com" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e17887fd17353b65b1b2ef1c526c83e26cd72e74f598a8dc1bee13a48f3d9f6" -dependencies = [ - "com_macros", -] - -[[package]] -name = "com_macros" -version = "0.6.0" +name = "cmake" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d375883580a668c7481ea6631fc1a8863e33cc335bf56bfad8d7e6d4b04b13a5" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" dependencies = [ - "com_macros_support", - "proc-macro2", - "syn 1.0.109", + "cc", ] [[package]] -name = "com_macros_support" -version = "0.6.0" +name = "codespan-reporting" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad899a1087a9296d5644792d7cb72b8e34c1bec8e7d4fbc002230169a6e8710c" +checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" dependencies = [ - "proc-macro2", - "quote", - "syn 1.0.109", + "serde", + "termcolor", + "unicode-width", ] [[package]] @@ -650,6 +555,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.7" @@ -663,8 +578,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" dependencies = [ "bitflags 1.3.2", - "core-foundation", - "core-graphics-types", + "core-foundation 0.9.4", + "core-graphics-types 0.1.3", "foreign-types", "libc", ] @@ -676,70 +591,54 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" +dependencies = [ + "bitflags 2.11.1", + "core-foundation 0.10.1", "libc", ] +[[package]] +name = "core_maths" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" +dependencies = [ + "libm", +] + [[package]] name = "cosmic-text" -version = "0.12.1" +version = "0.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59fd57d82eb4bfe7ffa9b1cec0c05e2fd378155b47f255a67983cb4afe0e80c2" +checksum = "173852283a9a57a3cbe365d86e74dc428a09c50421477d5ad6fe9d9509e37737" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "fontdb", + "harfrust", + "linebender_resource_handle", "log", "rangemap", - "rayon", "rustc-hash 1.1.0", - "rustybuzz", "self_cell", + "skrifa 0.37.0", + "smol_str", "swash", "sys-locale", - "ttf-parser 0.21.1", "unicode-bidi", "unicode-linebreak", "unicode-script", "unicode-segmentation", ] -[[package]] -name = "cpufeatures" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] - -[[package]] -name = "crc32fast" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "crossbeam-deque" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" -dependencies = [ - "crossbeam-epoch", - "crossbeam-utils", -] - -[[package]] -name = "crossbeam-epoch" -version = "0.9.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" -dependencies = [ - "crossbeam-utils", -] - [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -753,20 +652,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] -name = "crypto-common" -version = "0.1.7" +name = "cryoglyph" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +checksum = "08bc795bdbccdbd461736fb163930a009da6597b226d6f6fce33e7a8eb6ec519" dependencies = [ - "generic-array", - "typenum", + "cosmic-text", + "etagere", + "lru", + "rustc-hash 2.1.2", + "wgpu", ] [[package]] -name = "ctor-lite" -version = "0.1.2" +name = "ctor" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e162d0c2e2068eb736b71e5597eff0b9944e6b973cd9f37b6a288ab9bf20e300" +checksum = "83cf0d42651b16c6dfe68685716d18480d18a9c39c62d76e8cf3eb6ed5d8bcbf" +dependencies = [ + "dtor", +] [[package]] name = "cursor-icon" @@ -774,94 +679,25 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" -[[package]] -name = "d3d12" -version = "0.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3e3d747f100290a1ca24b752186f61f6637e1deffe3bf6320de6fcb29510a307" -dependencies = [ - "bitflags 2.11.0", - "libloading 0.8.9", - "winapi", -] - -[[package]] -name = "dark-light" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a76fa97167fa740dcdbfe18e8895601e1bc36525f09b044e00916e717c03a3c" -dependencies = [ - "dconf_rs", - "detect-desktop-environment", - "dirs", - "objc", - "rust-ini", - "web-sys", - "winreg", - "zbus 4.4.0", -] - -[[package]] -name = "dconf_rs" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7046468a81e6a002061c01e6a7c83139daf91b11c30e66795b13217c2d885c8b" - -[[package]] -name = "detect-desktop-environment" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21d8ad60dd5b13a4ee6bd8fa2d5d88965c597c67bce32b5fc49c94f55cb50810" - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - [[package]] name = "directories" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35" -dependencies = [ - "dirs-sys 0.4.1", -] - -[[package]] -name = "dirs" -version = "4.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca3aa72a6f96ea37bbc5aa912f6788242832f75369bdfdadcb0e38423f100059" -dependencies = [ - "dirs-sys 0.3.7", -] - -[[package]] -name = "dirs-sys" -version = "0.3.7" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" dependencies = [ - "libc", - "redox_users", - "winapi", + "dirs-sys", ] [[package]] name = "dirs-sys" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", "redox_users", - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -876,7 +712,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "block2 0.6.2", "libc", "objc2 0.6.4", @@ -890,7 +726,7 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -899,14 +735,17 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" dependencies = [ - "libloading 0.8.9", + "libloading", ] [[package]] -name = "dlv-list" -version = "0.3.0" +name = "document-features" +version = "0.2.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0688c2a7f92e427f44895cd63841bff7b29f8d7a1648b9e7e07a4a365b2e1257" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] [[package]] name = "downcast-rs" @@ -921,63 +760,28 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" [[package]] -name = "drm" -version = "0.14.2" +name = "dtor" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5b71449a23fe79542d6527ca572844b2016abf9573c49e43144d546b1735aec" -dependencies = [ - "bitflags 2.11.0", - "bytemuck", - "bytemuck_derive", - "drm-ffi", - "drm-fourcc", - "libc", - "rustix 1.1.4", -] +checksum = "edf234dd1594d6dd434a8fb8cada51ddbbc593e40e4a01556a0b31c62da2775b" [[package]] -name = "drm-ffi" -version = "0.9.1" +name = "dunce" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51a91c9b32ac4e8105dec255e849e0d66e27d7c34d184364fb93e469db08f690" -dependencies = [ - "drm-sys", - "rustix 1.1.4", -] +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] -name = "drm-fourcc" -version = "2.2.0" +name = "endi" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0aafbcdb8afc29c1a7ee5fbe53b5d62f4565b35a042a662ca9fecd0b54dae6f4" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" [[package]] -name = "drm-sys" -version = "0.8.1" +name = "enumflags2" +version = "0.7.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8e1361066d91f5ffccff060a3c3be9c3ecde15be2959c1937595f7a82a9f8" -dependencies = [ - "libc", - "linux-raw-sys 0.9.4", -] - -[[package]] -name = "either" -version = "1.15.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" - -[[package]] -name = "endi" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" - -[[package]] -name = "enumflags2" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" +checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" dependencies = [ "enumflags2_derive", "serde", @@ -991,7 +795,7 @@ checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1028,9 +832,9 @@ dependencies = [ [[package]] name = "euclid" -version = "0.22.13" +version = "0.22.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df61bf483e837f88d5c2291dcf55c67be7e676b3a51acc48db3a7b163b91ed63" +checksum = "f1a05365e3b1c6d1650318537c7460c6923f1abdd272ad6842baa2b509957a06" dependencies = [ "num-traits", ] @@ -1056,26 +860,11 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "fast-srgb8" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd2e7510819d6fbf51a5545c8f922716ecfb14df168a3242f7d33e0239efe6a1" - [[package]] name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - -[[package]] -name = "fdeflate" -version = "0.3.7" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" -dependencies = [ - "simd-adler32", -] +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "find-msvc-tools" @@ -1084,26 +873,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] -name = "flate2" -version = "1.1.9" +name = "foldhash" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" -dependencies = [ - "crc32fast", - "miniz_oxide", -] +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "foldhash" -version = "0.1.5" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" [[package]] name = "font-types" -version = "0.7.3" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39a654f404bbcbd48ea58c617c2993ee91d1cb63727a37bf2323a4edeed1b8c5" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "font-types" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3971f9a5ca983419cdc386941ba3b9e1feba01a0ab888adf78739feb2798492" +checksum = "5b38ad915f6dadd993ced50848a8291a543bd41ca62bc10740d5e64e2ab4cfd7" dependencies = [ "bytemuck", ] @@ -1119,16 +913,16 @@ dependencies = [ [[package]] name = "fontdb" -version = "0.16.2" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0299020c3ef3f60f526a4f64ab4a3d4ce116b1acbf24cdd22da0068e5d81dc3" +checksum = "457e789b3d1202543297a350643cf459f836cade38934e7a4cf6a39e7cde2905" dependencies = [ "fontconfig-parser", "log", "memmap2", "slotmap", "tinyvec", - "ttf-parser 0.20.0", + "ttf-parser", ] [[package]] @@ -1149,7 +943,7 @@ checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1167,6 +961,12 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + [[package]] name = "futures" version = "0.3.32" @@ -1236,7 +1036,7 @@ checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -1268,16 +1068,6 @@ dependencies = [ "slab", ] -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - [[package]] name = "gethostname" version = "1.1.0" @@ -1347,9 +1137,9 @@ checksum = "151665d9be52f9bb40fc7966565d39666f2d1e69233571b71b87791c7e0528b3" [[package]] name = "glow" -version = "0.13.1" +version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1" +checksum = "c5e5ea60d70410161c8bf5da3fdfeaa1c72ed2c15f8bbb9d19fe3a4fad085f08" dependencies = [ "js-sys", "slotmap", @@ -1359,9 +1149,9 @@ dependencies = [ [[package]] name = "glutin_wgl_sys" -version = "0.5.0" +version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead" +checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" dependencies = [ "gl_generator", ] @@ -1372,7 +1162,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "gpu-alloc-types", ] @@ -1382,40 +1172,39 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", ] [[package]] name = "gpu-allocator" -version = "0.25.0" +version = "0.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f56f6318968d03c18e1bcf4857ff88c61157e9da8e47c5f29055d60e1228884" +checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" dependencies = [ "log", "presser", "thiserror 1.0.69", - "winapi", - "windows", + "windows 0.58.0", ] [[package]] name = "gpu-descriptor" -version = "0.2.4" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cc11df1ace8e7e564511f53af41f3e42ddc95b56fd07b3f4445d2a6048bc682c" +checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "gpu-descriptor-types", - "hashbrown 0.14.5", + "hashbrown 0.15.5", ] [[package]] name = "gpu-descriptor-types" -version = "0.1.2" +version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6bf0b36e6f090b7e1d8a4b49c0cb81c1f8376f72198c65dd3ad9ff3556b8b78c" +checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", ] [[package]] @@ -1436,26 +1225,21 @@ checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" dependencies = [ "cfg-if", "crunchy", + "num-traits", "zerocopy", ] [[package]] -name = "hashbrown" -version = "0.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" -dependencies = [ - "ahash 0.7.8", -] - -[[package]] -name = "hashbrown" -version = "0.14.5" +name = "harfrust" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "92c020db12c71d8a12a3fe7607873cade3a01a6287e29d540c8723276221b9d8" dependencies = [ - "ahash 0.8.12", - "allocator-api2", + "bitflags 2.11.1", + "bytemuck", + "core_maths", + "read-fonts 0.35.0", + "smallvec", ] [[package]] @@ -1464,7 +1248,7 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -1472,21 +1256,15 @@ name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +dependencies = [ + "foldhash 0.2.0", +] [[package]] -name = "hassle-rs" -version = "0.11.0" +name = "hashbrown" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af2a7e73e1f34c48da31fb668a907f250794837e08faa144fd24f0b8b741e890" -dependencies = [ - "bitflags 2.11.0", - "com", - "libc", - "libloading 0.8.9", - "thiserror 1.0.69", - "widestring", - "winapi", -] +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "heck" @@ -1553,9 +1331,9 @@ checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "hyper" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" dependencies = [ "atomic-waker", "bytes", @@ -1566,7 +1344,6 @@ dependencies = [ "httparse", "itoa", "pin-project-lite", - "pin-utils", "smallvec", "tokio", "want", @@ -1574,19 +1351,17 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.7" +version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ "http", "hyper", "hyper-util", "rustls", - "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", - "webpki-roots", ] [[package]] @@ -1614,191 +1389,198 @@ dependencies = [ [[package]] name = "iced" -version = "0.13.1" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88acfabc84ec077eaf9ede3457ffa3a104626d79022a9bf7f296093b1d60c73f" +checksum = "000e01026c93ba643f8357a3db3ada0e6555265a377f6f9291c472f6dd701fb3" dependencies = [ "iced_core", + "iced_debug", "iced_futures", "iced_renderer", + "iced_runtime", "iced_widget", "iced_winit", - "thiserror 1.0.69", + "thiserror 2.0.18", ] [[package]] name = "iced_core" -version = "0.13.2" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0013a238275494641bf8f1732a23a808196540dc67b22ff97099c044ae4c8a1c" +checksum = "91ab1937d699403e7e69252ae743a902bcee9f4ab2052cc4c9a46fcf34729d85" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "bytes", - "dark-light", "glam", + "lilt", "log", "num-traits", - "once_cell", - "palette", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", "smol_str", - "thiserror 1.0.69", + "thiserror 2.0.18", "web-time", ] [[package]] -name = "iced_futures" -version = "0.13.2" +name = "iced_debug" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c04a6745ba2e80f32cf01e034fd00d853aa4f4cd8b91888099cb7aaee0d5d7c" +checksum = "25035ab0215a620e53f4103e36fc4e59a1fb2817e4bfc38a30ad27b4202ea0be" dependencies = [ - "futures", "iced_core", + "iced_futures", "log", - "rustc-hash 2.1.1", - "tokio", - "wasm-bindgen-futures", - "wasm-timer", ] [[package]] -name = "iced_glyphon" -version = "0.6.0" +name = "iced_futures" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41c3bb56f1820ca252bc1d0994ece33d233a55657c0c263ea7cb16895adbde82" +checksum = "8c0c85ccad42dfbec7293c36c018af0ea0dbcc52d137a4a9a0b0f6822a3fdf0a" dependencies = [ - "cosmic-text", - "etagere", - "lru", - "rustc-hash 2.1.1", - "wgpu", + "futures", + "iced_core", + "log", + "rustc-hash 2.1.2", + "tokio", + "wasm-bindgen-futures", + "wasmtimer", ] [[package]] name = "iced_graphics" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba25a18cfa6d5cc160aca7e1b34f73ccdff21680fa8702168c09739767b6c66f" +checksum = "234ca1c2cec4155055f68fa5fad1b5242c496ac8238d80a259bca382fb44a102" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "bytemuck", "cosmic-text", "half", "iced_core", "iced_futures", "log", - "once_cell", "raw-window-handle", - "rustc-hash 2.1.1", - "thiserror 1.0.69", + "rustc-hash 2.1.2", + "thiserror 2.0.18", "unicode-segmentation", ] +[[package]] +name = "iced_program" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dfafec2947cda688d8eb00dac337ba11aa60f9ef6335aed343e189d26e4a673" +dependencies = [ + "iced_graphics", + "iced_runtime", +] + [[package]] name = "iced_renderer" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73558208059f9e622df2bf434e044ee2f838ce75201a023cf0ca3e1244f46c2a" +checksum = "250cc0802408e8c077986ec56c7d07c65f423ee658a4b9fd795a1f2aae5dac05" dependencies = [ "iced_graphics", "iced_tiny_skia", "iced_wgpu", "log", - "thiserror 1.0.69", + "thiserror 2.0.18", ] [[package]] name = "iced_runtime" -version = "0.13.2" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "348b5b2c61c934d88ca3b0ed1ed913291e923d086a66fa288ce9669da9ef62b5" +checksum = "d1889b819ce4c06674183242e336c8d49465665441396914dc07cc86f44fa8d4" dependencies = [ "bytes", "iced_core", "iced_futures", "raw-window-handle", - "thiserror 1.0.69", + "thiserror 2.0.18", ] [[package]] name = "iced_tiny_skia" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c625d368284fcc43b0b36b176f76eff1abebe7959dd58bd8ce6897d641962a50" +checksum = "fe0acf8b75a3bc914aff5f2329fdffc1b36eeaea29dda0e4bd232f1c62e9cc3d" dependencies = [ "bytemuck", "cosmic-text", + "iced_debug", "iced_graphics", "kurbo", "log", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", "softbuffer", "tiny-skia", ] [[package]] name = "iced_wgpu" -version = "0.13.5" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15708887133671d2bcc6c1d01d1f176f43a64d6cdc3b2bf893396c3ee498295f" +checksum = "ff144a999b0ca0f8a10257934500060240825c42e950ec0ebee9c8ae30561c13" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "bytemuck", + "cryoglyph", "futures", "glam", "guillotiere", - "iced_glyphon", + "iced_debug", "iced_graphics", "log", - "once_cell", - "rustc-hash 2.1.1", - "thiserror 1.0.69", + "rustc-hash 2.1.2", + "thiserror 2.0.18", "wgpu", ] [[package]] name = "iced_widget" -version = "0.13.4" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81429e1b950b0e4bca65be4c4278fea6678ea782030a411778f26fa9f8983e1d" +checksum = "b1596afa0d3109c2618e8bc12bae6c11d3064df8f95c42dfce570397dbe957ab" dependencies = [ "iced_renderer", - "iced_runtime", + "log", "num-traits", - "once_cell", - "rustc-hash 2.1.1", - "thiserror 1.0.69", + "rustc-hash 2.1.2", + "thiserror 2.0.18", "unicode-segmentation", ] [[package]] name = "iced_winit" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f44cd4e1c594b6334f409282937bf972ba14d31fedf03c23aa595d982a2fda28" +checksum = "8b7dbedc47562d1de3b9707d939f678b88c382004b7ab5a18f7a7dd723162d75" dependencies = [ - "iced_futures", - "iced_graphics", - "iced_runtime", + "iced_debug", + "iced_program", "log", - "rustc-hash 2.1.1", - "thiserror 1.0.69", + "mundy", + "rustc-hash 2.1.2", + "thiserror 2.0.18", "tracing", "wasm-bindgen-futures", "web-sys", - "winapi", "window_clipboard", "winit", ] [[package]] name = "icu_collections" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c" dependencies = [ "displaydoc", "potential_utf", + "utf8_iter", "yoke", "zerofrom", "zerovec", @@ -1806,9 +1588,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", "litemap", @@ -1819,9 +1601,9 @@ dependencies = [ [[package]] name = "icu_normalizer" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" +checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4" dependencies = [ "icu_collections", "icu_normalizer_data", @@ -1833,15 +1615,15 @@ dependencies = [ [[package]] name = "icu_normalizer_data" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" +checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38" [[package]] name = "icu_properties" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" +checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de" dependencies = [ "icu_collections", "icu_locale_core", @@ -1853,15 +1635,15 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.2" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" +checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", "icu_locale_core", @@ -1891,9 +1673,9 @@ dependencies = [ [[package]] name = "idna_adapter" -version = "1.2.1" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +checksum = "cb68373c0d6620ef8105e855e7745e18b0d00d3bdb07fb532e434244cdb9a714" dependencies = [ "icu_normalizer", "icu_properties", @@ -1901,68 +1683,85 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.13.0" +version = "2.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" +checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.16.1", + "hashbrown 0.17.1", "serde", "serde_core", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - [[package]] name = "ipnet" version = "2.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d98f6fed1fde3f8c21bc40a1abb88dd75e67924f9cffc3ef95607bad8017f8e2" -[[package]] -name = "iri-string" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" -dependencies = [ - "memchr", - "serde", -] - [[package]] name = "itoa" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" +checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "jni" -version = "0.21.1" +version = "0.22.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" dependencies = [ - "cesu8", "cfg-if", "combine", - "jni-sys", + "jni-macros", + "jni-sys 0.4.1", "log", - "thiserror 1.0.69", + "simd_cesu8", + "thiserror 2.0.18", "walkdir", - "windows-sys 0.45.0", + "windows-link", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn", ] [[package]] name = "jni-sys" -version = "0.3.0" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" +dependencies = [ + "jni-sys 0.4.1", +] + +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn", +] [[package]] name = "jobserver" @@ -1976,10 +1775,12 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.91" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +checksum = "67df7112613f8bfd9150013a0314e196f4800d3201ae742489d999db2f979f08" dependencies = [ + "cfg-if", + "futures-util", "once_cell", "wasm-bindgen", ] @@ -1991,7 +1792,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" dependencies = [ "libc", - "libloading 0.8.9", + "libloading", "pkg-config", ] @@ -2019,19 +1820,9 @@ checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" [[package]] name = "libc" -version = "0.2.182" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" - -[[package]] -name = "libloading" -version = "0.7.4" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libloading" @@ -2051,27 +1842,36 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.14" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "libc", "plain", - "redox_syscall 0.7.3", + "redox_syscall 0.7.5", ] [[package]] -name = "linux-raw-sys" -version = "0.4.15" +name = "lilt" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" +checksum = "f67562e5eff6b20553fa9be1c503356768420994e28f67e3eafe6f41910e57ad" +dependencies = [ + "web-time", +] + +[[package]] +name = "linebender_resource_handle" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a5ff6bcca6c4867b1c4fd4ef63e4db7436ef363e0ad7531d1558856bae64f4" [[package]] name = "linux-raw-sys" -version = "0.9.4" +version = "0.4.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd945864f07fe9f5371a27ad7b52a172b4b499999f1d97574c9fa68373937e12" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" [[package]] name = "linux-raw-sys" @@ -2081,9 +1881,15 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.1" +version = "0.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" + +[[package]] +name = "litrs" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" [[package]] name = "lock_api" @@ -2102,9 +1908,9 @@ checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "lru" -version = "0.12.5" +version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "234cf4f4a04dc1f57e24b96cc0cd600cf2af460d4161ac5ecdd0af8e1f3b2a38" +checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" [[package]] name = "lru-slab" @@ -2147,34 +1953,24 @@ dependencies = [ [[package]] name = "metal" -version = "0.27.0" +version = "0.32.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c43f73953f8cbe511f021b58f18c3ce1c3d1ae13fe953293e13345bf83217f25" +checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "block", - "core-graphics-types", + "core-graphics-types 0.2.0", "foreign-types", "log", "objc", "paste", ] -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - [[package]] name = "mio" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "wasi", @@ -2182,23 +1978,54 @@ dependencies = [ ] [[package]] -name = "naga" -version = "0.19.2" +name = "mundy" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e3524642f53d9af419ab5e8dd29d3ba155708267667c2f3f06c88c9e130843" +checksum = "f32eb0db40f2df2bcfb05c93b8f73938d4c26ce9ac8881f1df0c8d3296921a73" dependencies = [ - "bit-set", - "bitflags 2.11.0", - "codespan-reporting", - "hexf-parse", - "indexmap", - "log", - "num-traits", - "rustc-hash 1.1.0", - "spirv", - "termcolor", - "thiserror 1.0.69", - "unicode-xid", + "android-build", + "async-io", + "cfg-if", + "dispatch", + "futures-channel", + "futures-lite", + "jni", + "ndk-context", + "objc2 0.6.4", + "objc2-app-kit 0.3.2", + "objc2-foundation 0.3.2", + "pin-project-lite", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows 0.62.2", + "zbus", +] + +[[package]] +name = "naga" +version = "27.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "066cf25f0e8b11ee0df221219010f213ad429855f57c494f995590c861a9a7d8" +dependencies = [ + "arrayvec", + "bit-set", + "bitflags 2.11.1", + "cfg-if", + "cfg_aliases", + "codespan-reporting", + "half", + "hashbrown 0.16.1", + "hexf-parse", + "indexmap", + "libm", + "log", + "num-traits", + "once_cell", + "rustc-hash 1.1.0", + "spirv", + "thiserror 2.0.18", + "unicode-ident", ] [[package]] @@ -2207,10 +2034,10 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.11.0", - "jni-sys", + "bitflags 2.11.1", + "jni-sys 0.3.1", "log", - "ndk-sys 0.6.0+11769913", + "ndk-sys", "num_enum", "raw-window-handle", "thiserror 1.0.69", @@ -2222,35 +2049,13 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" -[[package]] -name = "ndk-sys" -version = "0.5.0+25.2.9519653" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8c196769dd60fd4f363e11d948139556a344e79d451aeb2fa2fd040738ef7691" -dependencies = [ - "jni-sys", -] - [[package]] name = "ndk-sys" version = "0.6.0+11769913" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" dependencies = [ - "jni-sys", -] - -[[package]] -name = "nix" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" -dependencies = [ - "bitflags 2.11.0", - "cfg-if", - "cfg_aliases 0.2.1", - "libc", - "memoffset", + "jni-sys 0.3.1", ] [[package]] @@ -2260,13 +2065,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", + "libm", ] [[package]] name = "num_enum" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" +checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26" dependencies = [ "num_enum_derive", "rustversion", @@ -2274,14 +2080,14 @@ dependencies = [ [[package]] name = "num_enum_derive" -version = "0.7.5" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" +checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -2291,7 +2097,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" dependencies = [ "malloc_buf", - "objc_exception", ] [[package]] @@ -2325,12 +2130,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "block2 0.5.1", "libc", "objc2 0.5.2", - "objc2-core-data", - "objc2-core-image", + "objc2-core-data 0.2.2", + "objc2-core-image 0.2.2", "objc2-foundation 0.2.2", "objc2-quartz-core 0.2.2", ] @@ -2341,10 +2146,19 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "block2 0.6.2", + "libc", "objc2 0.6.4", + "objc2-cloud-kit 0.3.2", + "objc2-core-data 0.3.2", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image 0.3.2", + "objc2-core-text", + "objc2-core-video", "objc2-foundation 0.3.2", + "objc2-quartz-core 0.3.2", ] [[package]] @@ -2353,13 +2167,24 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", "objc2-foundation 0.2.2", ] +[[package]] +name = "objc2-cloud-kit" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73ad74d880bb43877038da939b7427bba67e9dd42004a18b809ba7d87cee241c" +dependencies = [ + "bitflags 2.11.1", + "objc2 0.6.4", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-contacts" version = "0.2.2" @@ -2377,19 +2202,30 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", ] +[[package]] +name = "objc2-core-data" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b402a653efbb5e82ce4df10683b6b28027616a2715e90009947d50b8dd298fa" +dependencies = [ + "bitflags 2.11.1", + "objc2 0.6.4", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-core-foundation" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "dispatch2", "objc2 0.6.4", ] @@ -2400,7 +2236,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "dispatch2", "objc2 0.6.4", "objc2-core-foundation", @@ -2419,6 +2255,16 @@ dependencies = [ "objc2-metal", ] +[[package]] +name = "objc2-core-image" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e5d563b38d2b97209f8e861173de434bd0214cf020e3423a52624cd1d989f006" +dependencies = [ + "objc2 0.6.4", + "objc2-foundation 0.3.2", +] + [[package]] name = "objc2-core-location" version = "0.2.2" @@ -2431,6 +2277,31 @@ dependencies = [ "objc2-foundation 0.2.2", ] +[[package]] +name = "objc2-core-text" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cde0dfb48d25d2b4862161a4d5fcc0e3c24367869ad306b0c9ec0073bfed92d" +dependencies = [ + "bitflags 2.11.1", + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-core-graphics", +] + +[[package]] +name = "objc2-core-video" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d425caf1df73233f29fd8a5c3e5edbc30d2d4307870f802d18f00d83dc5141a6" +dependencies = [ + "bitflags 2.11.1", + "objc2 0.6.4", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-io-surface", +] + [[package]] name = "objc2-encode" version = "4.1.0" @@ -2443,7 +2314,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "block2 0.5.1", "dispatch", "libc", @@ -2456,7 +2327,9 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", + "block2 0.6.2", + "libc", "objc2 0.6.4", "objc2-core-foundation", ] @@ -2467,7 +2340,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "objc2 0.6.4", "objc2-core-foundation", ] @@ -2490,7 +2363,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -2502,7 +2375,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "block2 0.5.1", "objc2 0.5.2", "objc2-foundation 0.2.2", @@ -2515,7 +2388,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "96c1358452b371bf9f104e21ec536d37a650eb10f7ee379fff67d2e08d537f1f" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "objc2 0.6.4", "objc2-core-foundation", "objc2-foundation 0.3.2", @@ -2537,12 +2410,12 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "block2 0.5.1", "objc2 0.5.2", - "objc2-cloud-kit", - "objc2-core-data", - "objc2-core-image", + "objc2-cloud-kit 0.2.2", + "objc2-core-data 0.2.2", + "objc2-core-image 0.2.2", "objc2-core-location", "objc2-foundation 0.2.2", "objc2-link-presentation", @@ -2569,7 +2442,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "block2 0.5.1", "objc2 0.5.2", "objc2-core-location", @@ -2577,19 +2450,16 @@ dependencies = [ ] [[package]] -name = "objc_exception" -version = "0.1.2" +name = "once_cell" +version = "1.21.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad970fb455818ad6cba4c122ad012fae53ae8b4795f86378bce65e4f6bab2ca4" -dependencies = [ - "cc", -] +checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50" [[package]] -name = "once_cell" -version = "1.21.3" +name = "openssl-probe" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "option-ext" @@ -2599,22 +2469,21 @@ checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] name = "orbclient" -version = "0.3.50" +version = "0.3.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ad2c6bae700b7aa5d1cc30c59bdd3a1c180b09dbaea51e2ae2b8e1cf211fdd" +checksum = "a570f6bca41d29acb2139229a7c873ec99bc9a313bd10804081d89bfac8ff329" dependencies = [ "libc", "libredox", ] [[package]] -name = "ordered-multimap" -version = "0.4.3" +name = "ordered-float" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccd746e37177e1711c20dd619a1620f34f5c8b569c53590a72dedd5344d8924a" +checksum = "b7d950ca161dc355eaf28f82b11345ed76c6e1f6eb1f4f4479e0323b9e2fbd0e" dependencies = [ - "dlv-list", - "hashbrown 0.12.3", + "num-traits", ] [[package]] @@ -2633,31 +2502,7 @@ version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b" dependencies = [ - "ttf-parser 0.25.1", -] - -[[package]] -name = "palette" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cbf71184cc5ecc2e4e1baccdb21026c20e5fc3dcf63028a086131b3ab00b6e6" -dependencies = [ - "approx", - "fast-srgb8", - "palette_derive", - "phf", -] - -[[package]] -name = "palette_derive" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5030daf005bface118c096f510ffb781fc28f9ab6a32ab224d8631be6851d30" -dependencies = [ - "by_address", - "proc-macro2", - "quote", - "syn 2.0.117", + "ttf-parser", ] [[package]] @@ -2666,17 +2511,6 @@ version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" -[[package]] -name = "parking_lot" -version = "0.11.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d17b78036a60663b797adeaee46f5c9dfebb86948d1255007a1d6be0271ff99" -dependencies = [ - "instant", - "lock_api", - "parking_lot_core 0.8.6", -] - [[package]] name = "parking_lot" version = "0.12.5" @@ -2684,21 +2518,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", - "parking_lot_core 0.9.12", -] - -[[package]] -name = "parking_lot_core" -version = "0.8.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60a2cfe6f0ad2bfc16aefa463b497d5c7a5ecd44a23efa72aa342d90177356dc" -dependencies = [ - "cfg-if", - "instant", - "libc", - "redox_syscall 0.2.16", - "smallvec", - "winapi", + "parking_lot_core", ] [[package]] @@ -2726,66 +2546,24 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" -[[package]] -name = "phf" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd6780a80ae0c52cc120a26a1a42c1ae51b247a253e4e06113d23d2c2edd078" -dependencies = [ - "phf_macros", - "phf_shared", -] - -[[package]] -name = "phf_generator" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" -dependencies = [ - "phf_shared", - "rand 0.8.5", -] - -[[package]] -name = "phf_macros" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f84ac04429c13a7ff43785d75ad27569f2951ce0ffd30a3321230db2fc727216" -dependencies = [ - "phf_generator", - "phf_shared", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "phf_shared" -version = "0.11.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" -dependencies = [ - "siphasher", -] - [[package]] name = "pin-project" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -2813,9 +2591,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "plain" @@ -2823,19 +2601,6 @@ version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" -[[package]] -name = "png" -version = "0.17.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - [[package]] name = "polling" version = "3.11.0" @@ -2856,11 +2621,26 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f3a9f18d041e6d0e102a0a46750538147e5e8992d3b4873aaafee2520b00ce3" +[[package]] +name = "portable-atomic" +version = "1.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" + +[[package]] +name = "portable-atomic-util" +version = "0.2.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" +dependencies = [ + "portable-atomic", +] + [[package]] name = "potential_utf" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ "zerovec", ] @@ -2887,7 +2667,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" dependencies = [ "proc-macro2", - "syn 2.0.117", + "syn", ] [[package]] @@ -2910,15 +2690,15 @@ dependencies = [ [[package]] name = "profiling" -version = "1.0.17" +version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" +checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" [[package]] name = "quick-xml" -version = "0.39.2" +version = "0.39.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d" +checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" dependencies = [ "memchr", ] @@ -2930,11 +2710,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" dependencies = [ "bytes", - "cfg_aliases 0.2.1", + "cfg_aliases", "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", "rustls", "socket2", "thiserror 2.0.18", @@ -2945,16 +2725,17 @@ dependencies = [ [[package]] name = "quinn-proto" -version = "0.11.13" +version = "0.11.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +checksum = "434b42fec591c96ef50e21e886936e66d3cc3f737104fdb9b737c40ffb94c098" dependencies = [ + "aws-lc-rs", "bytes", "getrandom 0.3.4", "lru-slab", - "rand 0.9.2", + "rand", "ring", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", "rustls", "rustls-pki-types", "slab", @@ -2970,7 +2751,7 @@ version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" dependencies = [ - "cfg_aliases 0.2.1", + "cfg_aliases", "libc", "once_cell", "socket2", @@ -3001,33 +2782,12 @@ checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" [[package]] name = "rand" -version = "0.8.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -dependencies = [ - "libc", - "rand_chacha 0.3.1", - "rand_core 0.6.4", -] - -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha 0.9.0", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_chacha" -version = "0.3.1" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +checksum = "44c5af06bb1b7d3216d91932aed5265164bf384dc89cd6ba05cf59a35f5f76ea" dependencies = [ - "ppv-lite86", - "rand_core 0.6.4", + "rand_chacha", + "rand_core", ] [[package]] @@ -3037,16 +2797,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ "ppv-lite86", - "rand_core 0.9.5", -] - -[[package]] -name = "rand_core" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -dependencies = [ - "getrandom 0.2.17", + "rand_core", ] [[package]] @@ -3076,43 +2827,25 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" -[[package]] -name = "rayon" -version = "1.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" -dependencies = [ - "either", - "rayon-core", -] - -[[package]] -name = "rayon-core" -version = "1.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" -dependencies = [ - "crossbeam-deque", - "crossbeam-utils", -] - [[package]] name = "read-fonts" -version = "0.22.7" +version = "0.35.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69aacb76b5c29acfb7f90155d39759a29496aebb49395830e928a9703d2eec2f" +checksum = "6717cf23b488adf64b9d711329542ba34de147df262370221940dfabc2c91358" dependencies = [ "bytemuck", - "font-types", + "core_maths", + "font-types 0.10.1", ] [[package]] -name = "redox_syscall" -version = "0.2.16" +name = "read-fonts" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fb5a58c1855b4b6819d59012155603f0b22ad30cad752600aadfcb695265519a" +checksum = "7b634fabf032fab15307ffd272149b622260f55974d9fad689292a5d33df02e5" dependencies = [ - "bitflags 1.3.2", + "bytemuck", + "font-types 0.11.3", ] [[package]] @@ -3130,27 +2863,27 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", ] [[package]] name = "redox_syscall" -version = "0.7.3" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" +checksum = "4666a1a60d8412eab19d94f6d13dcc9cea0a5ef4fdf6a5db306537413c661b1b" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", ] [[package]] name = "redox_users" -version = "0.4.6" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.17", "libredox", - "thiserror 1.0.69", + "thiserror 2.0.18", ] [[package]] @@ -3161,9 +2894,9 @@ checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" [[package]] name = "reqwest" -version = "0.12.28" +version = "0.13.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eddd3ca559203180a307f12d114c268abf583f59b03cb906fd0b3ff8646c1147" +checksum = "62e0021ea2c22aed41653bc7e1419abb2c97e038ff2c33d0e1309e49a97deec0" dependencies = [ "base64", "bytes", @@ -3181,9 +2914,9 @@ dependencies = [ "quinn", "rustls", "rustls-pki-types", + "rustls-platform-verifier", "serde", "serde_json", - "serde_urlencoded", "sync_wrapper", "tokio", "tokio-rustls", @@ -3194,31 +2927,30 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots", ] [[package]] name = "rfd" -version = "0.15.4" +version = "0.17.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef2bee61e6cffa4635c72d7d81a84294e28f0930db0ddcb0f66d10244674ebed" +checksum = "20dafead71c16a34e1ff357ddefc8afc11e7d51d6d2b9fbd07eaa48e3e540220" dependencies = [ - "ashpd", "block2 0.6.2", "dispatch2", "js-sys", + "libc", "log", "objc2 0.6.4", "objc2-app-kit 0.3.2", "objc2-core-foundation", "objc2-foundation 0.3.2", + "percent-encoding", "pollster", "raw-window-handle", - "urlencoding", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3241,16 +2973,6 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c20b6793b5c2fa6553b250154b78d6d0db37e72700ae35fad9387a46f487c97" -[[package]] -name = "rust-ini" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6d5f2436026b4f6e79dc829837d467cc7e9a55ee40e750d716713540715a2df" -dependencies = [ - "cfg-if", - "ordered-multimap", -] - [[package]] name = "rustc-hash" version = "1.1.0" @@ -3259,9 +2981,18 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" + +[[package]] +name = "rustc_version" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" +dependencies = [ + "semver", +] [[package]] name = "rustix" @@ -3269,7 +3000,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "errno", "libc", "linux-raw-sys 0.4.15", @@ -3282,7 +3013,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "errno", "libc", "linux-raw-sys 0.12.1", @@ -3291,67 +3022,84 @@ dependencies = [ [[package]] name = "rustls" -version = "0.23.37" +version = "0.23.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" +checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ + "aws-lc-rs", "once_cell", - "ring", "rustls-pki-types", "rustls-webpki", "subtle", "zeroize", ] +[[package]] +name = "rustls-native-certs" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework", +] + [[package]] name = "rustls-pki-types" -version = "1.14.0" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ "web-time", "zeroize", ] [[package]] -name = "rustls-webpki" -version = "0.103.9" +name = "rustls-platform-verifier" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" dependencies = [ - "ring", - "rustls-pki-types", - "untrusted", + "core-foundation 0.10.1", + "core-foundation-sys", + "jni", + "log", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", ] [[package]] -name = "rustversion" -version = "1.0.22" +name = "rustls-platform-verifier-android" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] -name = "rustybuzz" -version = "0.14.1" +name = "rustls-webpki" +version = "0.103.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfb9cf8877777222e4a3bc7eb247e398b56baba500c38c1c46842431adc8b55c" +checksum = "61c429a8649f110dddef65e2a5ad240f747e85f7758a6bccc7e5777bd33f756e" dependencies = [ - "bitflags 2.11.0", - "bytemuck", - "libm", - "smallvec", - "ttf-parser 0.21.1", - "unicode-bidi-mirroring", - "unicode-ccc", - "unicode-properties", - "unicode-script", + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", ] [[package]] -name = "ryu" -version = "1.0.23" +name = "rustversion" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "same-file" @@ -3362,6 +3110,15 @@ dependencies = [ "winapi-util", ] +[[package]] +name = "schannel" +version = "0.1.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91c1b7e4904c873ef0710c1f407dde2e6287de2bebc1bbbf7d430bb7cbffd939" +dependencies = [ + "windows-sys 0.61.2", +] + [[package]] name = "scoped-tls" version = "1.0.1" @@ -3387,6 +3144,29 @@ dependencies = [ "tiny-skia", ] +[[package]] +name = "security-framework" +version = "3.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" +dependencies = [ + "bitflags 2.11.1", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "self_cell" version = "1.2.2" @@ -3395,9 +3175,9 @@ checksum = "b12e76d157a900eb52e81bc6e9f3069344290341720e9178cde2407113ac8d89" [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "serde" @@ -3426,7 +3206,7 @@ checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -3450,30 +3230,7 @@ checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_urlencoded" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -dependencies = [ - "form_urlencoded", - "itoa", - "ryu", - "serde", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", + "syn", ] [[package]] @@ -3493,25 +3250,39 @@ dependencies = [ ] [[package]] -name = "simd-adler32" -version = "0.3.8" +name = "simd_cesu8" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] [[package]] -name = "siphasher" -version = "1.0.2" +name = "simdutf8" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" + +[[package]] +name = "skrifa" +version = "0.37.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" +checksum = "8c31071dedf532758ecf3fed987cdb4bd9509f900e026ab684b4ecb81ea49841" +dependencies = [ + "bytemuck", + "read-fonts 0.35.0", +] [[package]] name = "skrifa" -version = "0.22.3" +version = "0.40.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e1c44ad1f6c5bdd4eefed8326711b7dbda9ea45dfd36068c427d332aa382cbe" +checksum = "7fbdfe3d2475fbd7ddd1f3e5cf8288a30eb3e5f95832829570cd88115a7434ac" dependencies = [ "bytemuck", - "read-fonts", + "read-fonts 0.37.0", ] [[package]] @@ -3541,7 +3312,7 @@ version = "0.19.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "calloop 0.13.0", "calloop-wayland-source 0.3.0", "cursor-icon", @@ -3566,7 +3337,7 @@ version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "calloop 0.14.4", "calloop-wayland-source 0.4.1", "cursor-icon", @@ -3609,12 +3380,12 @@ dependencies = [ [[package]] name = "socket2" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" +checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -3625,7 +3396,6 @@ checksum = "aac18da81ebbf05109ab275b157c22a653bb3c12cf884450179942f81bcbf6c3" dependencies = [ "as-raw-xcb-connection", "bytemuck", - "drm", "fastrand", "js-sys", "memmap2", @@ -3655,7 +3425,7 @@ version = "0.3.0+sdk-1.3.268.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", ] [[package]] @@ -3690,26 +3460,15 @@ checksum = "0193cc4331cfd2f3d2011ef287590868599a2f33c3e69bc22c1a3d3acf9e02fb" [[package]] name = "swash" -version = "0.1.19" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbd59f3f359ddd2c95af4758c18270eddd9c730dde98598023cdabff472c2ca2" +checksum = "842f3cd369c2ba38966204f983eaa5e54a8e84a7d7159ed36ade2b6c335aae64" dependencies = [ - "skrifa", + "skrifa 0.40.0", "yazi", "zeno", ] -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - [[package]] name = "syn" version = "2.0.117" @@ -3738,7 +3497,7 @@ checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -3752,9 +3511,9 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.26.0" +version = "3.27.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" +checksum = "32497e9a4c7b38532efcdebeef879707aa9f794296a4f0244f6f69e9bc8574bd" dependencies = [ "fastrand", "getrandom 0.4.2", @@ -3798,7 +3557,7 @@ checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -3809,7 +3568,7 @@ checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -3823,7 +3582,6 @@ dependencies = [ "bytemuck", "cfg-if", "log", - "png", "tiny-skia-path", ] @@ -3840,22 +3598,22 @@ dependencies = [ [[package]] name = "tiny-xlib" -version = "0.2.4" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0324504befd01cab6e0c994f34b2ffa257849ee019d3fb3b64fb2c858887d89e" +checksum = "a90a0ca3ee6a69f2ad28fd11621a4c3f03b371f366be500b64df260c4ffbafb4" dependencies = [ "as-raw-xcb-connection", - "ctor-lite", - "libloading 0.8.9", + "ctor", + "libloading", "pkg-config", "tracing", ] [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", "zerovec", @@ -3863,9 +3621,9 @@ dependencies = [ [[package]] name = "tinyvec" -version = "1.10.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" +checksum = "3e61e67053d25a4e82c844e8424039d9745781b3fc4f32b8d55ed50f5f667ef3" dependencies = [ "tinyvec_macros", ] @@ -3878,14 +3636,14 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.50.0" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", "mio", - "parking_lot 0.12.5", + "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", @@ -3895,13 +3653,13 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -3916,18 +3674,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.0.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32c2555c699578a4f59f0cc68e5116c8d7cabbd45e1409b989d4be085b53f13e" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.25.4+spec-1.1.0" +version = "0.25.11+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7193cbd0ce53dc966037f54351dbbcf0d5a642c7f0038c382ef9e677ce8c13f2" +checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b" dependencies = [ "indexmap", "toml_datetime", @@ -3937,9 +3695,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.9+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ "winnow", ] @@ -3961,20 +3719,20 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.8" +version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" +checksum = "68d6fdd9f81c2819c9a8b0e0cd91660e7746a8e6ea2ba7c6b2b057985f6bcb51" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "bytes", "futures-util", "http", "http-body", - "iri-string", "pin-project-lite", "tower", "tower-layer", "tower-service", + "url", ] [[package]] @@ -4009,7 +3767,7 @@ checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -4027,35 +3785,20 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" -[[package]] -name = "ttf-parser" -version = "0.20.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17f77d76d837a7830fe1d4f12b7b4ba4192c1888001c7164257e4bc6d21d96b4" - -[[package]] -name = "ttf-parser" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c591d83f69777866b9126b24c6dd9a18351f177e49d625920d19f989fd31cf8" - [[package]] name = "ttf-parser" version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" - -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +dependencies = [ + "core_maths", +] [[package]] name = "uds_windows" -version = "1.2.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "51b70b87d15e91f553711b40df3048faf27a7a04e01e0ddc0cf9309f0af7c2ca" +checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e" dependencies = [ "memoffset", "tempfile", @@ -4068,18 +3811,6 @@ version = "0.3.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" -[[package]] -name = "unicode-bidi-mirroring" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23cb788ffebc92c5948d0e997106233eeb1d8b9512f93f41651f52b6c5f5af86" - -[[package]] -name = "unicode-ccc" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1df77b101bcc4ea3d78dafc5ad7e4f58ceffe0b2b16bf446aeb50b6cb4157656" - [[package]] name = "unicode-ident" version = "1.0.24" @@ -4092,12 +3823,6 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" -[[package]] -name = "unicode-properties" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" - [[package]] name = "unicode-script" version = "0.5.8" @@ -4106,15 +3831,15 @@ checksum = "383ad40bb927465ec0ce7720e033cb4ca06912855fc35db31b5755d0de75b1ee" [[package]] name = "unicode-segmentation" -version = "1.12.0" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c" [[package]] name = "unicode-width" -version = "0.1.14" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unicode-xid" @@ -4138,15 +3863,8 @@ dependencies = [ "idna", "percent-encoding", "serde", - "serde_derive", ] -[[package]] -name = "urlencoding" -version = "2.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" - [[package]] name = "utf8_iter" version = "1.0.4" @@ -4155,9 +3873,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.21.0" +version = "1.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" +checksum = "ddd74a9687298c6858e9b88ec8935ec45d22e8fd5e6394fa1bd4e99a87789c76" dependencies = [ "js-sys", "serde_core", @@ -4197,11 +3915,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.57.1", ] [[package]] @@ -4210,14 +3928,14 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.51.0", ] [[package]] name = "wasm-bindgen" -version = "0.2.114" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +checksum = "49ace1d07c165b0864824eee619580c4689389afa9dc9ed3a4c75040d82e6790" dependencies = [ "cfg-if", "once_cell", @@ -4228,23 +3946,19 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.64" +version = "0.4.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +checksum = "96492d0d3ffba25305a7dc88720d250b1401d7edca02cc3bcd50633b424673b8" dependencies = [ - "cfg-if", - "futures-util", "js-sys", - "once_cell", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.114" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +checksum = "8e68e6f4afd367a562002c05637acb8578ff2dea1943df76afb9e83d177c8578" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -4252,22 +3966,22 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.114" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +checksum = "d95a9ec35c64b2a7cb35d3fead40c4238d0940c86d107136999567a4703259f2" dependencies = [ "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" -version = "0.2.114" +version = "0.2.121" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +checksum = "c4e0100b01e9f0d03189a92b96772a1fb998639d981193d7dbab487302513441" dependencies = [ "unicode-ident", ] @@ -4294,38 +4008,37 @@ dependencies = [ "wasmparser", ] -[[package]] -name = "wasm-timer" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be0ecb0db480561e9a7642b5d3e4187c128914e58aa84330b9493e3eb68c5e7f" -dependencies = [ - "futures", - "js-sys", - "parking_lot 0.11.2", - "pin-utils", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - [[package]] name = "wasmparser" version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "hashbrown 0.15.5", "indexmap", "semver", ] +[[package]] +name = "wasmtimer" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1c598d6b99ea013e35844697fc4670d08339d5cda15588f193c6beedd12f644b" +dependencies = [ + "futures", + "js-sys", + "parking_lot", + "pin-utils", + "slab", + "wasm-bindgen", +] + [[package]] name = "wayland-backend" -version = "0.3.13" +version = "0.3.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eaa6143502b9a87f759cb6a649ca801a226f77740eb54f3951cba2227790afeb" +checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d" dependencies = [ "cc", "downcast-rs", @@ -4337,11 +4050,11 @@ dependencies = [ [[package]] name = "wayland-client" -version = "0.31.13" +version = "0.31.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab51d9f7c071abeee76007e2b742499e535148035bb835f97aaed1338cf516c3" +checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "rustix 1.1.4", "wayland-backend", "wayland-scanner", @@ -4353,16 +4066,16 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "cursor-icon", "wayland-backend", ] [[package]] name = "wayland-cursor" -version = "0.31.13" +version = "0.31.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b3298683470fbdc6ca40151dfc48c8f2fd4c41a26e13042f801f85002384091" +checksum = "4a52d18780be9b1314328a3de5f930b73d2200112e3849ca6cb11822793fb34d" dependencies = [ "rustix 1.1.4", "wayland-client", @@ -4371,11 +4084,11 @@ dependencies = [ [[package]] name = "wayland-protocols" -version = "0.32.11" +version = "0.32.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b23b5df31ceff1328f06ac607591d5ba360cf58f90c8fad4ac8d3a55a3c4aec7" +checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "wayland-backend", "wayland-client", "wayland-scanner", @@ -4387,7 +4100,7 @@ version = "20250721.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -4396,11 +4109,11 @@ dependencies = [ [[package]] name = "wayland-protocols-misc" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "429b99200febaf95d4f4e46deff6fe4382bcff3280ee16a41cf887b3c3364984" +checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -4409,11 +4122,11 @@ dependencies = [ [[package]] name = "wayland-protocols-plasma" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d392fc283a87774afc9beefcd6f931582bb97fe0e6ced0b306a62cb1d026527c" +checksum = "2b6d8cf1eb2c1c31ed1f5643c88a6e53538129d4af80030c8cabd1f9fa884d91" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -4422,11 +4135,11 @@ dependencies = [ [[package]] name = "wayland-protocols-wlr" -version = "0.3.11" +version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78248e4cc0eff8163370ba5c158630dcae1f3497a586b826eca2ef5f348d6235" +checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "wayland-backend", "wayland-client", "wayland-protocols", @@ -4435,9 +4148,9 @@ dependencies = [ [[package]] name = "wayland-scanner" -version = "0.31.9" +version = "0.31.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c86287151a309799b821ca709b7345a048a2956af05957c89cb824ab919fa4e3" +checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a" dependencies = [ "proc-macro2", "quick-xml", @@ -4446,9 +4159,9 @@ dependencies = [ [[package]] name = "wayland-sys" -version = "0.31.9" +version = "0.31.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81d2bd69b1dadd601d0e98ef2fc9339a1b1e00cec5ee7545a77b5a0f52a90394" +checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be" dependencies = [ "dlib", "log", @@ -4458,9 +4171,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.91" +version = "0.3.98" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +checksum = "4b572dff8bcf38bad0fa19729c89bb5748b2b9b1d8be70cf90df697e3a8f32aa" dependencies = [ "js-sys", "wasm-bindgen", @@ -4477,27 +4190,31 @@ dependencies = [ ] [[package]] -name = "webpki-roots" -version = "1.0.6" +name = "webpki-root-certs" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" dependencies = [ "rustls-pki-types", ] [[package]] name = "wgpu" -version = "0.19.4" +version = "27.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cbd7311dbd2abcfebaabf1841a2824ed7c8be443a0f29166e5d3c6a53a762c01" +checksum = "bfe68bac7cde125de7a731c3400723cadaaf1703795ad3f4805f187459cd7a77" dependencies = [ "arrayvec", + "bitflags 2.11.1", "cfg-if", - "cfg_aliases 0.1.1", + "cfg_aliases", + "document-features", + "hashbrown 0.16.1", "js-sys", "log", "naga", - "parking_lot 0.12.5", + "parking_lot", + "portable-atomic", "profiling", "raw-window-handle", "smallvec", @@ -4512,108 +4229,126 @@ dependencies = [ [[package]] name = "wgpu-core" -version = "0.19.4" +version = "27.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28b94525fc99ba9e5c9a9e24764f2bc29bad0911a7446c12f446a8277369bf3a" +checksum = "27a75de515543b1897b26119f93731b385a19aea165a1ec5f0e3acecc229cae7" dependencies = [ "arrayvec", + "bit-set", "bit-vec", - "bitflags 2.11.0", - "cfg_aliases 0.1.1", - "codespan-reporting", + "bitflags 2.11.1", + "bytemuck", + "cfg_aliases", + "document-features", + "hashbrown 0.16.1", "indexmap", "log", "naga", "once_cell", - "parking_lot 0.12.5", + "parking_lot", + "portable-atomic", "profiling", "raw-window-handle", "rustc-hash 1.1.0", "smallvec", - "thiserror 1.0.69", - "web-sys", + "thiserror 2.0.18", + "wgpu-core-deps-apple", + "wgpu-core-deps-emscripten", + "wgpu-core-deps-windows-linux-android", "wgpu-hal", "wgpu-types", ] +[[package]] +name = "wgpu-core-deps-apple" +version = "27.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0772ae958e9be0c729561d5e3fd9a19679bcdfb945b8b1a1969d9bfe8056d233" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-emscripten" +version = "27.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b06ac3444a95b0813ecfd81ddb2774b66220b264b3e2031152a4a29fda4da6b5" +dependencies = [ + "wgpu-hal", +] + +[[package]] +name = "wgpu-core-deps-windows-linux-android" +version = "27.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71197027d61a71748e4120f05a9242b2ad142e3c01f8c1b47707945a879a03c3" +dependencies = [ + "wgpu-hal", +] + [[package]] name = "wgpu-hal" -version = "0.19.5" +version = "27.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfabcfc55fd86611a855816326b2d54c3b2fd7972c27ce414291562650552703" +checksum = "5b21cb61c57ee198bc4aff71aeadff4cbb80b927beb912506af9c780d64313ce" dependencies = [ "android_system_properties", "arrayvec", "ash", "bit-set", - "bitflags 2.11.0", + "bitflags 2.11.1", "block", - "cfg_aliases 0.1.1", - "core-graphics-types", - "d3d12", + "bytemuck", + "cfg-if", + "cfg_aliases", + "core-graphics-types 0.2.0", "glow", "glutin_wgl_sys", "gpu-alloc", "gpu-allocator", "gpu-descriptor", - "hassle-rs", + "hashbrown 0.16.1", "js-sys", "khronos-egl", "libc", - "libloading 0.8.9", + "libloading", "log", "metal", "naga", - "ndk-sys 0.5.0+25.2.9519653", + "ndk-sys", "objc", "once_cell", - "parking_lot 0.12.5", + "ordered-float", + "parking_lot", + "portable-atomic", + "portable-atomic-util", "profiling", "range-alloc", "raw-window-handle", "renderdoc-sys", - "rustc-hash 1.1.0", "smallvec", - "thiserror 1.0.69", + "thiserror 2.0.18", "wasm-bindgen", "web-sys", "wgpu-types", - "winapi", + "windows 0.58.0", + "windows-core 0.58.0", ] [[package]] name = "wgpu-types" -version = "0.19.2" +version = "27.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b671ff9fb03f78b46ff176494ee1ebe7d603393f42664be55b64dc8d53969805" +checksum = "afdcf84c395990db737f2dd91628706cb31e86d72e53482320d368e52b5da5eb" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", + "bytemuck", "js-sys", + "log", + "thiserror 2.0.18", "web-sys", ] -[[package]] -name = "widestring" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72069c3113ab32ab29e5584db3c6ec55d416895e60715417b5b883a357c3e471" - -[[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" -dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", -] - -[[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" - [[package]] name = "winapi-util" version = "0.1.11" @@ -4623,45 +4358,132 @@ dependencies = [ "windows-sys 0.61.2", ] -[[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" - [[package]] name = "window_clipboard" -version = "0.4.1" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6d692d46038c433f9daee7ad8757e002a4248c20b0a3fbc991d99521d3bcb6d" +checksum = "d5654226305eaf2dde8853fb482861d28e5dcecbbd40cb88e8393d94bb80d733" dependencies = [ "clipboard-win", "clipboard_macos", "clipboard_wayland", "clipboard_x11", "raw-window-handle", - "thiserror 1.0.69", + "thiserror 2.0.18", ] [[package]] name = "windows" -version = "0.52.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48a53791691ab099e5e2ad123536d0fff50652600abaf43bbf952894110d0be" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" dependencies = [ - "windows-core", + "windows-core 0.58.0", "windows-targets 0.52.6", ] +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections", + "windows-core 0.62.2", + "windows-future", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core 0.62.2", +] + [[package]] name = "windows-core" -version = "0.52.0" +version = "0.58.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +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.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" +dependencies = [ + "windows-implement 0.60.2", + "windows-interface 0.59.3", + "windows-link", + "windows-result 0.4.1", + "windows-strings 0.5.1", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core 0.62.2", + "windows-link", + "windows-threading", +] + +[[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", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[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", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "windows-link" version = "0.2.1" @@ -4669,21 +4491,50 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] -name = "windows-sys" -version = "0.45.0" +name = "windows-numerics" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" dependencies = [ - "windows-targets 0.42.2", + "windows-core 0.62.2", + "windows-link", ] [[package]] -name = "windows-sys" -version = "0.48.0" +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.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ - "windows-targets 0.48.5", + "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.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", ] [[package]] @@ -4722,36 +4573,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - -[[package]] -name = "windows-targets" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" -dependencies = [ - "windows_aarch64_gnullvm 0.48.5", - "windows_aarch64_msvc 0.48.5", - "windows_i686_gnu 0.48.5", - "windows_i686_msvc 0.48.5", - "windows_x86_64_gnu 0.48.5", - "windows_x86_64_gnullvm 0.48.5", - "windows_x86_64_msvc 0.48.5", -] - [[package]] name = "windows-targets" version = "0.52.6" @@ -4786,16 +4607,13 @@ dependencies = [ ] [[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" +name = "windows-threading" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link", +] [[package]] name = "windows_aarch64_gnullvm" @@ -4809,18 +4627,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - -[[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" - [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -4833,18 +4639,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - -[[package]] -name = "windows_i686_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" - [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -4869,18 +4663,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - -[[package]] -name = "windows_i686_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" - [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -4893,18 +4675,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - -[[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" - [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -4917,18 +4687,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -4941,18 +4699,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - -[[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" - [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -4971,16 +4717,16 @@ version = "0.30.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6755fa58a9f8350bd1e472d4c3fcc25f824ec358933bba33306d0b63df5978d" dependencies = [ - "ahash 0.8.12", + "ahash", "android-activity", "atomic-waker", - "bitflags 2.11.0", + "bitflags 2.11.1", "block2 0.5.1", "bytemuck", "calloop 0.13.0", - "cfg_aliases 0.2.1", + "cfg_aliases", "concurrent-queue", - "core-foundation", + "core-foundation 0.9.4", "core-graphics", "cursor-icon", "dpi", @@ -5019,22 +4765,13 @@ dependencies = [ [[package]] name = "winnow" -version = "0.7.14" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" dependencies = [ "memchr", ] -[[package]] -name = "winreg" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "80d0f4e272c85def139476380b12f9ac60926689dd2e01d4923222f40580869d" -dependencies = [ - "winapi", -] - [[package]] name = "wit-bindgen" version = "0.51.0" @@ -5044,6 +4781,12 @@ dependencies = [ "wit-bindgen-rust-macro", ] +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + [[package]] name = "wit-bindgen-core" version = "0.51.0" @@ -5065,7 +4808,7 @@ dependencies = [ "heck", "indexmap", "prettyplease", - "syn 2.0.117", + "syn", "wasm-metadata", "wit-bindgen-core", "wit-component", @@ -5081,7 +4824,7 @@ dependencies = [ "prettyplease", "proc-macro2", "quote", - "syn 2.0.117", + "syn", "wit-bindgen-core", "wit-bindgen-rust", ] @@ -5093,7 +4836,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.11.0", + "bitflags 2.11.1", "indexmap", "log", "serde", @@ -5125,9 +4868,9 @@ dependencies = [ [[package]] name = "writeable" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "x11-dl" @@ -5149,7 +4892,7 @@ dependencies = [ "as-raw-xcb-connection", "gethostname", "libc", - "libloading 0.8.9", + "libloading", "once_cell", "rustix 1.1.4", "x11rb-protocol", @@ -5167,23 +4910,13 @@ version = "0.3.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" -[[package]] -name = "xdg-home" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" -dependencies = [ - "libc", - "windows-sys 0.59.0", -] - [[package]] name = "xkbcommon-dl" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "dlib", "log", "once_cell", @@ -5204,15 +4937,15 @@ checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f" [[package]] name = "yazi" -version = "0.1.6" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c94451ac9513335b5e23d7a8a2b61a7102398b8cca5160829d313e84c9d98be1" +checksum = "e01738255b5a16e78bbb83e7fbba0a1e7dd506905cfc53f4622d89015a03fbb5" [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" dependencies = [ "stable_deref_trait", "yoke-derive", @@ -5221,59 +4954,21 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", "synstructure", ] [[package]] name = "zbus" -version = "4.4.0" +version = "5.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" -dependencies = [ - "async-broadcast", - "async-executor", - "async-fs", - "async-io", - "async-lock", - "async-process", - "async-recursion", - "async-task", - "async-trait", - "blocking", - "enumflags2", - "event-listener", - "futures-core", - "futures-sink", - "futures-util", - "hex", - "nix", - "ordered-stream", - "rand 0.8.5", - "serde", - "serde_repr", - "sha1", - "static_assertions", - "tracing", - "uds_windows", - "windows-sys 0.52.0", - "xdg-home", - "zbus_macros 4.4.0", - "zbus_names 3.0.0", - "zvariant 4.2.0", -] - -[[package]] -name = "zbus" -version = "5.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca82f95dbd3943a40a53cfded6c2d0a2ca26192011846a1810c4256ef92c60bc" +checksum = "c3bcbf15c8708d7fc1be0c993622e0a5cbd5e8b52bfa40afa4c3e0cd8d724ac1" dependencies = [ "async-broadcast", "async-executor", @@ -5299,105 +4994,81 @@ dependencies = [ "uuid", "windows-sys 0.61.2", "winnow", - "zbus_macros 5.14.0", - "zbus_names 4.3.1", - "zvariant 5.10.0", -] - -[[package]] -name = "zbus_macros" -version = "4.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.117", - "zvariant_utils 2.1.0", + "zbus_macros", + "zbus_names", + "zvariant", ] [[package]] name = "zbus_macros" -version = "5.14.0" +version = "5.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897e79616e84aac4b2c46e9132a4f63b93105d54fe8c0e8f6bffc21fa8d49222" +checksum = "51fa5406ad9175a8c825a931f8cf347116b531b3634fcb0b627c290f1f2516ff" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", - "zbus_names 4.3.1", - "zvariant 5.10.0", - "zvariant_utils 3.3.0", + "syn", + "zbus_names", + "zvariant", + "zvariant_utils", ] [[package]] name = "zbus_names" -version = "3.0.0" +version = "4.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" -dependencies = [ - "serde", - "static_assertions", - "zvariant 4.2.0", -] - -[[package]] -name = "zbus_names" -version = "4.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" +checksum = "7074f3e50b894eac91750142016d30d0a89be8e67dbfd9704fb875825760e52d" dependencies = [ "serde", "winnow", - "zvariant 5.10.0", + "zvariant", ] [[package]] name = "zeno" -version = "0.2.3" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd15f8e0dbb966fd9245e7498c7e9e5055d9e5c8b676b95bd67091cd11a1e697" +checksum = "6df3dc4292935e51816d896edcd52aa30bc297907c26167fec31e2b0c6a32524" [[package]] name = "zerocopy" -version = "0.8.40" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a789c6e490b576db9f7e6b6d661bcc9799f7c0ac8352f56ea20193b2681532e5" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.40" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", "synstructure", ] @@ -5409,9 +5080,9 @@ checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0" [[package]] name = "zerotrie" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" dependencies = [ "displaydoc", "yoke", @@ -5420,9 +5091,9 @@ dependencies = [ [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "yoke", "zerofrom", @@ -5431,13 +5102,13 @@ dependencies = [ [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -5448,78 +5119,40 @@ checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" [[package]] name = "zvariant" -version = "4.2.0" +version = "5.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" +checksum = "1c1567a6ec68df868cbbfde844cfc6d81649fe5109a62b116b19fabd53e618ee" dependencies = [ "endi", "enumflags2", "serde", - "static_assertions", - "zvariant_derive 4.2.0", -] - -[[package]] -name = "zvariant" -version = "5.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5708299b21903bbe348e94729f22c49c55d04720a004aa350f1f9c122fd2540b" -dependencies = [ - "endi", - "enumflags2", - "serde", - "url", "winnow", - "zvariant_derive 5.10.0", - "zvariant_utils 3.3.0", + "zvariant_derive", + "zvariant_utils", ] [[package]] name = "zvariant_derive" -version = "4.2.0" +version = "5.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" +checksum = "c7d5b780599bbde114e39d9a0799577fad1ced5105d38515745f7b3099d8ceda" dependencies = [ "proc-macro-crate", "proc-macro2", "quote", - "syn 2.0.117", - "zvariant_utils 2.1.0", -] - -[[package]] -name = "zvariant_derive" -version = "5.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b59b012ebe9c46656f9cc08d8da8b4c726510aef12559da3e5f1bf72780752c" -dependencies = [ - "proc-macro-crate", - "proc-macro2", - "quote", - "syn 2.0.117", - "zvariant_utils 3.3.0", -] - -[[package]] -name = "zvariant_utils" -version = "2.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", + "syn", + "zvariant_utils", ] [[package]] name = "zvariant_utils" -version = "3.3.0" +version = "3.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" +checksum = "6d464f5733ffa07a3164d656f18533caace9d0638596721355d73256a410d691" dependencies = [ "proc-macro2", "quote", "serde", - "syn 2.0.117", + "syn", "winnow", ] diff --git a/Cargo.toml b/Cargo.toml index 1bd599f..11438dd 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,25 +1,29 @@ [package] -name = "bitcoin-node-manager" -version = "0.1.1" +name = "bitengine" +version = "0.1.2" edition = "2021" -description = "Bitcoin & Electrs Node Manager — macOS GUI" -authors = ["Bitcoin Node Manager"] +rust-version = "1.80" +description = "BitEngine GUI for Bitcoin Core and Electrs" +license = "MIT" +repository = "https://github.com/csd113/BitEngine" +keywords = ["bitcoin", "electrs", "gui"] +categories = ["gui"] [[bin]] -name = "bitcoin_node_manager" +name = "bitengine" path = "src/main.rs" [dependencies] -# GUI framework: Iced chosen for pure-Rust native rendering (no webview dependency, -# no Node.js toolchain, bundles cleanly as a macOS .app, and has a proper Elm-style -# architecture that maps perfectly onto the reactive status-polling model here). -iced = { version = "0.13", features = ["tokio", "advanced"] } +# GUI framework: Iced chosen for pure-Rust native rendering (no webview dependency +# or Node.js toolchain) and an Elm-style architecture that maps onto the +# reactive status-polling model here. +iced = { version = "0.14", features = ["tokio", "advanced"] } # Async runtime (iced tokio feature drives the executor) tokio = { version = "1", features = ["full"] } # HTTP client for Bitcoin JSON-RPC calls -reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls"] } +reqwest = { version = "0.13", default-features = false, features = ["json", "rustls"] } # Serialisation serde = { version = "1", features = ["derive"] } @@ -27,19 +31,19 @@ serde_json = "1" # Error handling anyhow = "1" -thiserror = "1" +thiserror = "2" -# Native macOS file-picker (async, works with tokio) -rfd = "0.15" +# Native file-picker (async, works with tokio) +rfd = { version = "0.17", default-features = false, features = ["xdg-portal"] } -# XDG / macOS Application Support directory resolution -directories = "5" +# Platform config/download directory resolution +directories = "6" -# Unix system calls (flock for single-instance guard) +# Unix process signals for graceful termination where available libc = "0.2" # Needed to name the Action type returned by scrollable::scroll_to -iced_runtime = "0.13" +iced_runtime = "0.14" [dev-dependencies] tempfile = "3" diff --git a/README.md b/README.md index fa40138..317acbd 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,16 @@ # ⚙️ BitEngine -**A native macOS GUI for managing your Bitcoin Core and Electrs nodes on an external SSD** +**A native cross-platform GUI for managing Bitcoin Core and Electrs nodes** -Built with Rust · Iced · Metal-accelerated · Apple Silicon native +Built with Rust · Iced · Native desktop rendering -Current release: `0.1.1` +Current release: `0.1.2` [![Rust](https://img.shields.io/badge/rust-1.75%2B-orange?logo=rust)](https://www.rust-lang.org/) [![CI](https://github.com/csd113/BitEngine/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/csd113/BitEngine/actions/workflows/ci.yml) -[![Platform](https://img.shields.io/badge/platform-macOS%2012%2B-blue?logo=apple)](https://www.apple.com/macos/) -[![Architecture](https://img.shields.io/badge/arch-arm64%20%7C%20x86__64-lightgrey)](#build) +[![Platform](https://img.shields.io/badge/platform-macOS%20arm64%20%7C%20Linux%20x64%2Farm64-blue)](#supported-platforms) +[![Architecture](https://img.shields.io/badge/macos-Apple%20Silicon%20only-lightgrey)](#supported-platforms) [![License](https://img.shields.io/badge/license-MIT-green)](#license) @@ -20,20 +20,34 @@ Current release: `0.1.1` ## What is BitEngine? -BitEngine is a macOS desktop application that lets you launch, monitor, and shut down a self-hosted **Bitcoin Core** (`bitcoind`) and **Electrs** indexer node — both stored on an external SSD — without touching the terminal. +BitEngine is a desktop application that lets you launch, monitor, and shut down a self-hosted **Bitcoin Core** (`bitcoind`) and **Electrs** indexer node without touching the terminal. - Dual side-by-side terminal panels with live log streaming - Real-time block height display via JSON-RPC - Green/grey status indicators: **Running · Synced · Ready** for each node -- One-click graceful shutdown (RPC stop → SIGTERM → SIGKILL) -- Binary updater: scans `~/Downloads/bitcoin_builds/` and atomically replaces binaries +- One-click shutdown (Bitcoin RPC stop first, then platform fallback) +- Binary updater: scans the platform Downloads `bitcoin_builds/` folder and atomically replaces binaries - Fully configurable data paths, persisted across sessions - Single-binary distribution — no runtime, no WebView, no Electron -Recent release work in `0.1.1`: -- Split the large UI module into smaller rendering and update helpers so the code is easier to maintain -- Tightened filesystem and config error handling so setup failures surface clearly instead of being ignored -- Kept GitHub Actions macOS-only and enforced `cargo fmt --all --check`, strict Clippy, tests, and a release build +Recent release work in `0.1.2`: +- Renamed the app, config namespace, and built binary to `BitEngine` +- Bumped the crate and release version to `0.1.2` +- Added first-class release artifacts for macOS Apple Silicon, Linux x86_64, and Linux ARM64 + +--- + +## Supported platforms + +BitEngine supports these release targets: + +| Platform | Target | Release artifact | +|---|---|---| +| macOS Apple Silicon | `aarch64-apple-darwin` | `BitEngine-macos-arm64.zip` containing `BitEngine.app` | +| Linux x86_64 | `x86_64-unknown-linux-gnu` | `BitEngine-linux-x64.tar.gz` | +| Linux ARM64 | `aarch64-unknown-linux-gnu` | `BitEngine-linux-arm64.tar.gz` | + +macOS Intel/x86_64 and universal macOS app bundles are intentionally not built or supported. --- @@ -47,9 +61,9 @@ Recent release work in `0.1.1`: │ 895,234 │ ├─────────────────────────────────────────────────────────────────────┤ │ DIRECTORY PATHS [Hide] │ -│ Binaries Folder /Volumes/SSD/Binaries [Browse…] ● │ -│ Bitcoin Data Directory /Volumes/SSD/BitcoinChain [Browse…] ● │ -│ Electrs DB Directory /Volumes/SSD/ElectrsDB [Browse…] ● │ +│ Binaries Folder /path/to/BitEngine/Binaries [Browse…] ● │ +│ Bitcoin Data Directory /path/to/BitEngine/BitcoinChain[Browse…] ● │ +│ Electrs DB Directory /path/to/BitEngine/ElectrsDB [Browse…] ● │ │ Changes take effect on next launch [Save] │ ├───────────────────────────────┬─────────────────────────────────────┤ │ Bitcoin [Launch] │ Electrs [Launch] │ @@ -84,27 +98,27 @@ Three per node, updated automatically: Polls `getblockchaininfo` via JSON-RPC every 5 seconds and displays the current block height with comma formatting (e.g. `895,234`). ### Binary updater -Click **Update Binaries…** to scan `~/Downloads/bitcoin_builds/binaries/` for versioned folders (`bitcoin-27.0`, `electrs-0.10.5`), pick the highest semantic version, and atomically replace binaries in your SSD `Binaries/` folder. +Click **Update Binaries…** to scan the platform Downloads `bitcoin_builds/binaries/` folder for versioned folders (`bitcoin-27.0`, `electrs-0.10.5`), pick the highest semantic version, and atomically replace binaries in your configured `Binaries/` folder. -If `bitcoin_builds` is not found, BitEngine checks for **BitForge.app** in `/Applications` and offers to open it, or shows the download link. +On macOS, if `bitcoin_builds` is not found, BitEngine checks for **BitForge.app** in `/Applications` and offers to open it. Linux users should place platform-specific Bitcoin Core and Electrs builds in the same Downloads layout. ### Graceful shutdown -- **Electrs only**: SIGTERM → 10 s wait → SIGKILL -- **Bitcoin (and Electrs)**: RPC `stop` command → 60 s wait → SIGKILL fallback +- **Electrs only**: graceful termination on Unix where available, then kill fallback +- **Bitcoin (and Electrs)**: RPC `stop` command → 60 s wait → platform kill fallback - Shutdown runs in a background thread so the UI stays responsive ### Configurable paths -All three data directories (Binaries, Bitcoin data, Electrs DB) are editable in the UI and persisted to `~/Library/Application Support/BitcoinNodeManager/config.json`. Changes take effect on the next node launch. +All three data directories (Binaries, Bitcoin data, Electrs DB) are editable in the UI and persisted in the platform config directory. Changes take effect on the next node launch. --- -## SSD directory layout +## Default directory layout -BitEngine expects this structure on your external SSD: +BitEngine derives default paths from the directory containing the executable. On macOS `.app` bundles, it walks from `BitEngine.app/Contents/MacOS/` to the bundle's parent directory for compatibility with the original external SSD layout. ``` -/ -├── BitEngine.app ← this application +/ +├── BitEngine.app or bitengine executable ├── Binaries/ │ ├── bitcoind │ ├── bitcoin-cli @@ -116,7 +130,7 @@ BitEngine expects this structure on your external SSD: └── ElectrsDB/ ``` -The SSD root is **auto-detected** from the binary's location. When running as a `.app` bundle the binary lives at `Contents/MacOS/`, so BitEngine walks up three directories to find the SSD root. You can override this with the `BITCOIN_NODE_MANAGER_ROOT` environment variable. +You can override this with the `BITENGINE_ROOT` environment variable; the legacy `BITCOIN_NODE_MANAGER_ROOT` name is still accepted for compatibility. --- @@ -129,65 +143,48 @@ The SSD root is **auto-detected** from the binary's location. When running as a curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh source "$HOME/.cargo/env" -# Apple Silicon target (already present on arm64 Macs — add to be sure) +# Supported release targets rustup target add aarch64-apple-darwin - -# Intel Mac target -rustup target add x86_64-apple-darwin +rustup target add x86_64-unknown-linux-gnu +rustup target add aarch64-unknown-linux-gnu ``` -> **Requires:** Rust 1.75+, macOS 12 Monterey or later, Xcode Command Line Tools (`xcode-select --install`) +> **Requires:** Rust 1.80+. macOS releases require Apple Silicon and macOS 12 Monterey or later. Linux builds need the native GUI development libraries used by `iced`/`rfd` (`libx11`, `libxkbcommon`, Wayland/EGL, GTK 3). ### Development build ```bash cargo build -./target/debug/bitcoin_node_manager +./target/debug/bitengine ``` -The GitHub Actions CI workflow uses the same macOS toolchain and runs: +The GitHub Actions CI workflow runs: - `cargo fmt --all --check` -- `cargo clippy --all-targets --all-features -- -D warnings` -- `cargo test --all-targets --all-features` -- `cargo build --release` +- `cargo clippy --workspace --all-targets --all-features -- -D warnings` +- `cargo test --workspace --all-features` +- release build checks for macOS Apple Silicon, Linux x86_64, and Linux ARM64 ### Release build (optimised, ~5 MB) ```bash -# Apple Silicon cargo build --release --target aarch64-apple-darwin - -# Intel -cargo build --release --target x86_64-apple-darwin +cargo build --release --target x86_64-unknown-linux-gnu +cargo build --release --target aarch64-unknown-linux-gnu ``` ### Bundle as a `.app` ```bash -./build_bundle.sh -# Output: ./dist/BitEngine.app +./build-mac-app.sh +# Output: ./BitEngine.app -open dist/BitEngine.app +open BitEngine.app ``` The script compiles, assembles the `.app` directory structure, writes `Info.plist`, copies the binary, and applies an ad-hoc codesign so Gatekeeper doesn't block local execution. -#### Universal binary (arm64 + x86_64) - -```bash -cargo build --release --target aarch64-apple-darwin -cargo build --release --target x86_64-apple-darwin - -lipo -create \ - target/aarch64-apple-darwin/release/bitcoin_node_manager \ - target/x86_64-apple-darwin/release/bitcoin_node_manager \ - -output dist/BitEngine.app/Contents/MacOS/BitEngine - -codesign --force --deep --sign "-" dist/BitEngine.app -``` - -Tagged `v*` releases are built automatically in GitHub Actions, packaged as a zipped universal `.app`, and attached to the GitHub Release. +Tagged `v*` releases are built automatically in GitHub Actions. The macOS artifact is Apple Silicon only; no macOS Intel or universal artifact is produced. --- @@ -220,16 +217,17 @@ xcrun stapler staple dist/BitEngine.app Config is stored at: ``` -~/Library/Application Support/BitcoinNodeManager/config.json +macOS: ~/Library/Application Support/BitEngine/config.json +Linux: $XDG_CONFIG_HOME/BitEngine/config.json or ~/.config/BitEngine/config.json ``` Example: ```json { - "binaries_path": "/Volumes/SSD/Binaries", - "bitcoin_data_path": "/Volumes/SSD/BitcoinChain", - "electrs_data_path": "/Volumes/SSD/ElectrsDB" + "binaries_path": "/path/to/BitEngine/Binaries", + "bitcoin_data_path": "/path/to/BitEngine/BitcoinChain", + "electrs_data_path": "/path/to/BitEngine/ElectrsDB" } ``` @@ -256,12 +254,12 @@ Cookie-based RPC authentication (`.cookie` file) is used by default. BitEngine c **Update Binaries…** (toolbar button) runs the following flow: -1. Check `~/Downloads/bitcoin_builds/binaries/` +1. Check the platform Downloads `bitcoin_builds/binaries/` directory 2. Scan for folders matching `bitcoin-X.Y.Z` and `electrs-X.Y.Z` 3. Pick the highest semantic version for each (major.minor.patch tuple comparison) 4. Copy binaries into the configured `Binaries/` folder: - Written to a `.tmp` file first - - `chmod 755` applied + - executable permissions applied on Unix platforms - Atomically renamed to the final path — a running binary is never half-replaced 5. Report what was updated in an overlay dialog @@ -269,8 +267,8 @@ If `bitcoin_builds` is not found: | Condition | Behaviour | |---|---| -| `/Applications/BitForge.app` exists | Offers to open BitForge | -| BitForge not found | Shows link to [BitForge on GitHub](https://github.com/csd113/BitForge-Python) | +| macOS `/Applications/BitForge.app` exists | Offers to open BitForge | +| BitForge not found or non-macOS platform | Shows instructions to place platform-specific builds in Downloads | --- @@ -279,14 +277,18 @@ If `bitcoin_builds` is not found: ``` src/ ├── main.rs Entry point -│ · Single-instance lock (fcntl LOCK_EX | LOCK_NB) -│ · SSD root auto-detection from binary path +│ · Cross-platform single-instance lock +│ · Default root auto-detection from binary path │ · Iced application bootstrap │ +├── platform.rs Platform boundary +│ · Supported target detection +│ · Downloads/home fallbacks +│ · Unix executable permissions and termination signal +│ ├── config.rs Persistent configuration │ · Serialised as JSON via serde_json -│ · Stored in ~/Library/Application Support (macOS) -│ · directories crate handles platform path resolution +│ · directories crate handles platform config path resolution │ ├── rpc.rs Bitcoin JSON-RPC client │ · reqwest + rustls (no OpenSSL dependency) @@ -297,15 +299,14 @@ src/ ├── process_manager.rs Child process lifecycle │ · Spawns bitcoind / electrs with stdout+stderr pipes │ · Two OS reader threads per process → Arc> -│ · SIGTERM → 10 s grace period → SIGKILL -│ · Electrs sync-line detection (5 log patterns) +│ · Platform termination request → 10 s grace period → kill │ ├── updater.rs Binary update system │ · Semver folder scanning (tuple comparison, no regex) -│ · Atomic copy: temp file → chmod 755 → rename -│ · BitForge.app detection and fallback link +│ · Atomic copy: temp file → executable bit on Unix → rename +│ · macOS BitForge.app detection and fallback instructions │ -└── ui.rs Iced 0.13 MVU application +└── ui.rs Iced 0.14 MVU application · App state struct · Message enum (all events) · update() — state transitions + Task dispatch @@ -334,16 +335,16 @@ The Iced update loop is the only writer to UI state. The background threads only | Crate | Version | Purpose | |---|---|---| -| `iced` | 0.13 | GUI framework (Metal-accelerated, Elm/MVU) | +| `iced` | 0.14 | GUI framework (native rendering, Elm/MVU) | | `tokio` | 1 | Async runtime (driven by iced's tokio feature) | -| `reqwest` | 0.12 | HTTP client for Bitcoin RPC (rustls, no OpenSSL) | +| `reqwest` | 0.13 | HTTP client for Bitcoin RPC (rustls, no OpenSSL) | | `serde` / `serde_json` | 1 | Config and RPC serialisation | | `anyhow` | 1 | Ergonomic error propagation | -| `thiserror` | 1 | Structured error type definitions | -| `rfd` | 0.15 | Native macOS file/folder picker dialog | -| `directories` | 5 | XDG / macOS Application Support path resolution | -| `libc` | 0.2 | `flock()` for single-instance guard, `SIGTERM` | -| `iced_runtime` | 0.13 | `Action` type for scroll task mapping | +| `thiserror` | 2 | Structured error type definitions | +| `rfd` | 0.17 | Native file/folder picker dialog | +| `directories` | 6 | Platform config and user directory resolution | +| `libc` | 0.2 | Unix `SIGTERM` for graceful process shutdown | +| `iced_runtime` | 0.14 | `Action` type for scroll task mapping | --- @@ -357,12 +358,12 @@ The Iced update loop is the only writer to UI state. The background threads only | Threading | GIL limits true parallelism | Real OS threads | | Terminal memory | Unbounded growth | Hard cap: 5 000 lines per panel | | UI blocking | `messagebox` blocks event loop | Overlay widget, never blocks | -| Process shutdown | `terminate()` only | RPC stop → SIGTERM → SIGKILL | -| Binary copy safety | `shutil.copy2` (non-atomic) | temp file → chmod → atomic rename | +| Process shutdown | `terminate()` only | RPC stop → platform termination → kill | +| Binary copy safety | `shutil.copy2` (non-atomic) | temp file → executable bit on Unix → atomic rename | | Semver comparison | Regex + string sort | Tuple comparison `(major, minor, patch)` | | Electrs sync detection | 3 log patterns | 5 log patterns | | RPC auth | Cookie + fallback | Same, cleaner error messages | -| Single-instance guard | `fcntl.flock` | `libc::flock` (no GIL risk) | +| Single-instance guard | Unix file lock | localhost listener guard | | Error handling | `try/except`, silent failures | `Result` throughout, no `unwrap()` | | Type safety | Runtime | Compile-time | @@ -376,6 +377,6 @@ MIT — see [LICENSE](LICENSE). ## Related projects -- [BitForge](https://github.com/csd113/BitForge-Rust) — builds Bitcoin Core and Electrs binaries for use with BitEngine +- [BitForge](https://github.com/csd113/BitForge) — builds Bitcoin Core and Electrs binaries for use with BitEngine - [Bitcoin Core](https://github.com/bitcoin/bitcoin) - [Electrs](https://github.com/romanz/electrs) diff --git a/app-icon.icns b/app-icon.icns new file mode 100644 index 0000000..3a71cf7 Binary files /dev/null and b/app-icon.icns differ diff --git a/build-mac-app.sh b/build-mac-app.sh new file mode 100755 index 0000000..892205d --- /dev/null +++ b/build-mac-app.sh @@ -0,0 +1,62 @@ +#!/usr/bin/env bash +set -euo pipefail + +APP_NAME="BitEngine" +BUNDLE_ID="com.yourname.bitengine" +VERSION="0.1.2" +TARGET="aarch64-apple-darwin" + +# Set this to your actual Cargo binary name if different +BIN_NAME="bitengine" + +ICON_FILE="app-icon.icns" +APP_DIR="${APP_NAME}.app" + +cargo build --release --target "${TARGET}" + +mkdir -p "${APP_DIR}/Contents/MacOS" +mkdir -p "${APP_DIR}/Contents/Resources" + +cp "target/${TARGET}/release/${BIN_NAME}" "${APP_DIR}/Contents/MacOS/${APP_NAME}" +chmod +x "${APP_DIR}/Contents/MacOS/${APP_NAME}" + +cp "${ICON_FILE}" "${APP_DIR}/Contents/Resources/${ICON_FILE}" + +cat > "${APP_DIR}/Contents/Info.plist" < + + + + CFBundleName + ${APP_NAME} + + CFBundleDisplayName + ${APP_NAME} + + CFBundleIdentifier + ${BUNDLE_ID} + + CFBundleVersion + ${VERSION} + + CFBundleShortVersionString + ${VERSION} + + CFBundleExecutable + ${APP_NAME} + + CFBundleIconFile + ${ICON_FILE} + + CFBundlePackageType + APPL + + LSMinimumSystemVersion + 10.13 + + +EOF + +codesign --deep --force --verify --sign - "${APP_DIR}" + +echo "Built ${APP_DIR}" diff --git a/src/config.rs b/src/config.rs index 1e08500..a2f0a67 100644 --- a/src/config.rs +++ b/src/config.rs @@ -1,19 +1,23 @@ //! Application configuration. //! -//! Stored as JSON in `~/Library/Application Support/BitcoinNodeManager/config.json` -//! (macOS) or `~/.config/BitcoinNodeManager/config.json` (other Unix). +//! Stored as JSON in the platform config directory resolved by `directories`. use std::path::{Path, PathBuf}; -use anyhow::{Context, Result}; +use anyhow::{Context as _, Result}; use directories::ProjectDirs; use serde::{Deserialize, Serialize}; -const APP_NAME: &str = "BitcoinNodeManager"; +const LEGACY_APP_NAME: &str = "BitcoinNodeManager"; const CONFIG_FILENAME: &str = "config.json"; +pub const DEFAULT_ELECTRS_METRICS_ADDR: &str = "127.0.0.1:4224"; +pub const DEFAULT_ELECTRUM_ADDR: &str = "127.0.0.1:50001"; /// All persisted settings for the node manager. -#[allow(clippy::struct_field_names)] +#[expect( + clippy::struct_field_names, + reason = "persisted fields mirror the stored config keys" +)] #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Config { /// Directory containing `bitcoind`, `bitcoin-cli`, `electrs`, etc. @@ -32,9 +36,25 @@ impl Config { match Self::load_from_file(&path) { Ok(cfg) => (cfg, None), - Err(e) => { - let warning = format!("Config load error ({e}), using defaults."); - eprintln!("{warning}"); + Err(primary_err) => { + if matches!( + primary_err.downcast_ref::(), + Some(err) if err.kind() == std::io::ErrorKind::NotFound + ) { + let legacy_path = Self::legacy_config_file_path(); + if legacy_path.exists() { + return match Self::load_from_file(&legacy_path) { + Ok(cfg) => (cfg, None), + Err(legacy_err) => { + let warning = + format!("Config load error ({legacy_err}), using defaults."); + (defaults, Some(warning)) + } + }; + } + } + + let warning = format!("Config load error ({primary_err}), using defaults."); (defaults, Some(warning)) } } @@ -55,12 +75,27 @@ impl Config { /// Path to the JSON config file on this platform. pub fn config_file_path() -> PathBuf { - ProjectDirs::from("", "", APP_NAME).map_or_else( - || dirs_fallback().join(CONFIG_FILENAME), + ProjectDirs::from("", "", crate::platform::APP_NAME).map_or_else( + || dirs_fallback(crate::platform::APP_NAME).join(CONFIG_FILENAME), + |proj| proj.config_dir().join(CONFIG_FILENAME), + ) + } + + fn legacy_config_file_path() -> PathBuf { + ProjectDirs::from("", "", LEGACY_APP_NAME).map_or_else( + || dirs_fallback(LEGACY_APP_NAME).join(CONFIG_FILENAME), |proj| proj.config_dir().join(CONFIG_FILENAME), ) } + pub fn electrs_metrics_url() -> String { + format!("http://{DEFAULT_ELECTRS_METRICS_ADDR}/metrics") + } + + pub const fn electrum_addr() -> &'static str { + DEFAULT_ELECTRUM_ADDR + } + // ── Internal helpers ───────────────────────────────────────────────────── fn defaults(ssd_root: &Path) -> Self { @@ -78,7 +113,6 @@ impl Config { } } -fn dirs_fallback() -> PathBuf { - let home = std::env::var("HOME").unwrap_or_else(|_| ".".into()); - PathBuf::from(home).join(".config").join(APP_NAME) +fn dirs_fallback(app_name: &str) -> PathBuf { + crate::platform::home_dir().join(".config").join(app_name) } diff --git a/src/electrs_status.rs b/src/electrs_status.rs new file mode 100644 index 0000000..8e456d7 --- /dev/null +++ b/src/electrs_status.rs @@ -0,0 +1,341 @@ +use std::time::Duration; + +use anyhow::{Context as _, Result}; +use reqwest::StatusCode; +use tokio::{net::TcpStream, time::timeout}; + +use crate::{ + config::Config, + rpc::{self, BlockchainInfo, RpcAuth}, +}; + +#[derive(Debug, Clone, Default, PartialEq)] +pub struct ElectrsStatus { + pub running: bool, + pub synced: bool, + pub ready: bool, + pub electrs_height: Option, + pub bitcoin_blocks: Option, + pub bitcoin_headers: Option, + pub sync_percent: Option, + pub metrics_error: Option, + pub bitcoin_error: Option, + pub connect_error: Option, +} + +pub async fn probe(config: &Config, process_running: bool) -> ElectrsStatus { + if !process_running { + return ElectrsStatus::default(); + } + + let auth = RpcAuth::from_data_dir(&config.bitcoin_data_path); + let metrics_url = Config::electrs_metrics_url(); + let electrum_addr = Config::electrum_addr().to_owned(); + + let (metrics_result, bitcoin_result, connect_result) = tokio::join!( + fetch_metrics(&metrics_url), + rpc::get_blockchain_info(&auth), + check_connectivity(&electrum_addr), + ); + + build_status( + process_running, + metrics_result.map_err(|e| e.to_string()), + bitcoin_result.map_err(|e| e.to_string()), + connect_result.map_err(|e| e.to_string()), + ) +} + +async fn fetch_metrics(url: &str) -> Result { + let response = reqwest::Client::builder() + .timeout(Duration::from_secs(3)) + .build() + .context("build metrics HTTP client")? + .get(url) + .send() + .await + .context("request electrs metrics")?; + + let status = response.status(); + if status != StatusCode::OK { + anyhow::bail!("metrics endpoint returned HTTP {status}"); + } + + response.text().await.context("read electrs metrics body") +} + +fn build_status( + process_running: bool, + metrics_result: Result, + bitcoin_result: Result, + connect_result: Result<(), String>, +) -> ElectrsStatus { + let running = process_running || metrics_result.is_ok(); + + let (electrs_height, metrics_error) = match metrics_result { + Ok(metrics) => match parse_tip_height(&metrics) { + Ok(height) => (Some(height), None), + Err(err) => (None, Some(err)), + }, + Err(err) => (None, Some(format!("metrics unreachable: {err}"))), + }; + + let (bitcoin_blocks, bitcoin_headers, bitcoin_error) = match bitcoin_result { + Ok(info) => (Some(info.blocks), Some(info.headers), None), + Err(err) => (None, None, Some(format!("Bitcoin Core unavailable: {err}"))), + }; + + let sync_percent = match (electrs_height, bitcoin_blocks) { + (Some(index_height), Some(blocks)) if blocks > 0 => u32::try_from(index_height) + .ok() + .zip(u32::try_from(blocks).ok()) + .map(|(index_height, blocks)| { + (f64::from(index_height) / f64::from(blocks) * 100.0).min(100.0) + }), + _ => None, + }; + + let synced = matches!( + (electrs_height, bitcoin_blocks), + (Some(index_height), Some(blocks)) if index_height >= blocks + ) && metrics_error.is_none() + && bitcoin_error.is_none(); + + let connect_error = connect_result + .err() + .map(|err| format!("connect failed: {err}")); + + // "Ready" follows the UI label semantics: a client is ready to connect when + // the Electrum port accepts a real TCP connection, even if indexing is still + // in progress. This stays separate from the "Synced" light on purpose. + let ready = connect_error.is_none(); + + ElectrsStatus { + running, + synced, + ready, + electrs_height, + bitcoin_blocks, + bitcoin_headers, + sync_percent, + metrics_error, + bitcoin_error, + connect_error, + } +} + +pub fn parse_tip_height(metrics: &str) -> Result { + let mut matching_lines = metrics + .lines() + .filter(|line| line.starts_with("electrs_index_height{") && line.contains("type=\"tip\"")); + + let Some(line) = matching_lines.next() else { + return Err("electrs tip height metric missing".to_owned()); + }; + + let Some(raw_value) = line.split_whitespace().last() else { + return Err("electrs tip height metric malformed".to_owned()); + }; + + raw_value + .parse::() + .map_err(|_| "electrs tip height metric malformed".to_owned()) +} + +async fn check_connectivity(addr: &str) -> Result<()> { + timeout(Duration::from_secs(2), TcpStream::connect(addr)) + .await + .context("electrum TCP connect timed out")? + .context("electrum TCP connect failed")?; + Ok(()) +} + +#[cfg(test)] +mod tests { + use std::path::PathBuf; + + use super::{build_status, parse_tip_height, probe, ElectrsStatus}; + use crate::{config::Config, rpc::BlockchainInfo}; + + #[test] + fn parses_tip_height_metric() { + let metrics = "# HELP electrs_index_height tip height\n\ +electrs_index_height{type=\"tip\"} 856201\n"; + + assert_eq!(parse_tip_height(metrics), Ok(856_201)); + } + + #[test] + fn reports_synced_when_heights_match() { + let status = build_status( + false, + Ok("electrs_index_height{type=\"tip\"} 856201".to_owned()), + Ok(BlockchainInfo { + blocks: 856_201, + headers: 856_201, + verification_progress: 1.0, + }), + Err("refused".to_owned()), + ); + + assert!(status.synced); + assert_eq!(status.sync_percent, Some(100.0)); + } + + #[test] + fn reports_not_synced_when_electrs_lags() { + let status = build_status( + false, + Ok("electrs_index_height{type=\"tip\"} 856100".to_owned()), + Ok(BlockchainInfo { + blocks: 856_201, + headers: 856_205, + verification_progress: 1.0, + }), + Err("refused".to_owned()), + ); + + assert!(!status.synced); + assert_eq!(status.electrs_height, Some(856_100)); + assert_eq!(status.bitcoin_blocks, Some(856_201)); + assert!(status.sync_percent.is_some_and(|percent| percent < 100.0)); + } + + #[test] + fn handles_missing_or_malformed_metrics_without_panicking() { + let missing = build_status( + false, + Ok("# no electrs tip metric here".to_owned()), + Ok(BlockchainInfo { + blocks: 100, + headers: 100, + verification_progress: 1.0, + }), + Err("refused".to_owned()), + ); + assert!(!missing.synced); + assert!(missing.metrics_error.is_some()); + + let malformed = build_status( + false, + Ok("electrs_index_height{type=\"tip\"} nope".to_owned()), + Ok(BlockchainInfo { + blocks: 100, + headers: 100, + verification_progress: 1.0, + }), + Err("refused".to_owned()), + ); + assert!(!malformed.synced); + assert!(malformed.metrics_error.is_some()); + + let unreachable = build_status( + false, + Err("connection reset".to_owned()), + Ok(BlockchainInfo { + blocks: 100, + headers: 100, + verification_progress: 1.0, + }), + Err("refused".to_owned()), + ); + assert!(!unreachable.synced); + assert!(unreachable.metrics_error.is_some()); + } + + #[test] + fn handles_bitcoin_unavailable_without_panicking() { + let status = build_status( + false, + Ok("electrs_index_height{type=\"tip\"} 856201".to_owned()), + Err("rpc auth failed".to_owned()), + Err("refused".to_owned()), + ); + + assert!(!status.synced); + assert!(status.bitcoin_error.is_some()); + assert_eq!(status.sync_percent, None); + } + + #[tokio::test] + async fn tcp_connect_success_marks_ready() { + let listener = tokio::net::TcpListener::bind("127.0.0.1:0") + .await + .expect("bind test listener"); + let addr = listener.local_addr().expect("listener addr"); + + let accept_task = tokio::spawn(async move { + let _ = listener.accept().await; + }); + + let status = build_status( + false, + Err("metrics down".to_owned()), + Err("bitcoin down".to_owned()), + super::check_connectivity(&addr.to_string()) + .await + .map_err(|e| e.to_string()), + ); + + assert!(status.ready); + accept_task.await.expect("accept task join"); + } + + #[tokio::test] + async fn tcp_connect_refused_marks_not_ready() { + let listener = tokio::net::TcpListener::bind("127.0.0.1:0") + .await + .expect("bind test listener"); + let addr = listener.local_addr().expect("listener addr"); + drop(listener); + + let status = build_status( + false, + Err("metrics down".to_owned()), + Err("bitcoin down".to_owned()), + super::check_connectivity(&addr.to_string()) + .await + .map_err(|e| e.to_string()), + ); + + assert!(!status.ready); + assert!(status.connect_error.is_some()); + } + + #[test] + fn running_is_true_from_process_check_even_if_metrics_fail() { + let status = build_status( + true, + Err("metrics down".to_owned()), + Err("bitcoin down".to_owned()), + Err("refused".to_owned()), + ); + + assert!(status.running); + } + + #[test] + fn running_is_true_from_metrics_even_without_process_check() { + let status = build_status( + false, + Ok("electrs_index_height{type=\"tip\"} 856201".to_owned()), + Err("bitcoin down".to_owned()), + Err("refused".to_owned()), + ); + + assert!(status.running); + } + + #[tokio::test] + async fn probe_is_quiet_when_electrs_process_is_not_running() { + let config = Config { + binaries_path: PathBuf::from("/missing/binaries"), + bitcoin_data_path: PathBuf::from("/missing/bitcoin"), + electrs_data_path: PathBuf::from("/missing/electrs"), + }; + + let status = probe(&config, false).await; + + assert_eq!(status, ElectrsStatus::default()); + } +} diff --git a/src/main.rs b/src/main.rs index 5e56495..f3a6306 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,55 +1,30 @@ -//! Bitcoin & Electrs Node Manager — macOS +//! `BitEngine` application entry point. //! //! Entry point. Responsibilities: -//! 1. Single-instance lock (prevents double-launch from macOS .app open events). -//! 2. Resolves the SSD root (directory containing this binary). +//! 1. Single-instance lock. +//! 2. Resolves the default working root. //! 3. Hands off to the Iced application loop. +#![expect( + clippy::multiple_crate_versions, + reason = "Iced, rfd, and their platform backends currently pull duplicate transitive crate versions" +)] + mod config; +mod electrs_status; +mod platform; mod process_manager; mod rpc; mod ui; mod updater; -use std::{ - fs::{self, OpenOptions}, - os::unix::fs::OpenOptionsExt, - path::{Path, PathBuf}, - process, -}; +use std::{path::PathBuf, process}; use iced::{window, Size, Task}; -/// Attempt to acquire an exclusive advisory lock on a temp file. -/// Returns an open file handle on success (caller must keep it alive). -/// Returns `None` if another instance already holds the lock. -fn acquire_single_instance_lock() -> Option { - use std::os::unix::io::AsRawFd; - let lock_path = std::env::temp_dir().join("BitcoinNodeManager.lock"); - - let file = OpenOptions::new() - .create(true) - .truncate(true) - .write(true) - .mode(0o600) - .open(&lock_path) - .ok()?; - - // LOCK_EX | LOCK_NB — non-blocking exclusive lock - let ret = unsafe { libc::flock(file.as_raw_fd(), libc::LOCK_EX | libc::LOCK_NB) }; - if ret == 0 { - Some(file) - } else { - None - } -} - fn main() -> iced::Result { // ── Single-instance guard ──────────────────────────────────────────────── - // macOS can fire two consecutive "open" events for the same .app bundle, - // causing the app to open and immediately close. We hold an exclusive - // flock() for the lifetime of the process. - let Some(_lock) = acquire_single_instance_lock() else { + let Some(_lock) = platform::SingleInstanceGuard::acquire() else { // Another instance is already running — exit silently. process::exit(0); }; @@ -62,12 +37,16 @@ fn main() -> iced::Result { // ── Launch Iced application ────────────────────────────────────────────── iced::application( - "Bitcoin & Electrs Node Manager", + move || { + let app = ui::App::new(&ssd_root); + (app, Task::none()) + }, ui::App::update, ui::App::view, ) + .title("BitEngine") .subscription(ui::App::subscription) - .theme(|_| iced::Theme::Dark) + .theme(app_theme) .window(window::Settings { size: Size::new(1440.0, 960.0), min_size: Some(Size::new(900.0, 700.0)), @@ -75,19 +54,29 @@ fn main() -> iced::Result { decorations: true, ..Default::default() }) - .run_with(move || { - let app = ui::App::new(&ssd_root); - (app, Task::none()) - }) + .run() +} + +const fn app_theme(_: &ui::App) -> iced::Theme { + iced::Theme::Dark } -/// Determine the SSD root directory. +/// Determine the default working root directory. /// /// Priority: -/// 1. If `BITCOIN_NODE_MANAGER_ROOT` env var is set, use that. -/// 2. If the binary is inside a `.app` bundle, walk up to the bundle's parent. -/// 3. Otherwise, use the directory containing the binary. +/// 1. If `BITENGINE_ROOT` env var is set, use that. +/// 2. If the legacy `BITCOIN_NODE_MANAGER_ROOT` env var is set, use that. +/// 3. On macOS, if the binary is inside a `.app` bundle, walk up to the +/// bundle's parent. +/// 4. Otherwise, use the directory containing the binary. fn resolve_ssd_root() -> PathBuf { + if let Ok(env_root) = std::env::var("BITENGINE_ROOT") { + let p = PathBuf::from(env_root); + if p.is_dir() { + return p; + } + } + if let Ok(env_root) = std::env::var("BITCOIN_NODE_MANAGER_ROOT") { let p = PathBuf::from(env_root); if p.is_dir() { @@ -98,20 +87,21 @@ fn resolve_ssd_root() -> PathBuf { let exe = std::env::current_exe().unwrap_or_else(|_| PathBuf::from(".")); let exe_dir = exe.parent().unwrap_or_else(|| std::path::Path::new(".")); - // If inside .app/Contents/MacOS/ - // walk up three levels to get the .app's parent directory. - if exe_dir.ends_with(Path::new("Contents/MacOS")) { - if let Some(bundle_parent) = exe_dir - .parent() // Contents/ - .and_then(|p| p.parent()) // .app/ - .and_then(|p| p.parent()) - // SSD root - { - return bundle_parent.to_path_buf(); + #[cfg(target_os = "macos")] + { + // If inside .app/Contents/MacOS/ + // walk up three levels to get the .app's parent directory. + if exe_dir.ends_with(std::path::Path::new("Contents/MacOS")) { + if let Some(bundle_parent) = exe_dir + .parent() // Contents/ + .and_then(|p| p.parent()) // .app/ + .and_then(|p| p.parent()) + // SSD root + { + return bundle_parent.to_path_buf(); + } } } exe_dir.to_path_buf() } - -// libc is used for flock() in acquire_single_instance_lock() diff --git a/src/platform.rs b/src/platform.rs new file mode 100644 index 0000000..e0d0585 --- /dev/null +++ b/src/platform.rs @@ -0,0 +1,163 @@ +//! Platform-specific helpers kept behind a small boundary. + +use std::{ + net::TcpListener, + path::{Path, PathBuf}, + process::{Child, Command}, +}; + +use anyhow::{Context as _, Result}; +use directories::UserDirs; + +pub const APP_NAME: &str = "BitEngine"; + +const SINGLE_INSTANCE_ADDR: &str = "127.0.0.1:49382"; + +#[derive(Debug)] +pub struct SingleInstanceGuard { + #[allow(dead_code)] + listener: TcpListener, +} + +impl SingleInstanceGuard { + #[must_use] + pub fn acquire() -> Option { + TcpListener::bind(SINGLE_INSTANCE_ADDR) + .ok() + .map(|listener| Self { listener }) + } +} + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum Platform { + MacOsAppleSilicon, + LinuxX64, + LinuxArm64, + Unsupported, +} + +impl Platform { + #[must_use] + pub fn current() -> Self { + match (std::env::consts::OS, std::env::consts::ARCH) { + ("macos", "aarch64") => Self::MacOsAppleSilicon, + ("linux", "x86_64") => Self::LinuxX64, + ("linux", "aarch64") => Self::LinuxArm64, + _ => Self::Unsupported, + } + } + + #[must_use] + pub const fn label(self) -> &'static str { + match self { + Self::MacOsAppleSilicon => "macOS Apple Silicon", + Self::LinuxX64 => "Linux x86_64", + Self::LinuxArm64 => "Linux ARM64", + Self::Unsupported => "unsupported platform", + } + } +} + +#[must_use] +pub fn executable_name(base: &str) -> String { + base.to_owned() +} + +#[must_use] +pub fn bitcoin_binary_names() -> Vec { + ["bitcoind", "bitcoin-cli", "bitcoin-tx", "bitcoin-util"] + .into_iter() + .map(executable_name) + .collect() +} + +#[must_use] +pub fn electrs_binary_name() -> String { + executable_name("electrs") +} + +#[must_use] +pub fn downloads_bitcoin_builds_dir() -> PathBuf { + UserDirs::new() + .and_then(|dirs| dirs.download_dir().map(Path::to_path_buf)) + .unwrap_or_else(home_dir) + .join("bitcoin_builds") +} + +#[must_use] +pub fn home_dir() -> PathBuf { + std::env::var_os("HOME").map_or_else( + || std::env::current_dir().unwrap_or_else(|_| PathBuf::from(".")), + PathBuf::from, + ) +} + +pub fn set_executable_permissions(path: &Path) -> Result<()> { + use std::os::unix::fs::PermissionsExt as _; + + let mut perms = std::fs::metadata(path) + .with_context(|| format!("stat {}", path.display()))? + .permissions(); + perms.set_mode(0o755); + std::fs::set_permissions(path, perms).with_context(|| format!("chmod {}", path.display())) +} + +pub fn terminate_child(child: &Child) { + if let Ok(pid) = libc::pid_t::try_from(child.id()) { + // SAFETY: `pid` comes from a live child process owned by this handle, + // and `kill` only sends a signal to that operating-system process. + let _ = unsafe { libc::kill(pid, libc::SIGTERM) }; + } +} + +#[cfg(target_os = "macos")] +#[must_use] +pub fn bitforge_app_path() -> Option { + let path = PathBuf::from("/Applications/BitForge.app"); + path.exists().then_some(path) +} + +#[cfg(not(target_os = "macos"))] +#[must_use] +pub fn bitforge_app_path() -> Option { + None +} + +pub fn open_path(path: &Path) -> Result<()> { + open_path_impl(path) +} + +#[cfg(target_os = "macos")] +fn open_path_impl(path: &Path) -> Result<()> { + Command::new("open") + .arg(path) + .spawn() + .with_context(|| format!("open {}", path.display()))?; + Ok(()) +} + +#[cfg(not(target_os = "macos"))] +fn open_path_impl(path: &Path) -> Result<()> { + Command::new("xdg-open") + .arg(path) + .spawn() + .with_context(|| format!("open {}", path.display()))?; + Ok(()) +} + +#[must_use] +pub fn command_display(program: &Path, args: &[String]) -> String { + std::iter::once(shell_display(program)) + .chain(args.iter().map(|arg| shell_display(Path::new(arg)))) + .collect::>() + .join(" ") +} + +fn shell_display(value: &Path) -> String { + let text = value.display().to_string(); + if text.contains(char::is_whitespace) { + format!("{text:?}") + } else { + text + } +} diff --git a/src/process_manager.rs b/src/process_manager.rs index 53199dd..2d0b2b9 100644 --- a/src/process_manager.rs +++ b/src/process_manager.rs @@ -10,7 +10,7 @@ use std::{ collections::VecDeque, - io::{BufRead, BufReader}, + io::{BufRead as _, BufReader}, path::Path, process::{Child, Command, Stdio}, sync::{Arc, Mutex}, @@ -18,7 +18,9 @@ use std::{ time::{Duration, Instant}, }; -use anyhow::{bail, Context, Result}; +use anyhow::{bail, Context as _, Result}; + +use crate::platform; // ── Thread-safe output queue ───────────────────────────────────────────────── @@ -52,11 +54,9 @@ impl ProcessHandle { matches!(self.child.try_wait(), Ok(None)) } - /// Graceful SIGTERM → 10 s wait → SIGKILL. + /// Graceful termination request → 10 s wait → kill fallback. pub fn terminate(&mut self) { - let pid = self.child.id().cast_signed(); - // Attempt graceful shutdown with SIGTERM - let _ = unsafe { libc::kill(pid, libc::SIGTERM) }; + platform::terminate_child(&self.child); let deadline = Instant::now() + Duration::from_secs(10); loop { if Instant::now() >= deadline { @@ -67,7 +67,7 @@ impl ProcessHandle { _ => thread::sleep(Duration::from_millis(200)), } } - // Escalate to SIGKILL + // Escalate to the platform kill fallback. let _ = self.child.kill(); let _ = self.child.wait(); } @@ -81,9 +81,9 @@ impl ProcessHandle { pub fn launch_bitcoind( binaries_path: &Path, data_dir: &Path, - queue: OutputQueue, + queue: &OutputQueue, ) -> Result { - let bitcoind = binaries_path.join("bitcoind"); + let bitcoind = binaries_path.join(platform::executable_name("bitcoind")); if !bitcoind.exists() { bail!("bitcoind not found at {}", bitcoind.display()); } @@ -91,22 +91,24 @@ pub fn launch_bitcoind( std::fs::create_dir_all(data_dir) .with_context(|| format!("create bitcoin data dir {}", data_dir.display()))?; - let cmd = [ - bitcoind.to_string_lossy().into_owned(), + let args = [ format!("-datadir={}", data_dir.display()), "-printtoconsole".into(), ]; - push_line(&queue, format!("$ {}", cmd.join(" "))); + push_line( + queue, + format!("$ {}", platform::command_display(&bitcoind, &args)), + ); - let child = Command::new(&cmd[0]) - .args(&cmd[1..]) + let child = Command::new(&bitcoind) + .args(&args) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() .with_context(|| format!("spawn bitcoind {}", bitcoind.display()))?; - spawn_reader_thread(child, &queue) + spawn_reader_thread(child, queue) } // ── Electrs ─────────────────────────────────────────────────────────────────── @@ -116,9 +118,10 @@ pub fn launch_electrs( binaries_path: &Path, bitcoin_data_dir: &Path, electrs_db_dir: &Path, - queue: OutputQueue, + electrum_addr: &str, + queue: &OutputQueue, ) -> Result { - let electrs = binaries_path.join("electrs"); + let electrs = binaries_path.join(platform::electrs_binary_name()); if !electrs.exists() { bail!("electrs not found at {}", electrs.display()); } @@ -126,8 +129,7 @@ pub fn launch_electrs( std::fs::create_dir_all(electrs_db_dir) .with_context(|| format!("create electrs db dir {}", electrs_db_dir.display()))?; - let cmd = [ - electrs.to_string_lossy().into_owned(), + let args = [ "--network".into(), "bitcoin".into(), "--daemon-dir".into(), @@ -135,19 +137,22 @@ pub fn launch_electrs( "--db-dir".into(), electrs_db_dir.to_string_lossy().into_owned(), "--electrum-rpc-addr".into(), - "127.0.0.1:50001".into(), + electrum_addr.to_owned(), ]; - push_line(&queue, format!("$ {}", cmd.join(" "))); + push_line( + queue, + format!("$ {}", platform::command_display(&electrs, &args)), + ); - let child = Command::new(&cmd[0]) - .args(&cmd[1..]) + let child = Command::new(&electrs) + .args(&args) .stdout(Stdio::piped()) .stderr(Stdio::piped()) .spawn() .with_context(|| format!("spawn electrs {}", electrs.display()))?; - spawn_reader_thread(child, &queue) + spawn_reader_thread(child, queue) } // ── Reader thread ───────────────────────────────────────────────────────────── @@ -190,15 +195,3 @@ fn spawn_reader_thread(mut child: Child, queue: &OutputQueue) -> Result bool { - let l = line.to_ascii_lowercase(); - l.contains("finished full compaction") - || l.contains("electrs running") - || l.contains("waiting for new block") - || l.contains("index update completed") - || l.contains("chain best block") -} diff --git a/src/rpc.rs b/src/rpc.rs index 2a482b5..b85453d 100644 --- a/src/rpc.rs +++ b/src/rpc.rs @@ -7,7 +7,7 @@ use std::path::Path; use std::time::Duration; -use anyhow::{bail, Context, Result}; +use anyhow::{bail, Context as _, Result}; use reqwest::Client; use serde::{Deserialize, Serialize}; use serde_json::Value; @@ -196,7 +196,7 @@ pub fn ensure_bitcoin_conf(data_dir: &Path) -> Result<()> { .with_context(|| format!("create bitcoin data dir {}", data_dir.display()))?; std::fs::write( &conf_path, - "# Bitcoin Core — auto-generated by Bitcoin Node Manager\n\ + "# Bitcoin Core — auto-generated by BitEngine\n\ server=1\n\ txindex=1\n\ rpcport=8332\n\ diff --git a/src/ui.rs b/src/ui.rs index 091638c..04508ec 100644 --- a/src/ui.rs +++ b/src/ui.rs @@ -2,7 +2,7 @@ //! //! Architecture overview //! ───────────────────── -//! The app follows the Elm/MVU pattern enforced by Iced 0.13: +//! The app follows the Elm/MVU pattern enforced by Iced 0.14: //! //! * `App` — immutable snapshot of all UI state. //! * `Message` — every possible event (user action, timer tick, @@ -30,11 +30,17 @@ use std::{ #[path = "ui_render.rs"] mod ui_render; -use iced::{time, widget::scrollable, Element, Subscription, Task}; +use iced::{ + time, + widget::{self, scrollable}, + Element, Subscription, Task, +}; use crate::{ config::Config, - process_manager::{self, is_electrs_synced_line, new_queue, OutputQueue, ProcessHandle}, + electrs_status::{self, ElectrsStatus}, + platform::{self, Platform}, + process_manager::{self, new_queue, OutputQueue, ProcessHandle}, rpc::{self, BlockchainInfo, RpcAuth}, updater::{self, UpdateResult}, }; @@ -70,7 +76,7 @@ pub enum Message { ShutdownElectrsOnly, // ── Async results ───────────────────────────────────────────────────────── - BlockchainInfoReceived(Result), + StatusPollReceived(StatusPollResult), UpdateBinaries, UpdateResult(String), // human-readable outcome message @@ -79,14 +85,10 @@ pub enum Message { DismissOverlay, /// Open BitForge.app (update flow). OpenBitForge(PathBuf), - - // ── No-op (used to complete Tasks that return nothing useful) ───────────── - Noop, } // ── App state ───────────────────────────────────────────────────────────────── -#[allow(clippy::struct_excessive_bools)] pub struct App { // ── Config ─────────────────────────────────────────────────────────────── config: Config, @@ -111,8 +113,7 @@ pub struct App { // ── Node status ─────────────────────────────────────────────────────────── bitcoin_running: bool, bitcoin_synced: bool, - electrs_running: bool, - electrs_synced: bool, + electrs_status: ElectrsStatus, block_height: u64, // ── UI state ────────────────────────────────────────────────────────────── @@ -124,6 +125,12 @@ pub struct App { bitforge_path: Option, } +#[derive(Debug, Clone)] +pub struct StatusPollResult { + blockchain_info: Result, + electrs_status: ElectrsStatus, +} + impl App { pub fn new(ssd_root: &Path) -> Self { let (config, config_warning) = Config::load(ssd_root); @@ -136,7 +143,11 @@ impl App { let electrs_queue = new_queue(); // Log startup info into the terminal queues - push_msg(&bitcoin_queue, "=== Bitcoin Node Manager started ==="); + push_msg(&bitcoin_queue, "=== BitEngine started ==="); + push_msg( + &bitcoin_queue, + &format!("Platform : {}", Platform::current().label()), + ); if let Some(warning) = config_warning.as_ref() { push_msg(&bitcoin_queue, warning); push_msg(&electrs_queue, warning); @@ -149,15 +160,21 @@ impl App { &bitcoin_queue, &format!("Binaries : {}", config.binaries_path.display()), ); + log_binary_resolution(&bitcoin_queue, &config.binaries_path, "bitcoind"); push_msg( &bitcoin_queue, &format!("Data dir : {}", config.bitcoin_data_path.display()), ); - push_msg(&electrs_queue, "=== Electrs Node Manager started ==="); + push_msg(&electrs_queue, "=== BitEngine started ==="); + push_msg( + &electrs_queue, + &format!("Platform : {}", Platform::current().label()), + ); push_msg( &electrs_queue, &format!("Binaries : {}", config.binaries_path.display()), ); + log_binary_resolution(&electrs_queue, &config.binaries_path, "electrs"); push_msg( &electrs_queue, &format!("DB dir : {}", config.electrs_data_path.display()), @@ -176,8 +193,7 @@ impl App { electrs_lines: Vec::new(), bitcoin_running: false, bitcoin_synced: false, - electrs_running: false, - electrs_synced: false, + electrs_status: ElectrsStatus::default(), block_height: 0, paths_visible: true, overlay_message: None, @@ -243,13 +259,16 @@ impl App { Message::LaunchElectrs => self.launch_electrs(), Message::ShutdownBoth => self.shutdown_both(), Message::ShutdownElectrsOnly => self.shutdown_electrs_only(), - Message::BlockchainInfoReceived(result) => { - if let Ok(info) = result { + Message::StatusPollReceived(result) => { + if let Ok(info) = result.blockchain_info { self.block_height = info.blocks; self.bitcoin_synced = info.headers > 0 && info.blocks >= info.headers.saturating_sub(1) && info.verification_progress > 0.9999; + } else if !self.bitcoin_running { + self.block_height = 0; } + self.apply_electrs_status(result.electrs_status); Task::none() } Message::UpdateBinaries => self.update_binaries(), @@ -263,12 +282,16 @@ impl App { Task::none() } Message::OpenBitForge(path) => { - let _ = std::process::Command::new("open").arg(&path).spawn(); + if let Err(err) = platform::open_path(&path) { + self.overlay_message = + Some(format!("Failed to open {}:\n{err}", path.display())); + self.bitforge_path = None; + return Task::none(); + } self.overlay_message = None; self.bitforge_path = None; Task::none() } - Message::Noop => Task::none(), } } @@ -286,9 +309,6 @@ impl App { if let Ok(mut q) = self.electrs_queue.lock() { while let Some(line) = q.pop_front() { - if is_electrs_synced_line(&line) { - self.electrs_synced = true; - } self.electrs_lines.push(line); els_new = true; } @@ -303,52 +323,45 @@ impl App { self.electrs_lines.drain(..drain_to); } - if self.bitcoin_running { - if let Some(h) = &mut self.bitcoin_handle { - if !h.is_running() { - self.bitcoin_running = false; - self.bitcoin_synced = false; - self.block_height = 0; - self.electrs_synced = false; - push_msg(&self.bitcoin_queue, "bitcoind has stopped."); - } + if let Some(h) = &mut self.bitcoin_handle { + if !h.is_running() { + self.bitcoin_handle = None; + self.bitcoin_running = false; + self.bitcoin_synced = false; + self.block_height = 0; + self.electrs_status.synced = false; + push_msg(&self.bitcoin_queue, "bitcoind has stopped."); } } - if self.electrs_running { - if let Some(h) = &mut self.electrs_handle { - if !h.is_running() { - self.electrs_running = false; - self.electrs_synced = false; - push_msg(&self.electrs_queue, "electrs has stopped."); - } + if let Some(h) = &mut self.electrs_handle { + if !h.is_running() { + self.electrs_handle = None; + self.electrs_status.running = false; + self.electrs_status.synced = false; + self.electrs_status.ready = false; + push_msg(&self.electrs_queue, "electrs has stopped."); } } let mut tasks: Vec> = Vec::new(); if btc_new { - tasks.push( - scrollable::scroll_to( - ui_render::bitcoin_scroll_id(), - scrollable::AbsoluteOffset { - x: 0.0, - y: f32::MAX, - }, - ) - .map(|_: iced_runtime::Action| Message::Noop), - ); + tasks.push(widget::operation::scroll_to( + ui_render::bitcoin_scroll_id(), + scrollable::AbsoluteOffset { + x: 0.0, + y: f32::MAX, + }, + )); } if els_new { - tasks.push( - scrollable::scroll_to( - ui_render::electrs_scroll_id(), - scrollable::AbsoluteOffset { - x: 0.0, - y: f32::MAX, - }, - ) - .map(|_: iced_runtime::Action| Message::Noop), - ); + tasks.push(widget::operation::scroll_to( + ui_render::electrs_scroll_id(), + scrollable::AbsoluteOffset { + x: 0.0, + y: f32::MAX, + }, + )); } if tasks.is_empty() { @@ -359,17 +372,27 @@ impl App { } fn handle_rpc_tick(&self) -> Task { - if !self.bitcoin_running { - return Task::none(); - } let auth = RpcAuth::from_data_dir(&self.config.bitcoin_data_path); + let config = self.config.clone(); + let process_running = self.electrs_handle.is_some(); + Task::perform( async move { - rpc::get_blockchain_info(&auth) - .await - .map_err(|e| e.to_string()) + let (blockchain_info, electrs_status) = tokio::join!( + async { + rpc::get_blockchain_info(&auth) + .await + .map_err(|e| e.to_string()) + }, + electrs_status::probe(&config, process_running), + ); + + StatusPollResult { + blockchain_info, + electrs_status, + } }, - Message::BlockchainInfoReceived, + Message::StatusPollReceived, ) } @@ -437,7 +460,7 @@ impl App { match process_manager::launch_bitcoind( &self.config.binaries_path, &self.config.bitcoin_data_path, - Arc::clone(&self.bitcoin_queue), + &self.bitcoin_queue, ) { Ok(handle) => { self.bitcoin_handle = Some(handle); @@ -453,7 +476,7 @@ impl App { } fn launch_electrs(&mut self) -> Task { - if self.electrs_running { + if self.electrs_status.running { self.overlay_message = Some("Electrs is already running.".into()); return Task::none(); } @@ -470,12 +493,15 @@ impl App { &self.config.binaries_path, &self.config.bitcoin_data_path, &self.config.electrs_data_path, - Arc::clone(&self.electrs_queue), + Config::electrum_addr(), + &self.electrs_queue, ) { Ok(handle) => { self.electrs_handle = Some(handle); - self.electrs_running = true; - self.electrs_synced = false; + self.electrs_status = ElectrsStatus { + running: true, + ..ElectrsStatus::default() + }; } Err(e) => { push_msg(&self.electrs_queue, &format!("Launch error: {e}")); @@ -503,8 +529,7 @@ impl App { tokio::runtime::Builder::new_current_thread() .enable_all() .build() - .map(|r| r.block_on(rpc::stop_bitcoind(&auth)).is_ok()) - .unwrap_or(false) + .is_ok_and(|r| r.block_on(rpc::stop_bitcoind(&auth)).is_ok()) }, |rt| rt.block_on(rpc::stop_bitcoind(&auth)).is_ok(), ); @@ -537,7 +562,7 @@ impl App { Task::none() } - fn update_binaries(&mut self) -> Task { + fn update_binaries(&self) -> Task { let binaries_dst = self.config.binaries_path.clone(); let btc_q = Arc::clone(&self.bitcoin_queue); Task::perform( @@ -552,11 +577,12 @@ impl App { format!("__BITFORGE_FOUND__{}", path.display()) } UpdateResult::BitForgeNotFound => "No bitcoin_builds folder found.\n\n\ - Download BitForge from:\n\ - https://github.com/csd113/BitForge-Python" + Place platform-specific bitcoin/electrs builds under your Downloads bitcoin_builds/binaries folder.\n\n\ + On macOS, BitForge can build those binaries:\n\ + https://github.com/csd113/BitForge" .into(), UpdateResult::BinariesSubfolderMissing => { - "Found ~/Downloads/bitcoin_builds but no 'binaries/' sub-folder inside it." + "Found bitcoin_builds in Downloads but no 'binaries/' sub-folder inside it." .into() } UpdateResult::NothingToUpdate => { @@ -593,8 +619,71 @@ impl App { push_msg(&els_q, "electrs stopped."); }); } - self.electrs_running = false; - self.electrs_synced = false; + self.electrs_status = ElectrsStatus::default(); + } + + fn apply_electrs_status(&mut self, mut next_status: ElectrsStatus) { + let previous = self.electrs_status.clone(); + let warnings_ready = self.bitcoin_running && self.bitcoin_synced && next_status.running; + + if !warnings_ready { + next_status.metrics_error = None; + next_status.bitcoin_error = None; + next_status.connect_error = None; + } + + if warnings_ready { + if previous.metrics_error != next_status.metrics_error { + if let Some(error) = next_status.metrics_error.as_deref() { + push_msg( + &self.electrs_queue, + &format!("Electrs metrics check failed: {error}"), + ); + } + } + + if previous.bitcoin_error != next_status.bitcoin_error { + if let Some(error) = next_status.bitcoin_error.as_deref() { + push_msg( + &self.electrs_queue, + &format!("Electrs sync check failed: {error}"), + ); + } + } + + if previous.connect_error != next_status.connect_error { + if let Some(error) = next_status.connect_error.as_deref() { + push_msg( + &self.electrs_queue, + &format!("Electrs connectivity check failed: {error}"), + ); + } + } + } + + if !previous.synced && next_status.synced { + let height = next_status.electrs_height.unwrap_or_default(); + push_msg( + &self.electrs_queue, + &format!("Electrs synced to Bitcoin Core at height {height}."), + ); + } else if previous.synced != next_status.synced { + if let (Some(index_height), Some(blocks)) = + (next_status.electrs_height, next_status.bitcoin_blocks) + { + let headers = next_status + .bitcoin_headers + .map_or_else(|| "unknown".to_owned(), |value| value.to_string()); + push_msg( + &self.electrs_queue, + &format!( + "Electrs not yet synced: indexed={index_height}, blocks={blocks}, headers={headers}." + ), + ); + } + } + + self.electrs_status = next_status; } // ── subscription ────────────────────────────────────────────────────────── @@ -631,3 +720,16 @@ fn push_msg(queue: &OutputQueue, msg: &str) { q.push_back(msg.to_owned()); } } + +fn log_binary_resolution(queue: &OutputQueue, binaries_path: &Path, binary: &str) { + let resolved = binaries_path.join(platform::executable_name(binary)); + let status = if resolved.exists() { + "found" + } else { + "missing" + }; + push_msg( + queue, + &format!("Resolved {binary}: {} ({status})", resolved.display()), + ); +} diff --git a/src/ui_render.rs b/src/ui_render.rs index 767ae07..4280df5 100644 --- a/src/ui_render.rs +++ b/src/ui_render.rs @@ -1,13 +1,13 @@ use std::path::PathBuf; -use iced::widget::scrollable::{Direction, Id as ScrollId, Scrollbar}; +use iced::widget::scrollable::{Direction, Scrollbar}; use iced::{ font::Font, - widget::{button, column, container, row, scrollable, text, text_input, Space}, + widget::{button, column, container, row, scrollable, text, text_input, Id, Space}, Alignment, Color, Element, Length, Padding, }; -use crate::config::Config; +use crate::{config::Config, platform::APP_NAME}; use super::{App, Message}; @@ -119,13 +119,19 @@ const TEXT_TER: Color = Color { b: 0.576, a: 1.0, }; // #8e8e93 +const DISABLED_BG: Color = Color { + r: 0.914, + g: 0.914, + b: 0.933, + a: 1.0, +}; // #e9e9ee -pub(super) fn bitcoin_scroll_id() -> ScrollId { - ScrollId::new("bitcoin_terminal") +pub(super) const fn bitcoin_scroll_id() -> Id { + Id::new("bitcoin_terminal") } -pub(super) fn electrs_scroll_id() -> ScrollId { - ScrollId::new("electrs_terminal") +pub(super) const fn electrs_scroll_id() -> Id { + Id::new("electrs_terminal") } pub(super) fn view(app: &App) -> Element<'_, Message> { @@ -135,7 +141,7 @@ pub(super) fn view(app: &App) -> Element<'_, Message> { view_paths_panel(app), view_node_panels(app), horizontal_rule(), - view_bottom_bar(), + view_bottom_bar(app), ] .width(Length::Fill) .height(Length::Fill); @@ -167,9 +173,26 @@ fn view_toolbar(app: &App) -> Element<'_, Message> { } out.chars().rev().collect::() } else { - "Connecting…".to_owned() + "Waiting for node".to_owned() }; + let title_block = column![ + text(APP_NAME) + .size(22) + .font(Font { + weight: iced::font::Weight::Bold, + ..Font::default() + }) + .color(Color::BLACK), + text("Bitcoin Core and Electrs control panel") + .size(11) + .color(TEXT_SEC) + .width(Length::Fill) + .wrapping(iced::widget::text::Wrapping::WordOrGlyph), + ] + .spacing(2) + .width(Length::FillPortion(2)); + let block_stat = column![ text("BLOCK HEIGHT").size(9).color(TEXT_TER), text(height_text) @@ -180,18 +203,26 @@ fn view_toolbar(app: &App) -> Element<'_, Message> { }) .color(Color::BLACK), ] - .spacing(2); + .spacing(2) + .width(Length::Shrink); let update_btn = - styled_button("Update Binaries…", ButtonStyle::Secondary).on_press(Message::UpdateBinaries); - - let toolbar_row = row![block_stat, Space::with_width(Length::Fill), update_btn,] - .align_y(Alignment::Center) - .padding(Padding::from([0, 16])); + styled_button("Update Binaries", ButtonStyle::Secondary).on_press(Message::UpdateBinaries); + + let toolbar_row = row![ + title_block, + Space::new().width(Length::Fill), + block_stat, + Space::new().width(16), + update_btn, + ] + .spacing(0) + .align_y(Alignment::Center) + .padding(Padding::from([0, 20])); container(toolbar_row) .width(Length::Fill) - .height(56) + .height(64) .style(|_| container::Style { background: Some(BAR.into()), ..Default::default() @@ -200,21 +231,30 @@ fn view_toolbar(app: &App) -> Element<'_, Message> { } fn view_paths_panel(app: &App) -> Element<'_, Message> { - let toggle_label = if app.paths_visible { "Hide" } else { "Show" }; + let toggle_label = if app.paths_visible { + "Hide Paths" + } else { + "Show Paths" + }; - let header = row![ + let heading = column![ text("DIRECTORY PATHS").size(10).color(TEXT_TER), - text(format!( - " Config: {}", - Config::config_file_path().display() - )) - .size(9) - .color(TEXT_TER), - Space::with_width(Length::Fill), + text(format!("Config: {}", Config::config_file_path().display())) + .size(9) + .color(TEXT_TER) + .width(Length::Fill) + .wrapping(iced::widget::text::Wrapping::WordOrGlyph), + ] + .spacing(3) + .width(Length::Fill); + + let header = row![ + heading, + Space::new().width(Length::Fill), styled_button(toggle_label, ButtonStyle::Secondary).on_press(Message::TogglePathsPanel), ] .align_y(Alignment::Center) - .padding(Padding::from([10, 20])); + .padding(Padding::from([12, 20])); if !app.paths_visible { return container(header) @@ -229,6 +269,7 @@ fn view_paths_panel(app: &App) -> Element<'_, Message> { let rows = column![ path_row( "Binaries Folder", + "Folder containing bitcoind, bitcoin-cli, and electrs", &app.binaries_path_edit, Message::BinariesPathChanged, Message::BrowseBinaries, @@ -236,6 +277,7 @@ fn view_paths_panel(app: &App) -> Element<'_, Message> { ), path_row( "Bitcoin Data Directory", + "Bitcoin Core data directory", &app.bitcoin_data_path_edit, Message::BitcoinDataPathChanged, Message::BrowseBitcoinData, @@ -243,6 +285,7 @@ fn view_paths_panel(app: &App) -> Element<'_, Message> { ), path_row( "Electrs DB Directory", + "Electrs index database directory", &app.electrs_data_path_edit, Message::ElectrsDataPathChanged, Message::BrowseElectrsData, @@ -252,13 +295,13 @@ fn view_paths_panel(app: &App) -> Element<'_, Message> { text("Changes take effect on the next node launch.") .size(10) .color(TEXT_TER), - Space::with_width(Length::Fill), + Space::new().width(Length::Fill), styled_button("Save Paths", ButtonStyle::Confirm).on_press(Message::SavePaths), ] .align_y(Alignment::Center) .padding(Padding::from([8, 0])), ] - .spacing(4) + .spacing(6) .padding(Padding::from([0, 20])); let body = column![header, rows].padding(Padding { @@ -280,8 +323,14 @@ fn view_paths_panel(app: &App) -> Element<'_, Message> { fn view_node_panels(app: &App) -> Element<'_, Message> { let bitcoin_panel = view_node_panel(NodePanelSpec { title: "Bitcoin", + subtitle: "Bitcoin Core node", accent: BTC_ACC, - launch_msg: Message::LaunchBitcoin, + launch_action: (!app.bitcoin_running).then_some(Message::LaunchBitcoin), + launch_hint: if app.bitcoin_running { + "Bitcoin is already running." + } else { + "Starts bitcoind with the configured data directory." + }, running: app.bitcoin_running, synced: app.bitcoin_synced, ready: app.bitcoin_running && app.bitcoin_synced, @@ -290,11 +339,20 @@ fn view_node_panels(app: &App) -> Element<'_, Message> { }); let electrs_panel = view_node_panel(NodePanelSpec { title: "Electrs", + subtitle: "Electrum server index", accent: ELS_ACC, - launch_msg: Message::LaunchElectrs, - running: app.electrs_running, - synced: app.electrs_synced, - ready: app.electrs_running && app.electrs_synced, + launch_action: (app.bitcoin_running && !app.electrs_status.running) + .then_some(Message::LaunchElectrs), + launch_hint: if app.electrs_status.running { + "Electrs is already running." + } else if !app.bitcoin_running { + "Start Bitcoin before launching Electrs." + } else { + "Starts electrs against the configured Bitcoin data directory." + }, + running: app.electrs_status.running, + synced: app.electrs_status.synced, + ready: app.electrs_status.ready, lines: &app.electrs_lines, scroll_id: electrs_scroll_id(), }); @@ -307,19 +365,27 @@ fn view_node_panels(app: &App) -> Element<'_, Message> { struct NodePanelSpec<'a> { title: &'a str, + subtitle: &'a str, accent: Color, - launch_msg: Message, + launch_action: Option, + launch_hint: &'a str, running: bool, synced: bool, ready: bool, lines: &'a [String], - scroll_id: ScrollId, + scroll_id: Id, } fn view_node_panel(spec: NodePanelSpec<'_>) -> Element<'_, Message> { let panel = column![ accent_bar(spec.accent), - panel_header(spec.title, spec.accent, spec.launch_msg), + panel_header( + spec.title, + spec.subtitle, + spec.accent, + spec.launch_action, + spec.launch_hint, + ), horizontal_rule(), panel_indicators(spec.running, spec.synced, spec.ready), horizontal_rule(), @@ -344,7 +410,7 @@ fn view_node_panel(spec: NodePanelSpec<'_>) -> Element<'_, Message> { } fn accent_bar(accent: Color) -> Element<'static, Message> { - container(Space::with_height(3)) + container(Space::new().height(3)) .width(Length::Fill) .style(move |_| container::Style { background: Some(accent.into()), @@ -353,33 +419,39 @@ fn accent_bar(accent: Color) -> Element<'static, Message> { .into() } -fn panel_header(title: &str, accent: Color, launch_msg: Message) -> Element<'_, Message> { - let launch_btn = button( - text("Launch") - .size(13) - .font(Font { - weight: iced::font::Weight::Bold, - ..Font::default() - }) - .color(Color::WHITE), - ) +fn panel_header<'a>( + title: &'a str, + subtitle: &'a str, + accent: Color, + launch_action: Option, + launch_hint: &'a str, +) -> Element<'a, Message> { + let launch_btn = button(text("Launch").size(13).font(Font { + weight: iced::font::Weight::Bold, + ..Font::default() + })) .padding(Padding::from([5, 18])) - .style(move |_, status| button::Style { - background: Some(match status { - button::Status::Hovered | button::Status::Pressed => darken(accent).into(), - _ => accent.into(), - }), - text_color: Color::WHITE, - border: iced::Border { - color: Color::TRANSPARENT, - width: 0.0, - radius: 6.0.into(), - }, - shadow: iced::Shadow::default(), + .style(move |_, status| { + let disabled = status == button::Status::Disabled; + button::Style { + background: Some(match status { + button::Status::Disabled => DISABLED_BG.into(), + button::Status::Hovered | button::Status::Pressed => darken(accent).into(), + button::Status::Active => accent.into(), + }), + text_color: if disabled { TEXT_TER } else { Color::WHITE }, + border: iced::Border { + color: Color::TRANSPARENT, + width: 0.0, + radius: 6.0.into(), + }, + shadow: iced::Shadow::default(), + snap: false, + } }) - .on_press(launch_msg); + .on_press_maybe(launch_action); - row![ + let heading = column![ text(title) .size(20) .font(Font { @@ -387,51 +459,59 @@ fn panel_header(title: &str, accent: Color, launch_msg: Message) -> Element<'_, ..Font::default() }) .color(Color::BLACK), - Space::with_width(Length::Fill), - launch_btn, + text(subtitle).size(11).color(TEXT_SEC), + text(launch_hint) + .size(10) + .color(TEXT_TER) + .width(Length::Fill) + .wrapping(iced::widget::text::Wrapping::WordOrGlyph), ] - .align_y(Alignment::Center) - .padding(Padding { - top: 14.0, - right: 20.0, - bottom: 10.0, - left: 20.0, - }) - .into() + .spacing(3) + .width(Length::Fill); + + row![heading, Space::new().width(Length::Fill), launch_btn,] + .align_y(Alignment::Center) + .padding(Padding { + top: 14.0, + right: 20.0, + bottom: 10.0, + left: 20.0, + }) + .into() } fn panel_indicators(running: bool, synced: bool, ready: bool) -> Element<'static, Message> { row![ - indicator_badge("Running", running), - Space::with_width(24), - indicator_badge("Synced", synced), - Space::with_width(24), - indicator_badge("Ready", ready), + indicator_badge( + "Process", + if running { "Running" } else { "Stopped" }, + running + ), + Space::new().width(8), + indicator_badge("Chain", if synced { "Synced" } else { "Waiting" }, synced), + Space::new().width(8), + indicator_badge("Service", if ready { "Ready" } else { "Not ready" }, ready), ] .align_y(Alignment::Center) - .padding(Padding::from([8, 20])) + .padding(Padding::from([10, 20])) .into() } -fn terminal_container( - running: bool, - lines: &[String], - scroll_id: ScrollId, -) -> Element<'_, Message> { +fn terminal_container(running: bool, lines: &[String], scroll_id: Id) -> Element<'_, Message> { let terminal_header = container( row![ row![ - text("TERMINAL").size(9).color(TEXT_TER), - Space::with_width(6), + text("LOG OUTPUT").size(9).color(TEXT_TER), + Space::new().width(6), text(format!("{} lines", lines.len())) .size(9) .color(TERM_DIM), ] .align_y(Alignment::Center), - Space::with_width(Length::Fill), + Space::new().width(Length::Fill), row![ text("●").size(11).color(if running { GREEN } else { OFF }), - Space::with_width(4), + Space::new().width(4), text(if running { "Live" } else { "Idle" }) .size(9) .color(TERM_DIM), @@ -457,8 +537,11 @@ fn terminal_container( ..Default::default() }); - let terminal_lines: Vec> = - lines.iter().map(|l| terminal_line_element(l)).collect(); + let terminal_lines: Vec> = if lines.is_empty() { + vec![empty_terminal_state(running)] + } else { + lines.iter().map(|l| terminal_line_element(l)).collect() + }; let terminal_content = column(terminal_lines) .spacing(0) @@ -487,15 +570,32 @@ fn terminal_container( .into() } -fn view_bottom_bar() -> Element<'static, Message> { - let shutdown_both = styled_button("Shutdown Bitcoind & Electrs", ButtonStyle::Destructive) - .on_press(Message::ShutdownBoth); +fn view_bottom_bar(app: &App) -> Element<'_, Message> { + let any_running = app.bitcoin_running || app.electrs_status.running; + let electrs_running = app.electrs_status.running; + + let shutdown_both = styled_button("Shutdown Bitcoin & Electrs", ButtonStyle::Destructive) + .on_press_maybe(any_running.then_some(Message::ShutdownBoth)); let shutdown_els = styled_button("Shutdown Electrs Only", ButtonStyle::Warning) - .on_press(Message::ShutdownElectrsOnly); + .on_press_maybe(electrs_running.then_some(Message::ShutdownElectrsOnly)); + let help_text = if any_running { + "Shutdown requests use graceful stop first, then terminate if needed." + } else { + "Start a service before shutdown controls become available." + }; - let btn_row = row![shutdown_both, Space::with_width(8), shutdown_els] - .align_y(Alignment::Center) - .padding(Padding::from([12, 16])); + let btn_row = row![ + text(help_text) + .size(10) + .color(TEXT_TER) + .width(Length::Fill) + .wrapping(iced::widget::text::Wrapping::WordOrGlyph), + shutdown_both, + Space::new().width(8), + shutdown_els, + ] + .align_y(Alignment::Center) + .padding(Padding::from([12, 16])); container(btn_row) .width(Length::Fill) @@ -523,14 +623,24 @@ fn view_overlay(message: &str, bitforge_path: Option) -> Element<'_, Me let dialog = container( column![ - text(message).size(14).color(Color::BLACK), - Space::with_height(16), - row(buttons).spacing(8).align_y(Alignment::Center), + text(message) + .size(14) + .color(Color::BLACK) + .width(Length::Fill) + .wrapping(iced::widget::text::Wrapping::WordOrGlyph) + .line_height(iced::widget::text::LineHeight::Relative(1.35)), + Space::new().height(20), + row![ + Space::new().width(Length::Fill), + row(buttons).spacing(8).align_y(Alignment::Center), + ] + .align_y(Alignment::Center), ] .spacing(0) .padding(24) - .width(440), + .width(Length::Fill), ) + .width(520) .style(|_| container::Style { background: Some(Color::WHITE.into()), border: iced::Border { @@ -572,7 +682,7 @@ fn view_overlay(message: &str, bitforge_path: Option) -> Element<'_, Me } fn horizontal_rule<'a>() -> Element<'a, Message> { - container(Space::with_height(1)) + container(Space::new().height(1)) .width(Length::Fill) .style(|_| container::Style { background: Some(BORDER.into()), @@ -581,14 +691,58 @@ fn horizontal_rule<'a>() -> Element<'a, Message> { .into() } -fn indicator_badge(label: &str, active: bool) -> Element<'_, Message> { +fn indicator_badge<'a>(label: &'a str, value: &'a str, active: bool) -> Element<'a, Message> { let dot_color = if active { GREEN } else { OFF }; - row![ - text("●").size(14).color(dot_color), - Space::with_width(6), - text(label).size(11).color(TEXT_SEC), - ] - .align_y(Alignment::Center) + let value_color = if active { Color::BLACK } else { TEXT_SEC }; + container( + row![ + text("●").size(13).color(dot_color), + column![ + text(label).size(9).color(TEXT_TER), + text(value).size(11).color(value_color), + ] + .spacing(1), + ] + .spacing(7) + .align_y(Alignment::Center), + ) + .padding(Padding::from([5, 9])) + .style(|_| container::Style { + background: Some( + Color { + r: 0.965, + g: 0.965, + b: 0.976, + a: 1.0, + } + .into(), + ), + border: iced::Border { + color: BORDER, + width: 1.0, + radius: 8.0.into(), + }, + ..Default::default() + }) + .into() +} + +fn empty_terminal_state(running: bool) -> Element<'static, Message> { + let message = if running { + "Waiting for new output..." + } else { + "No log output yet. Launch the service to start streaming logs." + }; + + container( + text(message) + .size(11) + .color(TERM_DIM) + .width(Length::Fill) + .wrapping(iced::widget::text::Wrapping::WordOrGlyph), + ) + .width(Length::Fill) + .padding(Padding::from([10, 0])) .into() } @@ -608,7 +762,14 @@ fn terminal_line_element(line: &str) -> Element<'_, Message> { Font::MONOSPACE }; - text(line).size(11).font(font).color(style.color).into() + text(line) + .size(11) + .font(font) + .color(style.color) + .width(Length::Fill) + .wrapping(iced::widget::text::Wrapping::WordOrGlyph) + .line_height(iced::widget::text::LineHeight::Relative(1.25)) + .into() } fn terminal_line_style(line: &str) -> TerminalTextStyle { @@ -679,28 +840,37 @@ fn terminal_line_style(line: &str) -> TerminalTextStyle { fn path_row<'a>( label: &'a str, + placeholder: &'a str, value: &'a str, on_change: impl Fn(String) -> Message + 'a, browse_msg: Message, exists: bool, ) -> Element<'a, Message> { + let exists_text = if exists { "Found" } else { "Missing" }; let exists_dot = text("●").size(13).color(if exists { GREEN } else { OFF }); + let status = row![ + exists_dot, + Space::new().width(4), + text(exists_text).size(10).color(TEXT_SEC), + ] + .align_y(Alignment::Center) + .width(76); row![ text(label).size(11).color(TEXT_SEC).width(180), - text_input("", value) + text_input(placeholder, value) .on_input(on_change) - .padding(Padding::from([4, 6])) + .padding(Padding::from([6, 8])) .font(Font::MONOSPACE) .size(11), - Space::with_width(6), - styled_button("Browse…", ButtonStyle::Secondary).on_press(browse_msg), - Space::with_width(6), - exists_dot, + Space::new().width(6), + styled_button("Browse", ButtonStyle::Secondary).on_press(browse_msg), + Space::new().width(6), + status, ] .align_y(Alignment::Center) - .spacing(4) - .padding(Padding::from([3, 0])) + .spacing(6) + .padding(Padding::from([4, 0])) .into() } @@ -736,20 +906,25 @@ fn styled_button(label: &str, style: ButtonStyle) -> button::Button<'_, Message> ButtonStyle::Confirm => (GREEN, darken(GREEN), Color::WHITE), }; - button(text(label).size(11).color(fg)) + button(text(label).size(11)) .padding(Padding::from([5, 14])) - .style(move |_, status| button::Style { - background: Some(match status { - button::Status::Hovered | button::Status::Pressed => hover_bg.into(), - _ => bg.into(), - }), - text_color: fg, - border: iced::Border { - color: Color::TRANSPARENT, - width: 0.0, - radius: 6.0.into(), - }, - shadow: iced::Shadow::default(), + .style(move |_, status| { + let disabled = status == button::Status::Disabled; + button::Style { + background: Some(match status { + button::Status::Disabled => DISABLED_BG.into(), + button::Status::Hovered | button::Status::Pressed => hover_bg.into(), + button::Status::Active => bg.into(), + }), + text_color: if disabled { TEXT_TER } else { fg }, + border: iced::Border { + color: Color::TRANSPARENT, + width: 0.0, + radius: 6.0.into(), + }, + shadow: iced::Shadow::default(), + snap: false, + } }) } @@ -761,3 +936,23 @@ fn darken(c: Color) -> Color { a: c.a, } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn terminal_line_style_highlights_common_states() { + let error = terminal_line_style("error: failed to launch"); + assert_eq!(error.color, MAC_RED); + assert!(error.bold); + + let warning = terminal_line_style("warning: retrying"); + assert_eq!(warning.color, MAC_ORG); + assert!(!warning.bold); + + let success = terminal_line_style("electrs ready"); + assert_eq!(success.color, GREEN); + assert!(!success.bold); + } +} diff --git a/src/updater.rs b/src/updater.rs index 2f9a6eb..8906193 100644 --- a/src/updater.rs +++ b/src/updater.rs @@ -1,8 +1,8 @@ //! Binary update system. //! -//! Scans `~/Downloads/bitcoin_builds/binaries/` for versioned folders, -//! selects the highest semantic version, and copies the relevant binaries -//! into the configured `Binaries/` directory on the SSD. +//! Scans the platform Downloads `bitcoin_builds/binaries/` folder for +//! versioned folders, selects the highest semantic version, and copies the +//! relevant binaries into the configured `Binaries/` directory. //! //! Folder naming convention expected: //! `bitcoin-27.0` → contains bitcoind, bitcoin-cli, bitcoin-tx, bitcoin-util @@ -10,11 +10,12 @@ use std::{ fs, - os::unix::fs::PermissionsExt, path::{Path, PathBuf}, }; -use anyhow::{Context, Result}; +use anyhow::{Context as _, Result}; + +use crate::platform; // ── Version parsing ─────────────────────────────────────────────────────────── @@ -38,7 +39,7 @@ pub fn find_latest_version(search_dir: &Path, prefix: &str) -> Option { for entry in entries.flatten() { let name = entry.file_name().to_string_lossy().into_owned(); // Must be a directory - if !entry.file_type().map(|t| t.is_dir()).unwrap_or(false) { + if !entry.file_type().is_ok_and(|t| t.is_dir()) { continue; } // Must match `-` @@ -63,7 +64,7 @@ pub fn find_latest_version(search_dir: &Path, prefix: &str) -> Option { /// /// Each binary is first written to a `.tmp` file, then atomically renamed, /// so a partial copy never replaces a working binary. -/// File permissions are set to 0o755 (rwxr-xr-x). +/// File permissions are set to 0o755 (rwxr-xr-x) on Unix platforms. /// /// Returns the list of binary names that were actually copied. pub fn copy_binaries(src_dir: &Path, dst_dir: &Path, names: &[&str]) -> Result> { @@ -85,12 +86,7 @@ pub fn copy_binaries(src_dir: &Path, dst_dir: &Path, names: &[&str]) -> Result UpdateResult { - let downloads = home_dir().join("Downloads").join("bitcoin_builds"); + let downloads = platform::downloads_bitcoin_builds_dir(); if !downloads.exists() { - let bitforge = PathBuf::from("/Applications/BitForge.app"); - return if bitforge.exists() { - UpdateResult::BitForgeFound(bitforge) - } else { - UpdateResult::BitForgeNotFound - }; + return platform::bitforge_app_path() + .map_or(UpdateResult::BitForgeNotFound, UpdateResult::BitForgeFound); } let binaries_src = downloads.join("binaries"); @@ -148,11 +140,9 @@ pub fn run_update(binaries_dst: &Path) -> UpdateResult { if let Some(folder) = btc_folder { let src = binaries_src.join(&folder); - match copy_binaries( - &src, - binaries_dst, - &["bitcoind", "bitcoin-cli", "bitcoin-tx", "bitcoin-util"], - ) { + let names = platform::bitcoin_binary_names(); + let names: Vec<&str> = names.iter().map(String::as_str).collect(); + match copy_binaries(&src, binaries_dst, &names) { Ok(copied) if !copied.is_empty() => { messages.push(format!("Bitcoin ({folder}): {}", copied.join(", "))); } @@ -163,7 +153,8 @@ pub fn run_update(binaries_dst: &Path) -> UpdateResult { if let Some(folder) = etr_folder { let src = binaries_src.join(&folder); - match copy_binaries(&src, binaries_dst, &["electrs"]) { + let electrs = platform::electrs_binary_name(); + match copy_binaries(&src, binaries_dst, &[electrs.as_str()]) { Ok(copied) if !copied.is_empty() => { messages.push(format!("Electrs ({folder}): {}", copied.join(", "))); } @@ -179,12 +170,6 @@ pub fn run_update(binaries_dst: &Path) -> UpdateResult { } } -fn home_dir() -> PathBuf { - std::env::var_os("HOME") - .map(PathBuf::from) - .unwrap_or_else(|| std::env::current_dir().unwrap_or_else(|_| PathBuf::from("."))) -} - // ── Tests ───────────────────────────────────────────────────────────────────── #[cfg(test)] @@ -200,13 +185,14 @@ mod tests { } #[test] - fn latest_version_selection() { - let tmp = tempfile::tempdir().unwrap(); + fn latest_version_selection() -> Result<()> { + let tmp = tempfile::tempdir()?; let dir = tmp.path(); - std::fs::create_dir(dir.join("bitcoin-26.0")).unwrap(); - std::fs::create_dir(dir.join("bitcoin-27.1")).unwrap(); - std::fs::create_dir(dir.join("bitcoin-27.0")).unwrap(); + std::fs::create_dir(dir.join("bitcoin-26.0"))?; + std::fs::create_dir(dir.join("bitcoin-27.1"))?; + std::fs::create_dir(dir.join("bitcoin-27.0"))?; let latest = find_latest_version(dir, "bitcoin"); assert_eq!(latest.as_deref(), Some("bitcoin-27.1")); + Ok(()) } }