diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 31d9d69..fb3c5e4 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -11,6 +11,7 @@ permissions:
jobs:
build:
strategy:
+ fail-fast: false
matrix:
include:
- target: x86_64-unknown-linux-gnu
@@ -38,7 +39,40 @@ jobs:
with:
targets: ${{ matrix.target }}
- - name: Install MinGW toolchain
+ # eframe needs a few system libs on Linux for window management, keyboard,
+ # and OpenGL/X11/Wayland. We install them on the Ubuntu runners regardless
+ # of arch so both CLI-only and UI builds succeed.
+ - name: Install Linux eframe system deps
+ if: runner.os == 'Linux'
+ run: |
+ sudo apt-get update
+ sudo apt-get install -y \
+ libxkbcommon-dev \
+ libwayland-dev \
+ libxcb1-dev libxcb-render0-dev libxcb-shape0-dev libxcb-xfixes0-dev \
+ libx11-dev \
+ libgl1-mesa-dev libglib2.0-dev libgtk-3-dev
+
+ - name: Install aarch64 cross-compile toolchain (Linux only)
+ if: matrix.target == 'aarch64-unknown-linux-gnu'
+ run: |
+ sudo dpkg --add-architecture arm64
+ sudo sed -i 's#^deb http#deb [arch=amd64] http#' /etc/apt/sources.list || true
+ echo 'deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy main restricted universe multiverse' | sudo tee /etc/apt/sources.list.d/arm64.list
+ echo 'deb [arch=arm64] http://ports.ubuntu.com/ubuntu-ports jammy-updates main restricted universe multiverse' | sudo tee -a /etc/apt/sources.list.d/arm64.list
+ sudo apt-get update
+ sudo apt-get install -y gcc-aarch64-linux-gnu \
+ libxkbcommon-dev:arm64 \
+ libwayland-dev:arm64 \
+ libxcb1-dev:arm64 libxcb-render0-dev:arm64 libxcb-shape0-dev:arm64 libxcb-xfixes0-dev:arm64 \
+ libx11-dev:arm64 \
+ libgl1-mesa-dev:arm64 libglib2.0-dev:arm64 libgtk-3-dev:arm64 || true
+ echo '[target.aarch64-unknown-linux-gnu]' >> ~/.cargo/config.toml
+ echo 'linker = "aarch64-linux-gnu-gcc"' >> ~/.cargo/config.toml
+ echo "PKG_CONFIG_PATH=/usr/lib/aarch64-linux-gnu/pkgconfig:/usr/share/pkgconfig" >> $GITHUB_ENV
+ echo "PKG_CONFIG_ALLOW_CROSS=1" >> $GITHUB_ENV
+
+ - name: Install Windows MinGW toolchain
if: matrix.target == 'x86_64-pc-windows-gnu'
id: msys2
uses: msys2/setup-msys2@v2
@@ -47,15 +81,7 @@ jobs:
update: true
install: mingw-w64-x86_64-gcc
- - name: Install cross-compilation tools
- if: matrix.target == 'aarch64-unknown-linux-gnu'
- run: |
- sudo apt-get update
- sudo apt-get install -y gcc-aarch64-linux-gnu
- echo '[target.aarch64-unknown-linux-gnu]' >> ~/.cargo/config.toml
- echo 'linker = "aarch64-linux-gnu-gcc"' >> ~/.cargo/config.toml
-
- - name: Configure GNU linker
+ - name: Configure Windows GNU linker
if: matrix.target == 'x86_64-pc-windows-gnu'
shell: pwsh
run: |
@@ -64,27 +90,69 @@ jobs:
Add-Content -Path $env:USERPROFILE/.cargo/config.toml -Value '[target.x86_64-pc-windows-gnu]'
Add-Content -Path $env:USERPROFILE/.cargo/config.toml -Value "linker = '$gcc'"
- - name: Build
- run: cargo build --release --target ${{ matrix.target }}
+ - name: Build CLI
+ run: cargo build --release --target ${{ matrix.target }} --bin mhrv-rs
+
+ # UI build: we try to build the UI binary on every platform. If it fails
+ # on cross-compile for linux-arm64 (missing arm64 system libs cross),
+ # we still ship the CLI.
+ - name: Build UI
+ if: matrix.target != 'aarch64-unknown-linux-gnu'
+ run: cargo build --release --target ${{ matrix.target }} --features ui --bin mhrv-rs-ui
+
+ - name: Try UI on aarch64-linux (best effort)
+ if: matrix.target == 'aarch64-unknown-linux-gnu'
+ continue-on-error: true
+ run: cargo build --release --target ${{ matrix.target }} --features ui --bin mhrv-rs-ui
- name: Package (unix)
if: runner.os != 'Windows'
run: |
mkdir -p dist
- cp target/${{ matrix.target }}/release/mhrv-rs dist/${{ matrix.name }}
- chmod +x dist/${{ matrix.name }}
+ cp target/${{ matrix.target }}/release/mhrv-rs dist/mhrv-rs
+ chmod +x dist/mhrv-rs
+ if [ -f target/${{ matrix.target }}/release/mhrv-rs-ui ]; then
+ cp target/${{ matrix.target }}/release/mhrv-rs-ui dist/mhrv-rs-ui
+ chmod +x dist/mhrv-rs-ui
+ fi
+
+ - name: Build macOS .app bundle
+ if: runner.os == 'macOS'
+ run: |
+ VER="${GITHUB_REF#refs/tags/v}"
+ ./assets/macos/build-app.sh dist/mhrv-rs-ui "$VER" dist
+ # Make a clean zip of just the .app for the release
+ cd dist
+ zip -qry "${{ matrix.name }}-app.zip" mhrv-rs.app
- name: Package (windows)
if: runner.os == 'Windows'
shell: pwsh
run: |
- New-Item -ItemType Directory -Force -Path dist
- Copy-Item target/${{ matrix.target }}/release/mhrv-rs.exe dist/${{ matrix.name }}.exe
+ New-Item -ItemType Directory -Force -Path dist | Out-Null
+ Copy-Item target/${{ matrix.target }}/release/mhrv-rs.exe dist/mhrv-rs.exe
+ if (Test-Path target/${{ matrix.target }}/release/mhrv-rs-ui.exe) {
+ Copy-Item target/${{ matrix.target }}/release/mhrv-rs-ui.exe dist/mhrv-rs-ui.exe
+ }
+
+ - name: Make archive
+ shell: bash
+ run: |
+ cd dist
+ if [ "${{ runner.os }}" = "Windows" ]; then
+ 7z a -tzip "${{ matrix.name }}.zip" mhrv-rs.exe mhrv-rs-ui.exe
+ else
+ tar czf "${{ matrix.name }}.tar.gz" mhrv-rs mhrv-rs-ui 2>/dev/null || tar czf "${{ matrix.name }}.tar.gz" mhrv-rs
+ fi
- uses: actions/upload-artifact@v4
with:
name: ${{ matrix.name }}
- path: dist/${{ matrix.name }}${{ runner.os == 'Windows' && '.exe' || '' }}
+ path: |
+ dist/${{ matrix.name }}.tar.gz
+ dist/${{ matrix.name }}.zip
+ dist/${{ matrix.name }}-app.zip
+ if-no-files-found: ignore
release:
needs: build
diff --git a/Cargo.lock b/Cargo.lock
index 291a476..4a93182 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2,12 +2,52 @@
# It is not intended for manual editing.
version = 4
+[[package]]
+name = "ab_glyph"
+version = "0.2.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2"
+dependencies = [
+ "ab_glyph_rasterizer",
+ "owned_ttf_parser",
+]
+
+[[package]]
+name = "ab_glyph_rasterizer"
+version = "0.1.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618"
+
+[[package]]
+name = "accesskit"
+version = "0.12.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74a4b14f3d99c1255dcba8f45621ab1a2e7540a0009652d33989005a4d0bfc6b"
+dependencies = [
+ "enumn",
+ "serde",
+]
+
[[package]]
name = "adler2"
version = "2.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa"
+[[package]]
+name = "ahash"
+version = "0.8.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
+dependencies = [
+ "cfg-if",
+ "getrandom 0.3.4",
+ "once_cell",
+ "serde",
+ "version_check",
+ "zerocopy",
+]
+
[[package]]
name = "aho-corasick"
version = "1.1.4"
@@ -17,6 +57,56 @@ dependencies = [
"memchr",
]
+[[package]]
+name = "android-activity"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ee91c0c2905bae44f84bfa4e044536541df26b7703fd0888deeb9060fcc44289"
+dependencies = [
+ "android-properties",
+ "bitflags 2.11.1",
+ "cc",
+ "cesu8",
+ "jni 0.21.1",
+ "jni-sys 0.3.1",
+ "libc",
+ "log",
+ "ndk",
+ "ndk-context",
+ "ndk-sys",
+ "num_enum",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "android-properties"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc7eb209b1518d6bb87b283c20095f5228ecda460da70b44f0802523dea6da04"
+
+[[package]]
+name = "arboard"
+version = "3.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf"
+dependencies = [
+ "clipboard-win",
+ "log",
+ "objc2 0.6.4",
+ "objc2-app-kit 0.3.2",
+ "objc2-foundation 0.3.2",
+ "parking_lot",
+ "percent-encoding",
+ "windows-sys 0.52.0",
+ "x11rb",
+]
+
+[[package]]
+name = "as-raw-xcb-connection"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b"
+
[[package]]
name = "asn1-rs"
version = "0.6.2"
@@ -90,18 +180,150 @@ dependencies = [
"fs_extra",
]
+[[package]]
+name = "base64"
+version = "0.21.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567"
+
[[package]]
name = "base64"
version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+[[package]]
+name = "bitflags"
+version = "1.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
+
+[[package]]
+name = "bitflags"
+version = "2.11.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "block-sys"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7"
+dependencies = [
+ "objc-sys",
+]
+
+[[package]]
+name = "block2"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15b55663a85f33501257357e6421bb33e769d5c9ffb5ba0921c975a123e35e68"
+dependencies = [
+ "block-sys",
+ "objc2 0.4.1",
+]
+
+[[package]]
+name = "block2"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f"
+dependencies = [
+ "objc2 0.5.2",
+]
+
+[[package]]
+name = "bumpalo"
+version = "3.20.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb"
+
+[[package]]
+name = "bytemuck"
+version = "1.25.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec"
+dependencies = [
+ "bytemuck_derive",
+]
+
+[[package]]
+name = "bytemuck_derive"
+version = "1.10.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "byteorder-lite"
+version = "0.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495"
+
[[package]]
name = "bytes"
version = "1.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33"
+[[package]]
+name = "calloop"
+version = "0.12.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fba7adb4dd5aa98e5553510223000e7148f621165ec5f9acd7113f6ca4995298"
+dependencies = [
+ "bitflags 2.11.1",
+ "log",
+ "polling",
+ "rustix 0.38.44",
+ "slab",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "calloop"
+version = "0.14.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7"
+dependencies = [
+ "bitflags 2.11.1",
+ "polling",
+ "rustix 1.1.4",
+ "slab",
+ "tracing",
+]
+
+[[package]]
+name = "calloop-wayland-source"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f0ea9b9476c7fad82841a8dbb380e2eae480c21910feba80725b46931ed8f02"
+dependencies = [
+ "calloop 0.12.4",
+ "rustix 0.38.44",
+ "wayland-backend",
+ "wayland-client",
+]
+
+[[package]]
+name = "calloop-wayland-source"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa"
+dependencies = [
+ "calloop 0.14.4",
+ "rustix 1.1.4",
+ "wayland-backend",
+ "wayland-client",
+]
+
[[package]]
name = "cc"
version = "1.2.60"
@@ -114,12 +336,42 @@ 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 = "cgl"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff"
+dependencies = [
+ "libc",
+]
+
+[[package]]
+name = "clipboard-win"
+version = "5.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4"
+dependencies = [
+ "error-code",
+]
+
[[package]]
name = "cmake"
version = "0.1.58"
@@ -129,6 +381,75 @@ dependencies = [
"cc",
]
+[[package]]
+name = "combine"
+version = "4.6.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd"
+dependencies = [
+ "bytes",
+ "memchr",
+]
+
+[[package]]
+name = "concurrent-queue"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973"
+dependencies = [
+ "crossbeam-utils",
+]
+
+[[package]]
+name = "core-foundation"
+version = "0.9.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f"
+dependencies = [
+ "core-foundation-sys",
+ "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"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b"
+
+[[package]]
+name = "core-graphics"
+version = "0.23.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation 0.9.4",
+ "core-graphics-types",
+ "foreign-types",
+ "libc",
+]
+
+[[package]]
+name = "core-graphics-types"
+version = "0.1.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation 0.9.4",
+ "libc",
+]
+
[[package]]
name = "crc32fast"
version = "1.5.0"
@@ -138,6 +459,18 @@ dependencies = [
"cfg-if",
]
+[[package]]
+name = "crossbeam-utils"
+version = "0.8.21"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
+
+[[package]]
+name = "cursor-icon"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f"
+
[[package]]
name = "data-encoding"
version = "2.10.0"
@@ -167,6 +500,43 @@ dependencies = [
"powerfmt",
]
+[[package]]
+name = "directories"
+version = "5.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9a49173b84e034382284f27f1af4dcbbd231ffa358c0fe316541a7337f376a35"
+dependencies = [
+ "dirs-sys",
+]
+
+[[package]]
+name = "dirs-sys"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c"
+dependencies = [
+ "libc",
+ "option-ext",
+ "redox_users",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "dispatch"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b"
+
+[[package]]
+name = "dispatch2"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38"
+dependencies = [
+ "bitflags 2.11.1",
+ "objc2 0.6.4",
+]
+
[[package]]
name = "displaydoc"
version = "0.2.5"
@@ -178,12 +548,172 @@ dependencies = [
"syn",
]
+[[package]]
+name = "dlib"
+version = "0.5.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a"
+dependencies = [
+ "libloading",
+]
+
+[[package]]
+name = "document-features"
+version = "0.2.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61"
+dependencies = [
+ "litrs",
+]
+
+[[package]]
+name = "downcast-rs"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2"
+
[[package]]
name = "dunce"
version = "1.0.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813"
+[[package]]
+name = "ecolor"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2e6b451ff1143f6de0f33fc7f1b68fecfd2c7de06e104de96c4514de3f5396f8"
+dependencies = [
+ "bytemuck",
+ "emath",
+ "serde",
+]
+
+[[package]]
+name = "eframe"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6490ef800b2e41ee129b1f32f9ac15f713233fe3bc18e241a1afe1e4fb6811e0"
+dependencies = [
+ "ahash",
+ "bytemuck",
+ "directories",
+ "document-features",
+ "egui",
+ "egui-winit",
+ "egui_glow",
+ "glow",
+ "glutin",
+ "glutin-winit",
+ "image",
+ "js-sys",
+ "log",
+ "objc2 0.5.2",
+ "objc2-app-kit 0.2.2",
+ "objc2-foundation 0.2.2",
+ "parking_lot",
+ "percent-encoding",
+ "raw-window-handle 0.5.2",
+ "raw-window-handle 0.6.2",
+ "ron",
+ "serde",
+ "static_assertions",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "web-sys",
+ "web-time",
+ "winapi",
+ "winit",
+]
+
+[[package]]
+name = "egui"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20c97e70a2768de630f161bb5392cbd3874fcf72868f14df0e002e82e06cb798"
+dependencies = [
+ "accesskit",
+ "ahash",
+ "emath",
+ "epaint",
+ "log",
+ "nohash-hasher",
+ "ron",
+ "serde",
+]
+
+[[package]]
+name = "egui-winit"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fac4e066af341bf92559f60dbdf2020b2a03c963415349af5f3f8d79ff7a4926"
+dependencies = [
+ "ahash",
+ "arboard",
+ "egui",
+ "log",
+ "raw-window-handle 0.6.2",
+ "serde",
+ "smithay-clipboard",
+ "web-time",
+ "webbrowser",
+ "winit",
+]
+
+[[package]]
+name = "egui_glow"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4e2bdc8b38cfa17cc712c4ae079e30c71c00cd4c2763c9e16dc7860a02769103"
+dependencies = [
+ "ahash",
+ "bytemuck",
+ "egui",
+ "glow",
+ "log",
+ "memoffset",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "emath"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0a6a21708405ea88f63d8309650b4d77431f4bc28fb9d8e6f77d3963b51249e6"
+dependencies = [
+ "bytemuck",
+ "serde",
+]
+
+[[package]]
+name = "enumn"
+version = "0.1.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "epaint"
+version = "0.28.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3f0dcc0a0771e7500e94cd1cb797bd13c9f23b9409bdc3c824e2cbc562b7fa01"
+dependencies = [
+ "ab_glyph",
+ "ahash",
+ "bytemuck",
+ "ecolor",
+ "emath",
+ "log",
+ "nohash-hasher",
+ "parking_lot",
+ "serde",
+]
+
[[package]]
name = "equivalent"
version = "1.0.2"
@@ -200,6 +730,21 @@ dependencies = [
"windows-sys 0.61.2",
]
+[[package]]
+name = "error-code"
+version = "3.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59"
+
+[[package]]
+name = "fdeflate"
+version = "0.3.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c"
+dependencies = [
+ "simd-adler32",
+]
+
[[package]]
name = "find-msvc-tools"
version = "0.1.9"
@@ -222,6 +767,42 @@ version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
+[[package]]
+name = "foreign-types"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965"
+dependencies = [
+ "foreign-types-macros",
+ "foreign-types-shared",
+]
+
+[[package]]
+name = "foreign-types-macros"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "foreign-types-shared"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b"
+
+[[package]]
+name = "form_urlencoded"
+version = "1.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
+dependencies = [
+ "percent-encoding",
+]
+
[[package]]
name = "fs_extra"
version = "1.3.0"
@@ -240,6 +821,34 @@ version = "0.3.32"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893"
+[[package]]
+name = "futures-task"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393"
+
+[[package]]
+name = "futures-util"
+version = "0.3.32"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6"
+dependencies = [
+ "futures-core",
+ "futures-task",
+ "pin-project-lite",
+ "slab",
+]
+
+[[package]]
+name = "gethostname"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8"
+dependencies = [
+ "rustix 1.1.4",
+ "windows-link",
+]
+
[[package]]
name = "getrandom"
version = "0.2.17"
@@ -263,6 +872,94 @@ dependencies = [
"wasip2",
]
+[[package]]
+name = "gl_generator"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a95dfc23a2b4a9a2f5ab41d194f8bfda3cabec42af4e39f08c339eb2a0c124d"
+dependencies = [
+ "khronos_api",
+ "log",
+ "xml-rs",
+]
+
+[[package]]
+name = "glow"
+version = "0.13.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bd348e04c43b32574f2de31c8bb397d96c9fcfa1371bd4ca6d8bdc464ab121b1"
+dependencies = [
+ "js-sys",
+ "slotmap",
+ "wasm-bindgen",
+ "web-sys",
+]
+
+[[package]]
+name = "glutin"
+version = "0.31.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "18fcd4ae4e86d991ad1300b8f57166e5be0c95ef1f63f3f5b827f8a164548746"
+dependencies = [
+ "bitflags 2.11.1",
+ "cfg_aliases",
+ "cgl",
+ "core-foundation 0.9.4",
+ "dispatch",
+ "glutin_egl_sys",
+ "glutin_glx_sys",
+ "glutin_wgl_sys",
+ "icrate",
+ "libloading",
+ "objc2 0.4.1",
+ "once_cell",
+ "raw-window-handle 0.5.2",
+ "wayland-sys",
+ "windows-sys 0.48.0",
+ "x11-dl",
+]
+
+[[package]]
+name = "glutin-winit"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ebcdfba24f73b8412c5181e56f092b5eff16671c514ce896b258a0a64bd7735"
+dependencies = [
+ "cfg_aliases",
+ "glutin",
+ "raw-window-handle 0.5.2",
+ "winit",
+]
+
+[[package]]
+name = "glutin_egl_sys"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "77cc5623f5309ef433c3dd4ca1223195347fe62c413da8e2fdd0eb76db2d9bcd"
+dependencies = [
+ "gl_generator",
+ "windows-sys 0.48.0",
+]
+
+[[package]]
+name = "glutin_glx_sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a165fd686c10dcc2d45380b35796e577eacfd43d4660ee741ec8ebe2201b3b4f"
+dependencies = [
+ "gl_generator",
+ "x11-dl",
+]
+
+[[package]]
+name = "glutin_wgl_sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6c8098adac955faa2d31079b65dc48841251f69efd3ac25477903fc424362ead"
+dependencies = [
+ "gl_generator",
+]
+
[[package]]
name = "h2"
version = "0.4.13"
@@ -288,6 +985,12 @@ version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51"
+[[package]]
+name = "hermit-abi"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c"
+
[[package]]
name = "http"
version = "1.4.0"
@@ -304,6 +1007,133 @@ version = "1.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87"
+[[package]]
+name = "icrate"
+version = "0.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "99d3aaff8a54577104bafdf686ff18565c3b6903ca5782a2026ef06e2c7aa319"
+dependencies = [
+ "block2 0.3.0",
+ "dispatch",
+ "objc2 0.4.1",
+]
+
+[[package]]
+name = "icu_collections"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2984d1cd16c883d7935b9e07e44071dca8d917fd52ecc02c04d5fa0b5a3f191c"
+dependencies = [
+ "displaydoc",
+ "potential_utf",
+ "utf8_iter",
+ "yoke",
+ "zerofrom",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_locale_core"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29"
+dependencies = [
+ "displaydoc",
+ "litemap",
+ "tinystr",
+ "writeable",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c56e5ee99d6e3d33bd91c5d85458b6005a22140021cc324cea84dd0e72cff3b4"
+dependencies = [
+ "icu_collections",
+ "icu_normalizer_data",
+ "icu_properties",
+ "icu_provider",
+ "smallvec",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_normalizer_data"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "da3be0ae77ea334f4da67c12f149704f19f81d1adf7c51cf482943e84a2bad38"
+
+[[package]]
+name = "icu_properties"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bee3b67d0ea5c2cca5003417989af8996f8604e34fb9ddf96208a033901e70de"
+dependencies = [
+ "icu_collections",
+ "icu_locale_core",
+ "icu_properties_data",
+ "icu_provider",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "icu_properties_data"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8e2bbb201e0c04f7b4b3e14382af113e17ba4f63e2c9d2ee626b720cbce54a14"
+
+[[package]]
+name = "icu_provider"
+version = "2.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421"
+dependencies = [
+ "displaydoc",
+ "icu_locale_core",
+ "writeable",
+ "yoke",
+ "zerofrom",
+ "zerotrie",
+ "zerovec",
+]
+
+[[package]]
+name = "idna"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
+dependencies = [
+ "idna_adapter",
+ "smallvec",
+ "utf8_iter",
+]
+
+[[package]]
+name = "idna_adapter"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344"
+dependencies = [
+ "icu_normalizer",
+ "icu_properties",
+]
+
+[[package]]
+name = "image"
+version = "0.25.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104"
+dependencies = [
+ "bytemuck",
+ "byteorder-lite",
+ "moxcms",
+ "num-traits",
+ "png",
+]
+
[[package]]
name = "indexmap"
version = "2.14.0"
@@ -320,6 +1150,80 @@ version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682"
+[[package]]
+name = "jni"
+version = "0.21.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97"
+dependencies = [
+ "cesu8",
+ "cfg-if",
+ "combine",
+ "jni-sys 0.3.1",
+ "log",
+ "thiserror 1.0.69",
+ "walkdir",
+ "windows-sys 0.45.0",
+]
+
+[[package]]
+name = "jni"
+version = "0.22.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498"
+dependencies = [
+ "cfg-if",
+ "combine",
+ "jni-macros",
+ "jni-sys 0.4.1",
+ "log",
+ "simd_cesu8",
+ "thiserror 2.0.18",
+ "walkdir",
+ "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.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 = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264"
+dependencies = [
+ "quote",
+ "syn",
+]
+
[[package]]
name = "jobserver"
version = "0.1.34"
@@ -330,6 +1234,24 @@ dependencies = [
"libc",
]
+[[package]]
+name = "js-sys"
+version = "0.3.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2964e92d1d9dc3364cae4d718d93f227e3abb088e747d92e0395bfdedf1c12ca"
+dependencies = [
+ "cfg-if",
+ "futures-util",
+ "once_cell",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "khronos_api"
+version = "3.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc"
+
[[package]]
name = "lazy_static"
version = "1.5.0"
@@ -342,6 +1264,61 @@ version = "0.2.185"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f"
+[[package]]
+name = "libloading"
+version = "0.8.9"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55"
+dependencies = [
+ "cfg-if",
+ "windows-link",
+]
+
+[[package]]
+name = "libredox"
+version = "0.1.16"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c"
+dependencies = [
+ "bitflags 2.11.1",
+ "libc",
+ "plain",
+ "redox_syscall 0.7.4",
+]
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.4.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
+
+[[package]]
+name = "linux-raw-sys"
+version = "0.12.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53"
+
+[[package]]
+name = "litemap"
+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 = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092"
+
+[[package]]
+name = "lock_api"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965"
+dependencies = [
+ "scopeguard",
+]
+
[[package]]
name = "log"
version = "0.4.29"
@@ -364,11 +1341,31 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79"
[[package]]
-name = "mhrv-rs"
-version = "0.3.0"
+name = "memmap2"
+version = "0.9.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3"
dependencies = [
- "base64",
+ "libc",
+]
+
+[[package]]
+name = "memoffset"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a"
+dependencies = [
+ "autocfg",
+]
+
+[[package]]
+name = "mhrv-rs"
+version = "0.4.0"
+dependencies = [
+ "base64 0.22.1",
"bytes",
+ "directories",
+ "eframe",
"flate2",
"h2",
"http",
@@ -416,6 +1413,53 @@ dependencies = [
"windows-sys 0.61.2",
]
+[[package]]
+name = "moxcms"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb85c154ba489f01b25c0d36ae69a87e4a1c73a72631fc6c0eb6dde34a73e44b"
+dependencies = [
+ "num-traits",
+ "pxfm",
+]
+
+[[package]]
+name = "ndk"
+version = "0.8.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2076a31b7010b17a38c01907c45b945e8f11495ee4dd588309718901b1f7a5b7"
+dependencies = [
+ "bitflags 2.11.1",
+ "jni-sys 0.3.1",
+ "log",
+ "ndk-sys",
+ "num_enum",
+ "raw-window-handle 0.5.2",
+ "raw-window-handle 0.6.2",
+ "thiserror 1.0.69",
+]
+
+[[package]]
+name = "ndk-context"
+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 0.3.1",
+]
+
+[[package]]
+name = "nohash-hasher"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451"
+
[[package]]
name = "nom"
version = "7.1.3"
@@ -469,6 +1513,210 @@ dependencies = [
"autocfg",
]
+[[package]]
+name = "num_enum"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d0bca838442ec211fa11de3a8b0e0e8f3a4522575b5c4c06ed722e005036f26"
+dependencies = [
+ "num_enum_derive",
+ "rustversion",
+]
+
+[[package]]
+name = "num_enum_derive"
+version = "0.7.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "680998035259dcfcafe653688bf2aa6d3e2dc05e98be6ab46afb089dc84f1df8"
+dependencies = [
+ "proc-macro-crate",
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
+[[package]]
+name = "objc-sys"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310"
+
+[[package]]
+name = "objc2"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "559c5a40fdd30eb5e344fbceacf7595a81e242529fb4e21cf5f43fb4f11ff98d"
+dependencies = [
+ "objc-sys",
+ "objc2-encode 3.0.0",
+]
+
+[[package]]
+name = "objc2"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804"
+dependencies = [
+ "objc-sys",
+ "objc2-encode 4.1.0",
+]
+
+[[package]]
+name = "objc2"
+version = "0.6.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f"
+dependencies = [
+ "objc2-encode 4.1.0",
+]
+
+[[package]]
+name = "objc2-app-kit"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff"
+dependencies = [
+ "bitflags 2.11.1",
+ "block2 0.5.1",
+ "libc",
+ "objc2 0.5.2",
+ "objc2-core-data",
+ "objc2-core-image",
+ "objc2-foundation 0.2.2",
+ "objc2-quartz-core",
+]
+
+[[package]]
+name = "objc2-app-kit"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c"
+dependencies = [
+ "bitflags 2.11.1",
+ "objc2 0.6.4",
+ "objc2-core-graphics",
+ "objc2-foundation 0.3.2",
+]
+
+[[package]]
+name = "objc2-core-data"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef"
+dependencies = [
+ "bitflags 2.11.1",
+ "block2 0.5.1",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.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.1",
+ "dispatch2",
+ "objc2 0.6.4",
+]
+
+[[package]]
+name = "objc2-core-graphics"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807"
+dependencies = [
+ "bitflags 2.11.1",
+ "dispatch2",
+ "objc2 0.6.4",
+ "objc2-core-foundation",
+ "objc2-io-surface",
+]
+
+[[package]]
+name = "objc2-core-image"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80"
+dependencies = [
+ "block2 0.5.1",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+ "objc2-metal",
+]
+
+[[package]]
+name = "objc2-encode"
+version = "3.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d079845b37af429bfe5dfa76e6d087d788031045b25cfc6fd898486fd9847666"
+
+[[package]]
+name = "objc2-encode"
+version = "4.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33"
+
+[[package]]
+name = "objc2-foundation"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8"
+dependencies = [
+ "bitflags 2.11.1",
+ "block2 0.5.1",
+ "libc",
+ "objc2 0.5.2",
+]
+
+[[package]]
+name = "objc2-foundation"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272"
+dependencies = [
+ "bitflags 2.11.1",
+ "objc2 0.6.4",
+ "objc2-core-foundation",
+]
+
+[[package]]
+name = "objc2-io-surface"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d"
+dependencies = [
+ "bitflags 2.11.1",
+ "objc2 0.6.4",
+ "objc2-core-foundation",
+]
+
+[[package]]
+name = "objc2-metal"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6"
+dependencies = [
+ "bitflags 2.11.1",
+ "block2 0.5.1",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+]
+
+[[package]]
+name = "objc2-quartz-core"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a"
+dependencies = [
+ "bitflags 2.11.1",
+ "block2 0.5.1",
+ "objc2 0.5.2",
+ "objc2-foundation 0.2.2",
+ "objc2-metal",
+]
+
[[package]]
name = "oid-registry"
version = "0.7.1"
@@ -484,22 +1732,124 @@ version = "1.21.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9f7c3e4beb33f85d45ae3e3a1792185706c8e16d043238c593331cc7cd313b50"
+[[package]]
+name = "option-ext"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d"
+
+[[package]]
+name = "orbclient"
+version = "0.3.53"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "12c6933ddbbd16539a7672e697bb8d41ac3a4e99ac43eeb40c07236bd7fcb2dd"
+dependencies = [
+ "libc",
+ "libredox",
+]
+
+[[package]]
+name = "owned_ttf_parser"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "36820e9051aca1014ddc75770aab4d68bc1e9e632f0f5627c4086bc216fb583b"
+dependencies = [
+ "ttf-parser",
+]
+
+[[package]]
+name = "parking_lot"
+version = "0.12.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a"
+dependencies = [
+ "lock_api",
+ "parking_lot_core",
+]
+
+[[package]]
+name = "parking_lot_core"
+version = "0.9.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1"
+dependencies = [
+ "cfg-if",
+ "libc",
+ "redox_syscall 0.5.18",
+ "smallvec",
+ "windows-link",
+]
+
[[package]]
name = "pem"
version = "3.0.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be"
dependencies = [
- "base64",
+ "base64 0.22.1",
"serde_core",
]
+[[package]]
+name = "percent-encoding"
+version = "2.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
+
[[package]]
name = "pin-project-lite"
version = "0.2.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
+[[package]]
+name = "pkg-config"
+version = "0.3.33"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e"
+
+[[package]]
+name = "plain"
+version = "0.2.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6"
+
+[[package]]
+name = "png"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61"
+dependencies = [
+ "bitflags 2.11.1",
+ "crc32fast",
+ "fdeflate",
+ "flate2",
+ "miniz_oxide",
+]
+
+[[package]]
+name = "polling"
+version = "3.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218"
+dependencies = [
+ "cfg-if",
+ "concurrent-queue",
+ "hermit-abi",
+ "pin-project-lite",
+ "rustix 1.1.4",
+ "windows-sys 0.61.2",
+]
+
+[[package]]
+name = "potential_utf"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564"
+dependencies = [
+ "zerovec",
+]
+
[[package]]
name = "powerfmt"
version = "0.2.0"
@@ -515,6 +1865,15 @@ dependencies = [
"zerocopy",
]
+[[package]]
+name = "proc-macro-crate"
+version = "3.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f"
+dependencies = [
+ "toml_edit",
+]
+
[[package]]
name = "proc-macro2"
version = "1.0.106"
@@ -524,6 +1883,21 @@ dependencies = [
"unicode-ident",
]
+[[package]]
+name = "pxfm"
+version = "0.1.29"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f"
+
+[[package]]
+name = "quick-xml"
+version = "0.39.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "958f21e8e7ceb5a1aa7fa87fab28e7c75976e0bfe7e23ff069e0a260f894067d"
+dependencies = [
+ "memchr",
+]
+
[[package]]
name = "quote"
version = "1.0.45"
@@ -569,6 +1943,18 @@ dependencies = [
"getrandom 0.2.17",
]
+[[package]]
+name = "raw-window-handle"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f2ff9a1f06a88b01621b7ae906ef0211290d1c8a168a15542486a8f61c0833b9"
+
+[[package]]
+name = "raw-window-handle"
+version = "0.6.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539"
+
[[package]]
name = "rcgen"
version = "0.13.2"
@@ -583,6 +1969,44 @@ dependencies = [
"yasna",
]
+[[package]]
+name = "redox_syscall"
+version = "0.3.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "567664f262709473930a4bf9e51bf2ebf3348f2e748ccc50dea20646858f8f29"
+dependencies = [
+ "bitflags 1.3.2",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.5.18"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d"
+dependencies = [
+ "bitflags 2.11.1",
+]
+
+[[package]]
+name = "redox_syscall"
+version = "0.7.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f450ad9c3b1da563fb6948a8e0fb0fb9269711c9c73d9ea1de5058c79c8d643a"
+dependencies = [
+ "bitflags 2.11.1",
+]
+
+[[package]]
+name = "redox_users"
+version = "0.4.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43"
+dependencies = [
+ "getrandom 0.2.17",
+ "libredox",
+ "thiserror 1.0.69",
+]
+
[[package]]
name = "regex-automata"
version = "0.4.14"
@@ -614,6 +2038,27 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "ron"
+version = "0.8.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b91f7eff05f748767f183df4320a63d6936e9c6107d97c9e6bdd9784f4289c94"
+dependencies = [
+ "base64 0.21.7",
+ "bitflags 2.11.1",
+ "serde",
+ "serde_derive",
+]
+
+[[package]]
+name = "rustc_version"
+version = "0.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
+dependencies = [
+ "semver",
+]
+
[[package]]
name = "rusticata-macros"
version = "4.1.0"
@@ -623,6 +2068,32 @@ dependencies = [
"nom",
]
+[[package]]
+name = "rustix"
+version = "0.38.44"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
+dependencies = [
+ "bitflags 2.11.1",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.4.15",
+ "windows-sys 0.52.0",
+]
+
+[[package]]
+name = "rustix"
+version = "1.1.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190"
+dependencies = [
+ "bitflags 2.11.1",
+ "errno",
+ "libc",
+ "linux-raw-sys 0.12.1",
+ "windows-sys 0.61.2",
+]
+
[[package]]
name = "rustls"
version = "0.23.38"
@@ -669,6 +2140,39 @@ dependencies = [
"untrusted",
]
+[[package]]
+name = "rustversion"
+version = "1.0.22"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
+
+[[package]]
+name = "same-file"
+version = "1.0.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502"
+dependencies = [
+ "winapi-util",
+]
+
+[[package]]
+name = "scoped-tls"
+version = "1.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
+
+[[package]]
+name = "scopeguard"
+version = "1.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
+
+[[package]]
+name = "semver"
+version = "1.0.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd"
+
[[package]]
name = "serde"
version = "1.0.228"
@@ -743,18 +2247,115 @@ version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214"
+[[package]]
+name = "simd_cesu8"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33"
+dependencies = [
+ "rustc_version",
+ "simdutf8",
+]
+
+[[package]]
+name = "simdutf8"
+version = "0.1.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
+
[[package]]
name = "slab"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5"
+[[package]]
+name = "slotmap"
+version = "1.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bdd58c3c93c3d278ca835519292445cb4b0d4dc59ccfdf7ceadaab3f8aeb4038"
+dependencies = [
+ "version_check",
+]
+
[[package]]
name = "smallvec"
version = "1.15.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+[[package]]
+name = "smithay-client-toolkit"
+version = "0.18.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "922fd3eeab3bd820d76537ce8f582b1cf951eceb5475c28500c7457d9d17f53a"
+dependencies = [
+ "bitflags 2.11.1",
+ "calloop 0.12.4",
+ "calloop-wayland-source 0.2.0",
+ "cursor-icon",
+ "libc",
+ "log",
+ "memmap2",
+ "rustix 0.38.44",
+ "thiserror 1.0.69",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-csd-frame",
+ "wayland-cursor",
+ "wayland-protocols 0.31.2",
+ "wayland-protocols-wlr 0.2.0",
+ "wayland-scanner",
+ "xkeysym",
+]
+
+[[package]]
+name = "smithay-client-toolkit"
+version = "0.20.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0512da38f5e2b31201a93524adb8d3136276fa4fe4aafab4e1f727a82b534cc0"
+dependencies = [
+ "bitflags 2.11.1",
+ "calloop 0.14.4",
+ "calloop-wayland-source 0.4.1",
+ "cursor-icon",
+ "libc",
+ "log",
+ "memmap2",
+ "rustix 1.1.4",
+ "thiserror 2.0.18",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-csd-frame",
+ "wayland-cursor",
+ "wayland-protocols 0.32.12",
+ "wayland-protocols-experimental",
+ "wayland-protocols-misc",
+ "wayland-protocols-wlr 0.3.12",
+ "wayland-scanner",
+ "xkeysym",
+]
+
+[[package]]
+name = "smithay-clipboard"
+version = "0.7.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "71704c03f739f7745053bde45fa203a46c58d25bc5c4efba1d9a60e9dba81226"
+dependencies = [
+ "libc",
+ "smithay-client-toolkit 0.20.0",
+ "wayland-backend",
+]
+
+[[package]]
+name = "smol_str"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "dd538fb6910ac1099850255cf94a94df6551fbdd602454387d0adb2d1ca6dead"
+dependencies = [
+ "serde",
+]
+
[[package]]
name = "socket2"
version = "0.6.3"
@@ -765,6 +2366,18 @@ dependencies = [
"windows-sys 0.61.2",
]
+[[package]]
+name = "stable_deref_trait"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596"
+
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
[[package]]
name = "subtle"
version = "2.6.1"
@@ -873,6 +2486,16 @@ dependencies = [
"time-core",
]
+[[package]]
+name = "tinystr"
+version = "0.8.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d"
+dependencies = [
+ "displaydoc",
+ "zerovec",
+]
+
[[package]]
name = "tokio"
version = "1.52.1"
@@ -923,12 +2546,43 @@ dependencies = [
"tokio",
]
+[[package]]
+name = "toml_datetime"
+version = "1.1.1+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7"
+dependencies = [
+ "serde_core",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.25.11+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b59c4d22ed448339746c59b905d24568fcbb3ab65a500494f7b8c3e97739f2b"
+dependencies = [
+ "indexmap",
+ "toml_datetime",
+ "toml_parser",
+ "winnow",
+]
+
+[[package]]
+name = "toml_parser"
+version = "1.1.2+spec-1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526"
+dependencies = [
+ "winnow",
+]
+
[[package]]
name = "tracing"
version = "0.1.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100"
dependencies = [
+ "log",
"pin-project-lite",
"tracing-attributes",
"tracing-core",
@@ -984,24 +2638,70 @@ dependencies = [
"tracing-log",
]
+[[package]]
+name = "ttf-parser"
+version = "0.25.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31"
+
[[package]]
name = "unicode-ident"
version = "1.0.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75"
+[[package]]
+name = "unicode-segmentation"
+version = "1.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9629274872b2bfaf8d66f5f15725007f635594914870f65218920345aa11aa8c"
+
[[package]]
name = "untrusted"
version = "0.9.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+[[package]]
+name = "url"
+version = "2.5.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
+dependencies = [
+ "form_urlencoded",
+ "idna",
+ "percent-encoding",
+ "serde",
+]
+
+[[package]]
+name = "utf8_iter"
+version = "1.0.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+
[[package]]
name = "valuable"
version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65"
+[[package]]
+name = "version_check"
+version = "0.9.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+
+[[package]]
+name = "walkdir"
+version = "2.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b"
+dependencies = [
+ "same-file",
+ "winapi-util",
+]
+
[[package]]
name = "wasi"
version = "0.11.1+wasi-snapshot-preview1"
@@ -1017,6 +2717,257 @@ dependencies = [
"wit-bindgen",
]
+[[package]]
+name = "wasm-bindgen"
+version = "0.2.118"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0bf938a0bacb0469e83c1e148908bd7d5a6010354cf4fb73279b7447422e3a89"
+dependencies = [
+ "cfg-if",
+ "once_cell",
+ "rustversion",
+ "wasm-bindgen-macro",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-futures"
+version = "0.4.68"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f371d383f2fb139252e0bfac3b81b265689bf45b6874af544ffa4c975ac1ebf8"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "wasm-bindgen-macro"
+version = "0.2.118"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eeff24f84126c0ec2db7a449f0c2ec963c6a49efe0698c4242929da037ca28ed"
+dependencies = [
+ "quote",
+ "wasm-bindgen-macro-support",
+]
+
+[[package]]
+name = "wasm-bindgen-macro-support"
+version = "0.2.118"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9d08065faf983b2b80a79fd87d8254c409281cf7de75fc4b773019824196c904"
+dependencies = [
+ "bumpalo",
+ "proc-macro2",
+ "quote",
+ "syn",
+ "wasm-bindgen-shared",
+]
+
+[[package]]
+name = "wasm-bindgen-shared"
+version = "0.2.118"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5fd04d9e306f1907bd13c6361b5c6bfc7b3b3c095ed3f8a9246390f8dbdee129"
+dependencies = [
+ "unicode-ident",
+]
+
+[[package]]
+name = "wayland-backend"
+version = "0.3.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2857dd20b54e916ec7253b3d6b4d5c4d7d4ca2c33c2e11c6c76a99bd8744755d"
+dependencies = [
+ "cc",
+ "downcast-rs",
+ "rustix 1.1.4",
+ "scoped-tls",
+ "smallvec",
+ "wayland-sys",
+]
+
+[[package]]
+name = "wayland-client"
+version = "0.31.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "645c7c96bb74690c3189b5c9cb4ca1627062bb23693a4fad9d8c3de958260144"
+dependencies = [
+ "bitflags 2.11.1",
+ "rustix 1.1.4",
+ "wayland-backend",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-csd-frame"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e"
+dependencies = [
+ "bitflags 2.11.1",
+ "cursor-icon",
+ "wayland-backend",
+]
+
+[[package]]
+name = "wayland-cursor"
+version = "0.31.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4a52d18780be9b1314328a3de5f930b73d2200112e3849ca6cb11822793fb34d"
+dependencies = [
+ "rustix 1.1.4",
+ "wayland-client",
+ "xcursor",
+]
+
+[[package]]
+name = "wayland-protocols"
+version = "0.31.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8f81f365b8b4a97f422ac0e8737c438024b5951734506b0e1d775c73030561f4"
+dependencies = [
+ "bitflags 2.11.1",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols"
+version = "0.32.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "563a85523cade2429938e790815fd7319062103b9f4a2dc806e9b53b95982d8f"
+dependencies = [
+ "bitflags 2.11.1",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols-experimental"
+version = "20250721.0.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1"
+dependencies = [
+ "bitflags 2.11.1",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols 0.32.12",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols-misc"
+version = "0.3.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e9567599ef23e09b8dad6e429e5738d4509dfc46b3b21f32841a304d16b29c8"
+dependencies = [
+ "bitflags 2.11.1",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols 0.32.12",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols-plasma"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "23803551115ff9ea9bce586860c5c5a971e360825a0309264102a9495a5ff479"
+dependencies = [
+ "bitflags 2.11.1",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols 0.31.2",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols-wlr"
+version = "0.2.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ad1f61b76b6c2d8742e10f9ba5c3737f6530b4c243132c2a2ccc8aa96fe25cd6"
+dependencies = [
+ "bitflags 2.11.1",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols 0.31.2",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-protocols-wlr"
+version = "0.3.12"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "eb04e52f7836d7c7976c78ca0250d61e33873c34156a2a1fc9474828ec268234"
+dependencies = [
+ "bitflags 2.11.1",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols 0.32.12",
+ "wayland-scanner",
+]
+
+[[package]]
+name = "wayland-scanner"
+version = "0.31.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9c324a910fd86ebdc364a3e61ec1f11737d3b1d6c273c0239ee8ff4bc0d24b4a"
+dependencies = [
+ "proc-macro2",
+ "quick-xml",
+ "quote",
+]
+
+[[package]]
+name = "wayland-sys"
+version = "0.31.11"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d8eab23fefc9e41f8e841df4a9c707e8a8c4ed26e944ef69297184de2785e3be"
+dependencies = [
+ "dlib",
+ "log",
+ "once_cell",
+ "pkg-config",
+]
+
+[[package]]
+name = "web-sys"
+version = "0.3.95"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4f2dfbb17949fa2088e5d39408c48368947b86f7834484e87b73de55bc14d97d"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "web-time"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aa30049b1c872b72c89866d458eae9f20380ab280ffd1b1e18df2d3e2d98cfe0"
+dependencies = [
+ "js-sys",
+ "wasm-bindgen",
+]
+
+[[package]]
+name = "webbrowser"
+version = "1.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0fc95580916af1e68ff6a7be07446fc5db73ebf71cf092de939bbf5f7e189f72"
+dependencies = [
+ "core-foundation 0.10.1",
+ "jni 0.22.4",
+ "log",
+ "ndk-context",
+ "objc2 0.6.4",
+ "objc2-foundation 0.3.2",
+ "url",
+ "web-sys",
+]
+
[[package]]
name = "webpki-roots"
version = "0.26.11"
@@ -1035,19 +2986,68 @@ dependencies = [
"rustls-pki-types",
]
+[[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"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
+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 = "windows-link"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
+[[package]]
+name = "windows-sys"
+version = "0.45.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0"
+dependencies = [
+ "windows-targets 0.42.2",
+]
+
+[[package]]
+name = "windows-sys"
+version = "0.48.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9"
+dependencies = [
+ "windows-targets 0.48.5",
+]
+
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
- "windows-targets",
+ "windows-targets 0.52.6",
]
[[package]]
@@ -1059,34 +3059,100 @@ 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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973"
dependencies = [
- "windows_aarch64_gnullvm",
- "windows_aarch64_msvc",
- "windows_i686_gnu",
+ "windows_aarch64_gnullvm 0.52.6",
+ "windows_aarch64_msvc 0.52.6",
+ "windows_i686_gnu 0.52.6",
"windows_i686_gnullvm",
- "windows_i686_msvc",
- "windows_x86_64_gnu",
- "windows_x86_64_gnullvm",
- "windows_x86_64_msvc",
+ "windows_i686_msvc 0.52.6",
+ "windows_x86_64_gnu 0.52.6",
+ "windows_x86_64_gnullvm 0.52.6",
+ "windows_x86_64_msvc 0.52.6",
]
+[[package]]
+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"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8"
+
[[package]]
name = "windows_aarch64_gnullvm"
version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3"
+[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469"
+[[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"
@@ -1099,36 +3165,179 @@ version = "0.52.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66"
+[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66"
+[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78"
+[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d"
+[[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"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec"
+[[package]]
+name = "winit"
+version = "0.29.15"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0d59ad965a635657faf09c8f062badd885748428933dad8e8bdd64064d92e5ca"
+dependencies = [
+ "ahash",
+ "android-activity",
+ "atomic-waker",
+ "bitflags 2.11.1",
+ "bytemuck",
+ "calloop 0.12.4",
+ "cfg_aliases",
+ "core-foundation 0.9.4",
+ "core-graphics",
+ "cursor-icon",
+ "icrate",
+ "js-sys",
+ "libc",
+ "log",
+ "memmap2",
+ "ndk",
+ "ndk-sys",
+ "objc2 0.4.1",
+ "once_cell",
+ "orbclient",
+ "percent-encoding",
+ "raw-window-handle 0.5.2",
+ "raw-window-handle 0.6.2",
+ "redox_syscall 0.3.5",
+ "rustix 0.38.44",
+ "smithay-client-toolkit 0.18.1",
+ "smol_str",
+ "unicode-segmentation",
+ "wasm-bindgen",
+ "wasm-bindgen-futures",
+ "wayland-backend",
+ "wayland-client",
+ "wayland-protocols 0.31.2",
+ "wayland-protocols-plasma",
+ "web-sys",
+ "web-time",
+ "windows-sys 0.48.0",
+ "x11-dl",
+ "x11rb",
+ "xkbcommon-dl",
+]
+
+[[package]]
+name = "winnow"
+version = "1.0.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "2ee1708bef14716a11bae175f579062d4554d95be2c6829f518df847b7b3fdd0"
+dependencies = [
+ "memchr",
+]
+
[[package]]
name = "wit-bindgen"
version = "0.57.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e"
+[[package]]
+name = "writeable"
+version = "0.6.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4"
+
+[[package]]
+name = "x11-dl"
+version = "2.21.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f"
+dependencies = [
+ "libc",
+ "once_cell",
+ "pkg-config",
+]
+
+[[package]]
+name = "x11rb"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414"
+dependencies = [
+ "as-raw-xcb-connection",
+ "gethostname",
+ "libc",
+ "libloading",
+ "once_cell",
+ "rustix 1.1.4",
+ "x11rb-protocol",
+]
+
+[[package]]
+name = "x11rb-protocol"
+version = "0.13.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd"
+
[[package]]
name = "x509-parser"
version = "0.16.0"
@@ -1147,6 +3356,37 @@ dependencies = [
"time",
]
+[[package]]
+name = "xcursor"
+version = "0.3.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b"
+
+[[package]]
+name = "xkbcommon-dl"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5"
+dependencies = [
+ "bitflags 2.11.1",
+ "dlib",
+ "log",
+ "once_cell",
+ "xkeysym",
+]
+
+[[package]]
+name = "xkeysym"
+version = "0.2.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56"
+
+[[package]]
+name = "xml-rs"
+version = "0.8.28"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "3ae8337f8a065cfc972643663ea4279e04e7256de865aa66fe25cec5fb912d3f"
+
[[package]]
name = "yasna"
version = "0.5.2"
@@ -1156,6 +3396,29 @@ dependencies = [
"time",
]
+[[package]]
+name = "yoke"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca"
+dependencies = [
+ "stable_deref_trait",
+ "yoke-derive",
+ "zerofrom",
+]
+
+[[package]]
+name = "yoke-derive"
+version = "0.8.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
[[package]]
name = "zerocopy"
version = "0.8.48"
@@ -1176,12 +3439,66 @@ dependencies = [
"syn",
]
+[[package]]
+name = "zerofrom"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "69faa1f2a1ea75661980b013019ed6687ed0e83d069bc1114e2cc74c6c04c4df"
+dependencies = [
+ "zerofrom-derive",
+]
+
+[[package]]
+name = "zerofrom-derive"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+ "synstructure",
+]
+
[[package]]
name = "zeroize"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
+[[package]]
+name = "zerotrie"
+version = "0.2.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf"
+dependencies = [
+ "displaydoc",
+ "yoke",
+ "zerofrom",
+]
+
+[[package]]
+name = "zerovec"
+version = "0.11.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239"
+dependencies = [
+ "yoke",
+ "zerofrom",
+ "zerovec-derive",
+]
+
+[[package]]
+name = "zerovec-derive"
+version = "0.11.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555"
+dependencies = [
+ "proc-macro2",
+ "quote",
+ "syn",
+]
+
[[package]]
name = "zmij"
version = "1.0.21"
diff --git a/Cargo.toml b/Cargo.toml
index f496a92..b7909fb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,14 +1,27 @@
[package]
name = "mhrv-rs"
-version = "0.3.0"
+version = "0.4.0"
edition = "2021"
description = "Rust port of MasterHttpRelayVPN -- DPI bypass via Google Apps Script relay with domain fronting"
license = "MIT"
+[lib]
+name = "mhrv_rs"
+path = "src/lib.rs"
+
[[bin]]
name = "mhrv-rs"
path = "src/main.rs"
+[[bin]]
+name = "mhrv-rs-ui"
+path = "src/bin/ui.rs"
+required-features = ["ui"]
+
+[features]
+default = []
+ui = ["dep:eframe"]
+
[dependencies]
tokio = { version = "1", features = ["rt-multi-thread", "macros", "net", "time", "io-util", "signal", "sync"] }
tokio-rustls = "0.26"
@@ -30,6 +43,14 @@ rand = "0.8"
h2 = "0.4"
http = "1"
flate2 = "1"
+directories = "5"
+
+# Optional UI dep: only pulled in when --features ui is set.
+eframe = { version = "0.28", default-features = false, features = [
+ "default_fonts",
+ "glow",
+ "persistence",
+], optional = true }
[profile.release]
panic = "abort"
diff --git a/README.md b/README.md
index e8f7b84..73bd876 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,21 @@ The censor's DPI sees `www.google.com` in the TLS SNI and lets it through. Googl
Linux (x86_64/aarch64), macOS (x86_64/aarch64), Windows (x86_64). Prebuilt binaries on the [releases page](https://github.com/therealaleph/MasterHttpRelayVPN-RUST/releases).
+## CLI or UI
+
+Each release ships two binaries:
+
+- **`mhrv-rs`** — the CLI. Always works. Headless servers, Docker, automation. No system deps on macOS/Windows; on Linux works even without a display server.
+- **`mhrv-rs-ui`** — the desktop UI (egui). Form for the config, Start/Stop/Test buttons, live stats, recent log. macOS releases also include `mhrv-rs.app` (double-click to launch). Linux UI requires a display server and common desktop libraries (`libxkbcommon`, `libwayland-client`, `libxcb`, `libgl`, `libx11`, `libgtk-3`); install them via your distro's package manager if missing.
+
+Config + the MITM CA live in the platform user-data dir:
+
+- macOS: `~/Library/Application Support/mhrv-rs/`
+- Linux: `~/.config/mhrv-rs/`
+- Windows: `%APPDATA%\mhrv-rs\`
+
+The CLI also falls back to `./config.json` in the current directory for backward compatibility.
+
## Setup Guide
### Step 1: Deploy the Apps Script relay (one-time)
diff --git a/assets/macos/Info.plist b/assets/macos/Info.plist
new file mode 100644
index 0000000..34c7337
--- /dev/null
+++ b/assets/macos/Info.plist
@@ -0,0 +1,30 @@
+
+
+
+
+ CFBundleName
+ mhrv-rs
+ CFBundleDisplayName
+ mhrv-rs
+ CFBundleIdentifier
+ com.therealaleph.mhrv-rs
+ CFBundleVersion
+ __VERSION__
+ CFBundleShortVersionString
+ __VERSION__
+ CFBundlePackageType
+ APPL
+ CFBundleExecutable
+ mhrv-rs-ui
+ CFBundleSignature
+ ????
+ LSMinimumSystemVersion
+ 10.14
+ NSHighResolutionCapable
+
+ NSPrincipalClass
+ NSApplication
+ LSApplicationCategoryType
+ public.app-category.utilities
+
+
diff --git a/assets/macos/build-app.sh b/assets/macos/build-app.sh
new file mode 100755
index 0000000..b2a01a5
--- /dev/null
+++ b/assets/macos/build-app.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+# Bundle the mhrv-rs-ui binary into a macOS .app.
+# Usage: build-app.sh
+set -eu
+
+BIN="$1"
+VER="$2"
+OUT_DIR="$3"
+
+APP="$OUT_DIR/mhrv-rs.app"
+rm -rf "$APP"
+mkdir -p "$APP/Contents/MacOS"
+mkdir -p "$APP/Contents/Resources"
+
+cp "$BIN" "$APP/Contents/MacOS/mhrv-rs-ui"
+chmod +x "$APP/Contents/MacOS/mhrv-rs-ui"
+
+SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
+sed "s/__VERSION__/$VER/g" "$SCRIPT_DIR/Info.plist" > "$APP/Contents/Info.plist"
+
+echo "Built $APP"
diff --git a/src/bin/ui.rs b/src/bin/ui.rs
new file mode 100644
index 0000000..5d1ba43
--- /dev/null
+++ b/src/bin/ui.rs
@@ -0,0 +1,672 @@
+use std::collections::VecDeque;
+use std::path::PathBuf;
+use std::sync::mpsc::{Receiver, Sender};
+use std::sync::{Arc, Mutex};
+use std::time::{Duration, Instant};
+
+use eframe::egui;
+use tokio::runtime::Runtime;
+use tokio::sync::Mutex as AsyncMutex;
+use tokio::task::JoinHandle;
+
+use mhrv_rs::cert_installer::install_ca;
+use mhrv_rs::config::{Config, ScriptId};
+use mhrv_rs::data_dir;
+use mhrv_rs::domain_fronter::DomainFronter;
+use mhrv_rs::mitm::{MitmCertManager, CA_CERT_FILE};
+use mhrv_rs::proxy_server::ProxyServer;
+use mhrv_rs::{scan_ips, test_cmd};
+
+const VERSION: &str = env!("CARGO_PKG_VERSION");
+const WIN_WIDTH: f32 = 520.0;
+const WIN_HEIGHT: f32 = 680.0;
+const LOG_MAX: usize = 200;
+
+fn main() -> eframe::Result<()> {
+ let _ = rustls::crypto::ring::default_provider().install_default();
+
+ let shared = Arc::new(Shared::default());
+ let (cmd_tx, cmd_rx) = std::sync::mpsc::channel::();
+
+ let shared_bg = shared.clone();
+ std::thread::Builder::new()
+ .name("mhrv-bg".into())
+ .spawn(move || background_thread(shared_bg, cmd_rx))
+ .expect("failed to spawn background thread");
+
+ let form = load_form();
+
+ let options = eframe::NativeOptions {
+ viewport: egui::ViewportBuilder::default()
+ .with_inner_size([WIN_WIDTH, WIN_HEIGHT])
+ .with_min_inner_size([420.0, 540.0])
+ .with_title(format!("mhrv-rs {}", VERSION)),
+ ..Default::default()
+ };
+
+ eframe::run_native(
+ "mhrv-rs",
+ options,
+ Box::new(move |cc| {
+ cc.egui_ctx.set_visuals(egui::Visuals::dark());
+ Ok(Box::new(App {
+ shared,
+ cmd_tx,
+ form,
+ last_poll: Instant::now(),
+ toast: None,
+ }))
+ }),
+ )
+}
+
+#[derive(Default)]
+struct Shared {
+ state: Mutex,
+}
+
+#[derive(Default)]
+struct UiState {
+ running: bool,
+ started_at: Option,
+ last_stats: Option,
+ log: VecDeque,
+ ca_trusted: Option,
+ last_test_ok: Option,
+ last_test_msg: String,
+}
+
+enum Cmd {
+ Start(Config),
+ Stop,
+ Test(Config),
+ InstallCa,
+ CheckCaTrusted,
+ PollStats,
+}
+
+struct App {
+ shared: Arc,
+ cmd_tx: Sender,
+ form: FormState,
+ last_poll: Instant,
+ toast: Option<(String, Instant)>,
+}
+
+#[derive(Clone)]
+struct FormState {
+ script_id: String,
+ auth_key: String,
+ google_ip: String,
+ front_domain: String,
+ listen_host: String,
+ listen_port: String,
+ socks5_port: String,
+ log_level: String,
+ verify_ssl: bool,
+ show_auth_key: bool,
+}
+
+fn load_form() -> FormState {
+ let path = data_dir::config_path();
+ let cwd = PathBuf::from("config.json");
+ let existing = if path.exists() {
+ Config::load(&path).ok()
+ } else if cwd.exists() {
+ Config::load(&cwd).ok()
+ } else {
+ None
+ };
+ if let Some(c) = existing {
+ let sid = match &c.script_id {
+ Some(ScriptId::One(s)) => s.clone(),
+ Some(ScriptId::Many(v)) => v.join(", "),
+ None => match &c.script_ids {
+ Some(ScriptId::One(s)) => s.clone(),
+ Some(ScriptId::Many(v)) => v.join(", "),
+ None => String::new(),
+ },
+ };
+ FormState {
+ script_id: sid,
+ auth_key: c.auth_key,
+ google_ip: c.google_ip,
+ front_domain: c.front_domain,
+ listen_host: c.listen_host,
+ listen_port: c.listen_port.to_string(),
+ socks5_port: c.socks5_port.map(|p| p.to_string()).unwrap_or_default(),
+ log_level: c.log_level,
+ verify_ssl: c.verify_ssl,
+ show_auth_key: false,
+ }
+ } else {
+ FormState {
+ script_id: String::new(),
+ auth_key: String::new(),
+ google_ip: "216.239.38.120".into(),
+ front_domain: "www.google.com".into(),
+ listen_host: "127.0.0.1".into(),
+ listen_port: "8085".into(),
+ socks5_port: "8086".into(),
+ log_level: "info".into(),
+ verify_ssl: true,
+ show_auth_key: false,
+ }
+ }
+}
+
+impl FormState {
+ fn to_config(&self) -> Result {
+ if self.script_id.trim().is_empty() {
+ return Err("Apps Script ID is required".into());
+ }
+ if self.auth_key.trim().is_empty() {
+ return Err("Auth key is required".into());
+ }
+ let listen_port: u16 = self
+ .listen_port
+ .parse()
+ .map_err(|_| "HTTP port must be a number".to_string())?;
+ let socks5_port: Option = if self.socks5_port.trim().is_empty() {
+ None
+ } else {
+ Some(
+ self.socks5_port
+ .parse()
+ .map_err(|_| "SOCKS5 port must be a number".to_string())?,
+ )
+ };
+ let ids: Vec = self
+ .script_id
+ .split(',')
+ .map(|s| s.trim().to_string())
+ .filter(|s| !s.is_empty())
+ .collect();
+ let script_id = if ids.len() == 1 {
+ Some(ScriptId::One(ids[0].clone()))
+ } else {
+ Some(ScriptId::Many(ids))
+ };
+ Ok(Config {
+ mode: "apps_script".into(),
+ google_ip: self.google_ip.trim().to_string(),
+ front_domain: self.front_domain.trim().to_string(),
+ script_id,
+ script_ids: None,
+ auth_key: self.auth_key.clone(),
+ listen_host: self.listen_host.trim().to_string(),
+ listen_port,
+ socks5_port,
+ log_level: self.log_level.trim().to_string(),
+ verify_ssl: self.verify_ssl,
+ hosts: std::collections::HashMap::new(),
+ enable_batching: false,
+ })
+ }
+}
+
+fn save_config(cfg: &Config) -> Result {
+ let path = data_dir::config_path();
+ if let Some(parent) = path.parent() {
+ std::fs::create_dir_all(parent).map_err(|e| e.to_string())?;
+ }
+ let json = serde_json::to_string_pretty(&ConfigWire::from(cfg))
+ .map_err(|e| e.to_string())?;
+ std::fs::write(&path, json).map_err(|e| e.to_string())?;
+ Ok(path)
+}
+
+#[derive(serde::Serialize)]
+struct ConfigWire<'a> {
+ mode: &'a str,
+ google_ip: &'a str,
+ front_domain: &'a str,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ script_id: Option>,
+ auth_key: &'a str,
+ listen_host: &'a str,
+ listen_port: u16,
+ #[serde(skip_serializing_if = "Option::is_none")]
+ socks5_port: Option,
+ log_level: &'a str,
+ verify_ssl: bool,
+ #[serde(skip_serializing_if = "std::collections::HashMap::is_empty")]
+ hosts: &'a std::collections::HashMap,
+}
+
+#[derive(serde::Serialize)]
+#[serde(untagged)]
+enum ScriptIdWire<'a> {
+ One(&'a str),
+ Many(Vec<&'a str>),
+}
+
+impl<'a> From<&'a Config> for ConfigWire<'a> {
+ fn from(c: &'a Config) -> Self {
+ let script_id = c.script_id.as_ref().map(|s| match s {
+ ScriptId::One(v) => ScriptIdWire::One(v.as_str()),
+ ScriptId::Many(v) => ScriptIdWire::Many(v.iter().map(String::as_str).collect()),
+ });
+ ConfigWire {
+ mode: c.mode.as_str(),
+ google_ip: c.google_ip.as_str(),
+ front_domain: c.front_domain.as_str(),
+ script_id,
+ auth_key: c.auth_key.as_str(),
+ listen_host: c.listen_host.as_str(),
+ listen_port: c.listen_port,
+ socks5_port: c.socks5_port,
+ log_level: c.log_level.as_str(),
+ verify_ssl: c.verify_ssl,
+ hosts: &c.hosts,
+ }
+ }
+}
+
+impl eframe::App for App {
+ fn update(&mut self, ctx: &egui::Context, _: &mut eframe::Frame) {
+ if self.last_poll.elapsed() > Duration::from_millis(700) {
+ let _ = self.cmd_tx.send(Cmd::PollStats);
+ self.last_poll = Instant::now();
+ }
+ ctx.request_repaint_after(Duration::from_millis(500));
+
+ egui::CentralPanel::default().show(ctx, |ui| {
+ ui.style_mut().spacing.item_spacing = egui::vec2(8.0, 6.0);
+
+ ui.horizontal(|ui| {
+ ui.label(egui::RichText::new(format!("mhrv-rs {}", VERSION))
+ .size(16.0));
+ ui.add_space(8.0);
+ let running = self.shared.state.lock().unwrap().running;
+ let dot = if running { "running" } else { "stopped" };
+ let color = if running { egui::Color32::from_rgb(70, 170, 100) } else { egui::Color32::from_rgb(170, 90, 90) };
+ ui.label(egui::RichText::new(dot).color(color).monospace());
+ });
+
+ ui.separator();
+
+ // Config form.
+ egui::Grid::new("cfg")
+ .num_columns(2)
+ .spacing([10.0, 6.0])
+ .show(ui, |ui| {
+ ui.label("Apps Script ID");
+ ui.add(egui::TextEdit::singleline(&mut self.form.script_id)
+ .desired_width(f32::INFINITY));
+ ui.end_row();
+
+ ui.label("Auth key");
+ ui.horizontal(|ui| {
+ let te = egui::TextEdit::singleline(&mut self.form.auth_key)
+ .password(!self.form.show_auth_key)
+ .desired_width(f32::INFINITY);
+ ui.add(te);
+ });
+ ui.end_row();
+
+ ui.label("Google IP");
+ ui.horizontal(|ui| {
+ ui.text_edit_singleline(&mut self.form.google_ip);
+ if ui.button("scan").on_hover_text(
+ "Try several known Google frontend IPs and report which are reachable (results printed to stdout/terminal)"
+ ).clicked() {
+ if let Ok(cfg) = self.form.to_config() {
+ let _ = self.cmd_tx.send(Cmd::Test(cfg.clone()));
+ self.toast = Some(("Scan started — check terminal for full results".into(), Instant::now()));
+ }
+ }
+ });
+ ui.end_row();
+
+ ui.label("Front domain");
+ ui.add(egui::TextEdit::singleline(&mut self.form.front_domain)
+ .desired_width(f32::INFINITY));
+ ui.end_row();
+
+ ui.label("Listen host");
+ ui.add(egui::TextEdit::singleline(&mut self.form.listen_host)
+ .desired_width(f32::INFINITY));
+ ui.end_row();
+
+ ui.label("HTTP port");
+ ui.add(egui::TextEdit::singleline(&mut self.form.listen_port).desired_width(80.0));
+ ui.end_row();
+
+ ui.label("SOCKS5 port");
+ ui.add(egui::TextEdit::singleline(&mut self.form.socks5_port).desired_width(80.0));
+ ui.end_row();
+
+ ui.label("Log level");
+ egui::ComboBox::from_id_source("loglevel")
+ .selected_text(&self.form.log_level)
+ .show_ui(ui, |ui| {
+ for lvl in ["warn", "info", "debug", "trace"] {
+ ui.selectable_value(&mut self.form.log_level, lvl.into(), lvl);
+ }
+ });
+ ui.end_row();
+
+ ui.label("");
+ ui.checkbox(&mut self.form.verify_ssl, "Verify TLS server certificate (recommended)");
+ ui.end_row();
+
+ ui.label("");
+ ui.checkbox(&mut self.form.show_auth_key, "Show auth key");
+ ui.end_row();
+ });
+
+ ui.add_space(4.0);
+ ui.horizontal(|ui| {
+ if ui.button("Save config").clicked() {
+ match self.form.to_config().and_then(|c| save_config(&c)) {
+ Ok(p) => self.toast = Some((format!("Saved to {}", p.display()), Instant::now())),
+ Err(e) => self.toast = Some((format!("Save failed: {}", e), Instant::now())),
+ }
+ }
+ ui.small(format!("location: {}", data_dir::config_path().display()));
+ });
+
+ ui.separator();
+
+ // Status + stats
+ let (running, started_at, stats, ca_trusted, last_test_msg) = {
+ let s = self.shared.state.lock().unwrap();
+ (s.running, s.started_at, s.last_stats, s.ca_trusted, s.last_test_msg.clone())
+ };
+
+ ui.horizontal(|ui| {
+ if running {
+ let up = started_at.map(|t| t.elapsed()).unwrap_or_default();
+ ui.label(egui::RichText::new(format!(
+ "Status: running (uptime {})", fmt_duration(up)
+ )).strong());
+ } else {
+ ui.label(egui::RichText::new("Status: stopped").strong());
+ }
+ });
+
+ if let Some(s) = stats {
+ egui::Grid::new("stats").num_columns(2).spacing([10.0, 4.0]).show(ui, |ui| {
+ ui.label("relay calls");
+ ui.label(egui::RichText::new(s.relay_calls.to_string()).monospace());
+ ui.end_row();
+ ui.label("failures");
+ ui.label(egui::RichText::new(s.relay_failures.to_string()).monospace());
+ ui.end_row();
+ ui.label("coalesced");
+ ui.label(egui::RichText::new(s.coalesced.to_string()).monospace());
+ ui.end_row();
+ ui.label("cache hits / total");
+ ui.label(egui::RichText::new(format!(
+ "{} / {} ({:.0}%)",
+ s.cache_hits,
+ s.cache_hits + s.cache_misses,
+ s.hit_rate()
+ )).monospace());
+ ui.end_row();
+ ui.label("cache size");
+ ui.label(egui::RichText::new(format!("{} KB", s.cache_bytes / 1024)).monospace());
+ ui.end_row();
+ ui.label("bytes relayed");
+ ui.label(egui::RichText::new(fmt_bytes(s.bytes_relayed)).monospace());
+ ui.end_row();
+ ui.label("active scripts");
+ ui.label(egui::RichText::new(format!(
+ "{} / {}", s.total_scripts - s.blacklisted_scripts, s.total_scripts
+ )).monospace());
+ ui.end_row();
+ });
+ }
+
+ ui.add_space(4.0);
+
+ ui.horizontal(|ui| {
+ if !running {
+ if ui.button("Start").clicked() {
+ match self.form.to_config() {
+ Ok(cfg) => {
+ let _ = self.cmd_tx.send(Cmd::Start(cfg));
+ }
+ Err(e) => {
+ self.toast = Some((format!("Cannot start: {}", e), Instant::now()));
+ }
+ }
+ }
+ } else if ui.button("Stop").clicked() {
+ let _ = self.cmd_tx.send(Cmd::Stop);
+ }
+
+ if ui.button("Test").clicked() {
+ match self.form.to_config() {
+ Ok(cfg) => {
+ let _ = self.cmd_tx.send(Cmd::Test(cfg));
+ }
+ Err(e) => {
+ self.toast = Some((format!("Cannot test: {}", e), Instant::now()));
+ }
+ }
+ }
+
+ if ui.button("Install CA").clicked() {
+ let _ = self.cmd_tx.send(Cmd::InstallCa);
+ }
+
+ if ui.button("Check CA").clicked() {
+ let _ = self.cmd_tx.send(Cmd::CheckCaTrusted);
+ }
+ });
+
+ if !last_test_msg.is_empty() {
+ ui.small(last_test_msg);
+ }
+ match ca_trusted {
+ Some(true) => { ui.small("CA appears trusted."); },
+ Some(false) => { ui.small("CA is NOT trusted in the system store. Click 'Install CA' (may require admin)."); },
+ None => {},
+ }
+
+ ui.separator();
+ ui.label(egui::RichText::new("Recent log").strong());
+ egui::ScrollArea::vertical()
+ .max_height(180.0)
+ .stick_to_bottom(true)
+ .show(ui, |ui| {
+ let log = self.shared.state.lock().unwrap().log.clone();
+ for line in log.iter() {
+ ui.monospace(line);
+ }
+ });
+
+ // Transient toast at the bottom.
+ if let Some((msg, t)) = &self.toast {
+ if t.elapsed() < Duration::from_secs(5) {
+ ui.add_space(4.0);
+ ui.colored_label(egui::Color32::from_rgb(200, 170, 80), msg);
+ } else {
+ self.toast = None;
+ }
+ }
+ });
+ }
+}
+
+fn fmt_duration(d: Duration) -> String {
+ let s = d.as_secs();
+ format!("{:02}:{:02}:{:02}", s / 3600, (s / 60) % 60, s % 60)
+}
+
+fn fmt_bytes(b: u64) -> String {
+ const K: u64 = 1024;
+ const M: u64 = K * K;
+ const G: u64 = M * K;
+ if b >= G {
+ format!("{:.2} GB", b as f64 / G as f64)
+ } else if b >= M {
+ format!("{:.2} MB", b as f64 / M as f64)
+ } else if b >= K {
+ format!("{:.1} KB", b as f64 / K as f64)
+ } else {
+ format!("{} B", b)
+ }
+}
+
+// ---------- Background thread: owns the tokio runtime + proxy lifecycle ----------
+
+fn background_thread(shared: Arc, rx: Receiver) {
+ let rt = Runtime::new().expect("failed to create tokio runtime");
+
+ let mut active: Option<(JoinHandle<()>, Arc>>>)> = None;
+
+ loop {
+ match rx.recv_timeout(Duration::from_millis(250)) {
+ Ok(Cmd::PollStats) => {
+ if let Some((_, fronter_slot)) = &active {
+ let slot = fronter_slot.clone();
+ let shared = shared.clone();
+ rt.spawn(async move {
+ let f = slot.lock().await;
+ if let Some(fronter) = f.as_ref() {
+ let s = fronter.snapshot_stats();
+ shared.state.lock().unwrap().last_stats = Some(s);
+ }
+ });
+ }
+ }
+ Ok(Cmd::Start(cfg)) => {
+ if active.is_some() {
+ push_log(&shared, "[ui] already running");
+ continue;
+ }
+ push_log(&shared, "[ui] starting proxy...");
+ let shared2 = shared.clone();
+ let fronter_slot: Arc>>> =
+ Arc::new(AsyncMutex::new(None));
+ let fronter_slot2 = fronter_slot.clone();
+
+ let handle = rt.spawn(async move {
+ let base = data_dir::data_dir();
+ let mitm = match MitmCertManager::new_in(&base) {
+ Ok(m) => m,
+ Err(e) => {
+ push_log(&shared2, &format!("[ui] MITM init failed: {}", e));
+ shared2.state.lock().unwrap().running = false;
+ return;
+ }
+ };
+ let mitm = Arc::new(AsyncMutex::new(mitm));
+ let server = match ProxyServer::new(&cfg, mitm) {
+ Ok(s) => s,
+ Err(e) => {
+ push_log(&shared2, &format!("[ui] proxy build failed: {}", e));
+ shared2.state.lock().unwrap().running = false;
+ return;
+ }
+ };
+ match DomainFronter::new(&cfg) {
+ Ok(f) => {
+ let arc = Arc::new(f);
+ *fronter_slot2.lock().await = Some(arc);
+ }
+ Err(e) => {
+ push_log(&shared2, &format!("[ui] fronter build failed: {}", e));
+ }
+ }
+ {
+ let mut s = shared2.state.lock().unwrap();
+ s.running = true;
+ s.started_at = Some(Instant::now());
+ }
+ push_log(&shared2, &format!(
+ "[ui] listening HTTP {}:{} SOCKS5 {}:{}",
+ cfg.listen_host, cfg.listen_port,
+ cfg.listen_host, cfg.socks5_port.unwrap_or(cfg.listen_port + 1)
+ ));
+ let _ = server.run().await;
+ shared2.state.lock().unwrap().running = false;
+ push_log(&shared2, "[ui] proxy stopped");
+ });
+
+ active = Some((handle, fronter_slot));
+ }
+ Ok(Cmd::Stop) => {
+ if let Some((handle, _)) = active.take() {
+ handle.abort();
+ shared.state.lock().unwrap().running = false;
+ shared.state.lock().unwrap().started_at = None;
+ shared.state.lock().unwrap().last_stats = None;
+ push_log(&shared, "[ui] stop requested");
+ }
+ }
+ Ok(Cmd::Test(cfg)) => {
+ let shared2 = shared.clone();
+ push_log(&shared, "[ui] running test...");
+ rt.spawn(async move {
+ let ok = test_cmd::run(&cfg).await;
+ shared2.state.lock().unwrap().last_test_ok = Some(ok);
+ shared2.state.lock().unwrap().last_test_msg = if ok {
+ "Test passed — relay is working.".into()
+ } else {
+ "Test failed — see terminal for details.".into()
+ };
+ push_log(&shared2, &format!("[ui] test result: {}", if ok { "pass" } else { "fail" }));
+ // Also run ip scan on demand (cheap).
+ let _ = scan_ips::run(&cfg).await;
+ });
+ }
+ Ok(Cmd::InstallCa) => {
+ let shared2 = shared.clone();
+ std::thread::spawn(move || {
+ push_log(&shared2, "[ui] installing CA...");
+ let base = data_dir::data_dir();
+ if let Err(e) = MitmCertManager::new_in(&base) {
+ push_log(&shared2, &format!("[ui] CA init failed: {}", e));
+ return;
+ }
+ let ca = base.join(CA_CERT_FILE);
+ match install_ca(&ca) {
+ Ok(()) => {
+ push_log(&shared2, "[ui] CA install ok");
+ shared2.state.lock().unwrap().ca_trusted = Some(true);
+ }
+ Err(e) => {
+ push_log(&shared2, &format!("[ui] CA install failed: {}", e));
+ push_log(&shared2, "[ui] hint: run the terminal binary with sudo/admin: mhrv-rs --install-cert");
+ }
+ }
+ });
+ }
+ Ok(Cmd::CheckCaTrusted) => {
+ let shared2 = shared.clone();
+ std::thread::spawn(move || {
+ let base = data_dir::data_dir();
+ let ca = base.join(CA_CERT_FILE);
+ let trusted = mhrv_rs::cert_installer::is_ca_trusted(&ca);
+ shared2.state.lock().unwrap().ca_trusted = Some(trusted);
+ });
+ }
+ Err(_) => {}
+ }
+
+ // Clean up finished task.
+ if let Some((handle, _)) = &active {
+ if handle.is_finished() {
+ active = None;
+ shared.state.lock().unwrap().running = false;
+ }
+ }
+ }
+}
+
+fn push_log(shared: &Shared, msg: &str) {
+ let line = format!(
+ "{} {}",
+ time::OffsetDateTime::now_utc().format(&time::format_description::well_known::Iso8601::DEFAULT).unwrap_or_default(),
+ msg
+ );
+ let mut s = shared.state.lock().unwrap();
+ s.log.push_back(line);
+ while s.log.len() > LOG_MAX {
+ s.log.pop_front();
+ }
+}
diff --git a/src/data_dir.rs b/src/data_dir.rs
new file mode 100644
index 0000000..f2e5792
--- /dev/null
+++ b/src/data_dir.rs
@@ -0,0 +1,54 @@
+use std::path::{Path, PathBuf};
+
+const APP_NAME: &str = "mhrv-rs";
+
+/// Returns the platform-appropriate user-data directory for this app, creating
+/// it if necessary. Falls back to the current directory if the dir can't be
+/// determined (rare).
+///
+/// - macOS: `~/Library/Application Support/mhrv-rs`
+/// - Linux: `~/.config/mhrv-rs` (or `$XDG_CONFIG_HOME/mhrv-rs`)
+/// - Windows: `%APPDATA%\mhrv-rs`
+pub fn data_dir() -> PathBuf {
+ let dir = directories::ProjectDirs::from("", "", APP_NAME)
+ .map(|d| d.config_dir().to_path_buf())
+ .unwrap_or_else(|| PathBuf::from("."));
+ let _ = std::fs::create_dir_all(&dir);
+ dir
+}
+
+/// Path to the config.json for this platform's data dir.
+pub fn config_path() -> PathBuf {
+ data_dir().join("config.json")
+}
+
+/// Path to the CA cert inside the data dir (the MITM CA).
+pub fn ca_cert_path() -> PathBuf {
+ data_dir().join("ca").join("ca.crt")
+}
+
+/// Path to the CA private key inside the data dir.
+pub fn ca_key_path() -> PathBuf {
+ data_dir().join("ca").join("ca.key")
+}
+
+/// Resolve a config path: if the user supplied an explicit path, use it.
+/// Otherwise look in the user-data dir first, fall back to `./config.json`
+/// in the current working directory (for backward compatibility with the
+/// original CLI behavior).
+pub fn resolve_config_path(cli_arg: Option<&Path>) -> PathBuf {
+ if let Some(p) = cli_arg {
+ return p.to_path_buf();
+ }
+ let user = config_path();
+ if user.exists() {
+ return user;
+ }
+ let cwd = PathBuf::from("config.json");
+ if cwd.exists() {
+ return cwd;
+ }
+ // Neither exists: return the user-data path so errors point to the
+ // blessed location and commands like "Save config" write there.
+ user
+}
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..4b8e44b
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,11 @@
+#![allow(dead_code)]
+
+pub mod cache;
+pub mod cert_installer;
+pub mod config;
+pub mod data_dir;
+pub mod domain_fronter;
+pub mod mitm;
+pub mod proxy_server;
+pub mod scan_ips;
+pub mod test_cmd;
diff --git a/src/main.rs b/src/main.rs
index 58478e3..1c180ad 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,30 +1,22 @@
#![allow(dead_code)]
-mod cache;
-mod cert_installer;
-mod config;
-mod domain_fronter;
-mod mitm;
-mod proxy_server;
-mod scan_ips;
-mod test_cmd;
-
-use std::path::{Path, PathBuf};
+use std::path::PathBuf;
use std::process::ExitCode;
use std::sync::Arc;
use tokio::sync::Mutex;
use tracing_subscriber::EnvFilter;
-use crate::cert_installer::{install_ca, is_ca_trusted};
-use crate::config::Config;
-use crate::mitm::{MitmCertManager, CA_CERT_FILE};
-use crate::proxy_server::ProxyServer;
+use mhrv_rs::cert_installer::{install_ca, is_ca_trusted};
+use mhrv_rs::config::Config;
+use mhrv_rs::mitm::{MitmCertManager, CA_CERT_FILE};
+use mhrv_rs::proxy_server::ProxyServer;
+use mhrv_rs::{scan_ips, test_cmd};
const VERSION: &str = env!("CARGO_PKG_VERSION");
struct Args {
- config_path: PathBuf,
+ config_path: Option,
install_cert: bool,
no_cert_check: bool,
command: Command,
@@ -60,7 +52,7 @@ ENV:
}
fn parse_args() -> Result {
- let mut config_path = PathBuf::from("config.json");
+ let mut config_path: Option = None;
let mut install_cert = false;
let mut no_cert_check = false;
let mut command = Command::Serve;
@@ -93,7 +85,7 @@ fn parse_args() -> Result {
}
"-c" | "--config" => {
let v = it.next().ok_or_else(|| "--config needs a path".to_string())?;
- config_path = PathBuf::from(v);
+ config_path = Some(PathBuf::from(v));
}
"--install-cert" => install_cert = true,
"--no-cert-check" => no_cert_check = true,
@@ -134,9 +126,8 @@ async fn main() -> ExitCode {
// --install-cert can run without a valid config — only needs the CA file.
if args.install_cert {
init_logging("info");
- // Ensure the CA exists.
- let base = Path::new(".");
- if let Err(e) = MitmCertManager::new_in(base) {
+ let base = mhrv_rs::data_dir::data_dir();
+ if let Err(e) = MitmCertManager::new_in(&base) {
eprintln!("failed to initialize CA: {}", e);
return ExitCode::FAILURE;
}
@@ -153,11 +144,15 @@ async fn main() -> ExitCode {
}
}
- let config = match Config::load(&args.config_path) {
+ let config_path = mhrv_rs::data_dir::resolve_config_path(args.config_path.as_deref());
+ let config = match Config::load(&config_path) {
Ok(c) => c,
Err(e) => {
eprintln!("{}", e);
- eprintln!("Copy config.example.json to config.json and fill in your values.");
+ eprintln!(
+ "No valid config found. Copy config.example.json to either:\n {}\nor run with --config .",
+ config_path.display()
+ );
return ExitCode::FAILURE;
}
};
@@ -193,8 +188,8 @@ async fn main() -> ExitCode {
}
// Initialize MITM manager (generates CA on first run).
- let base = Path::new(".");
- let mitm = match MitmCertManager::new_in(base) {
+ let base = mhrv_rs::data_dir::data_dir();
+ let mitm = match MitmCertManager::new_in(&base) {
Ok(m) => m,
Err(e) => {
eprintln!("failed to init MITM CA: {}", e);