diff --git a/.github/workflows/language.yml b/.github/workflows/language.yml index 4232a58..3131e45 100644 --- a/.github/workflows/language.yml +++ b/.github/workflows/language.yml @@ -20,10 +20,6 @@ on: - '.gitignore' - 'docs/**' merge_group: - paths-ignore: - - '**/*.md' - - '.gitignore' - - 'docs/**' concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -53,9 +49,19 @@ jobs: tool: cargo-make - name: Install vibe-style (latest release) + env: + GITHUB_TOKEN: ${{ github.token }} run: | set -euo pipefail - VERSION="$(curl -fsSL https://api.github.com/repos/hack-ink/vibe-style/releases/latest | grep -oE '"tag_name": "v[^"]+"' | cut -d'"' -f4)" + VERSION="$( + curl -fsSL \ + -H "Accept: application/vnd.github+json" \ + -H "Authorization: Bearer ${GITHUB_TOKEN}" \ + -H "X-GitHub-Api-Version: 2022-11-28" \ + https://api.github.com/repos/hack-ink/vibe-style/releases/latest \ + | grep -oE '"tag_name": "v[^"]+"' \ + | cut -d'"' -f4 + )" TARGET="$(rustc -vV | awk -F': ' '/^host:/ {print $2}')" ASSET="vibe-style-${TARGET}-${VERSION}.tgz" @@ -79,7 +85,9 @@ jobs: run: cargo make fmt-rust-check - name: Run tests - run: cargo make test-rust + env: + VOXIT_NATIVE_HOST_SIGN_IDENTITY: '-' + run: cargo make test toml: name: TOML checks diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7b9cd30..2ad0edb 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -40,22 +40,16 @@ jobs: cache: true components: rustfmt, clippy - - name: Install cargo-bundle - uses: taiki-e/install-action@v2 - with: - tool: cargo-bundle - - - name: Add target - run: rustup target add ${{ matrix.target.name }} - - - name: Build - run: cargo build --profile final-release --locked --target ${{ matrix.target.name }} -p voxit - - name: Bundle and Pack (macOS app) if: matrix.target.os == 'macos-latest' run: | - cargo bundle --profile final-release --target ${{ matrix.target.name }} -p voxit - APP_PATH="target/${{ matrix.target.name }}/bundle/osx/Voxit.app" + VOXIT_NATIVE_HOST_RUST_PROFILE=final-release \ + VOXIT_NATIVE_HOST_SWIFT_CONFIGURATION=release \ + VOXIT_NATIVE_HOST_LOCKED=1 \ + VOXIT_NATIVE_HOST_SIGN_IDENTITY=- \ + ./scripts/build_and_run.sh stage + COMMON_ROOT="$(cd "$(git rev-parse --git-common-dir)/.." && pwd)" + APP_PATH="$COMMON_ROOT/target/voxit-native-host/Voxit.app" if [ ! -d "$APP_PATH" ]; then echo "Could not locate Voxit.app at $APP_PATH" exit 1 @@ -106,8 +100,8 @@ jobs: mkdir -p artifacts mv voxit-*/* artifacts/ cd artifacts - sha256sum * | tee ../SHA256 - md5sum * | tee ../MD5 + sha256sum ./* | tee ../SHA256 + md5sum ./* | tee ../MD5 mv ../SHA256 . mv ../MD5 . diff --git a/Cargo.lock b/Cargo.lock index 0c6181c..16b9a94 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3,5721 +3,1801 @@ version = 4 [[package]] -name = "ab_glyph" -version = "0.2.32" +name = "atomic-waker" +version = "1.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" + +[[package]] +name = "aws-lc-rs" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c0457472c38ea5bd1c3b5ada5e368271cb550be7a4ca4a0b4634e9913f6cc2" +checksum = "d9a7b350e3bb1767102698302bc37256cbd48422809984b98d292c40e2579aa9" dependencies = [ - "ab_glyph_rasterizer", - "owned_ttf_parser", + "aws-lc-sys", + "zeroize", ] [[package]] -name = "ab_glyph_rasterizer" -version = "0.1.10" +name = "aws-lc-sys" +version = "0.37.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "366ffbaa4442f4684d91e2cd7c5ea7c4ed8add41959a31447066e279e432b618" +checksum = "b092fe214090261288111db7a2b2c2118e5a7f30dc2569f1732c4069a6840549" +dependencies = [ + "cc", + "cmake", + "dunce", + "fs_extra", +] [[package]] -name = "accesskit" -version = "0.21.1" +name = "base64" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf203f9d3bd8f29f98833d1fbef628df18f759248a547e7e01cfbf63cda36a99" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] -name = "accesskit_atspi_common" -version = "0.14.2" +name = "bitflags" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "890d241cf51fc784f0ac5ac34dfc847421f8d39da6c7c91a0fcc987db62a8267" -dependencies = [ - "accesskit", - "accesskit_consumer", - "atspi-common", - "serde", - "thiserror 1.0.69", - "zvariant", -] +checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] -name = "accesskit_consumer" -version = "0.31.0" +name = "block-buffer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db81010a6895d8707f9072e6ce98070579b43b717193d2614014abd5cb17dd43" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "accesskit", - "hashbrown 0.15.5", + "generic-array", ] [[package]] -name = "accesskit_macos" -version = "0.22.2" +name = "block2" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0089e5c0ac0ca281e13ea374773898d9354cc28d15af9f0f7394d44a495b575" +checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" dependencies = [ - "accesskit", - "accesskit_consumer", - "hashbrown 0.15.5", - "objc2 0.5.2", - "objc2-app-kit 0.2.2", - "objc2-foundation 0.2.2", + "objc2", ] [[package]] -name = "accesskit_unix" -version = "0.17.2" +name = "bumpalo" +version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "301e55b39cfc15d9c48943ce5f572204a551646700d0e8efa424585f94fec528" -dependencies = [ - "accesskit", - "accesskit_atspi_common", - "async-channel", - "async-executor", - "async-task", - "atspi", - "futures-lite", - "futures-util", - "serde", - "zbus", -] +checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] -name = "accesskit_windows" -version = "0.29.2" +name = "bytes" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2d63dd5041e49c363d83f5419a896ecb074d309c414036f616dc0b04faca971" -dependencies = [ - "accesskit", - "accesskit_consumer", - "hashbrown 0.15.5", - "static_assertions", - "windows 0.61.3", - "windows-core 0.61.2", -] +checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] -name = "accesskit_winit" -version = "0.29.2" +name = "cc" +version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8cfabe59d0eaca7412bfb1f70198dd31e3b0496fee7e15b066f9c36a1a140a0" +checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ - "accesskit", - "accesskit_macos", - "accesskit_unix", - "accesskit_windows", - "raw-window-handle", - "winit", + "find-msvc-tools", + "jobserver", + "libc", + "shlex", ] [[package]] -name = "addr2line" -version = "0.25.1" +name = "cesu8" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" -dependencies = [ - "gimli", -] +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] -name = "adler2" -version = "2.0.1" +name = "cfg_aliases" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] -name = "ahash" -version = "0.8.12" +name = "cmake" +version = "0.1.57" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" dependencies = [ - "cfg-if", - "getrandom 0.3.4", - "once_cell", - "version_check", - "zerocopy", + "cc", ] [[package]] -name = "aho-corasick" -version = "1.1.4" +name = "combine" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ + "bytes", "memchr", ] [[package]] -name = "android-activity" -version = "0.6.0" +name = "core-foundation" +version = "0.9.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" dependencies = [ - "android-properties", - "bitflags 2.11.0", - "cc", - "cesu8", - "jni", - "jni-sys", + "core-foundation-sys", "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 = "android_system_properties" -version = "0.1.5" +name = "core-foundation" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" dependencies = [ + "core-foundation-sys", "libc", ] [[package]] -name = "anyhow" -version = "1.0.102" +name = "core-foundation-sys" +version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f202df86484c868dbad7eaa557ef785d5c66295e41b460ef922eca0723b842c" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] -name = "arboard" -version = "3.6.1" +name = "coreaudio-rs" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" +checksum = "d15c3c3cee7c087938f7ad1c3098840b3ef1f1bdc7f6e496336c3b1e7a6f3914" dependencies = [ - "clipboard-win", - "image", - "log", - "objc2 0.6.4", - "objc2-app-kit 0.3.2", + "bitflags", + "libc", + "objc2-audio-toolbox", + "objc2-core-audio", + "objc2-core-audio-types", "objc2-core-foundation", - "objc2-core-graphics", - "objc2-foundation 0.3.2", - "parking_lot", - "percent-encoding", - "windows-sys 0.60.2", - "x11rb", ] [[package]] -name = "arrayref" -version = "0.3.9" +name = "cpufeatures" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] [[package]] -name = "arrayvec" -version = "0.7.6" +name = "crypto-common" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +dependencies = [ + "generic-array", + "typenum", +] [[package]] -name = "as-raw-xcb-connection" -version = "1.0.1" +name = "data-encoding" +version = "2.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" +checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" [[package]] -name = "ash" -version = "0.38.0+1.3.281" +name = "digest" +version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb44936d800fea8f016d7f2311c6a4f97aebd5dc86f09906139ec848cf3a46f" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "libloading 0.8.9", + "block-buffer", + "crypto-common", ] [[package]] -name = "async-broadcast" -version = "0.7.2" +name = "directories" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" dependencies = [ - "event-listener", - "event-listener-strategy", - "futures-core", - "pin-project-lite", + "dirs-sys", ] [[package]] -name = "async-channel" -version = "2.5.0" +name = "dirs-sys" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", + "libc", + "option-ext", + "redox_users", + "windows-sys 0.61.2", ] [[package]] -name = "async-executor" -version = "1.14.0" +name = "dispatch2" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c96bf972d85afc50bf5ab8fe2d54d1586b4e0b46c97c50a0c9e71e2f7bcd812a" +checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "async-task", - "concurrent-queue", - "fastrand", - "futures-lite", - "pin-project-lite", - "slab", + "bitflags", + "objc2", ] [[package]] -name = "async-io" -version = "2.6.0" +name = "displaydoc" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite", - "parking", - "polling", - "rustix 1.1.4", - "slab", - "windows-sys 0.61.2", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "async-lock" -version = "3.4.2" +name = "dunce" +version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" -dependencies = [ - "event-listener", - "event-listener-strategy", - "pin-project-lite", -] +checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" [[package]] -name = "async-process" -version = "2.5.0" +name = "find-msvc-tools" +version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" -dependencies = [ - "async-channel", - "async-io", - "async-lock", - "async-signal", - "async-task", - "blocking", - "cfg-if", - "event-listener", - "futures-lite", - "rustix 1.1.4", -] +checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] -name = "async-recursion" -version = "1.1.1" +name = "form_urlencoded" +version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", + "percent-encoding", ] [[package]] -name = "async-signal" -version = "0.2.13" +name = "fs_extra" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" + +[[package]] +name = "futures-channel" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43c070bbf59cd3570b6b2dd54cd772527c7c3620fce8be898406dd3ed6adc64c" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ - "async-io", - "async-lock", - "atomic-waker", - "cfg-if", "futures-core", - "futures-io", - "rustix 1.1.4", - "signal-hook-registry", - "slab", - "windows-sys 0.61.2", + "futures-sink", ] [[package]] -name = "async-task" -version = "4.7.1" +name = "futures-core" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] -name = "async-trait" -version = "0.1.89" +name = "futures-io" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] -name = "atk" -version = "0.18.2" +name = "futures-macro" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "241b621213072e993be4f6f3a9e4b45f65b7e6faad43001be957184b7bb1824b" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ - "atk-sys", - "glib", - "libc", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "atk-sys" -version = "0.18.2" +name = "futures-sink" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e48b684b0ca77d2bbadeef17424c2ea3c897d44d566a1617e7e8f30614d086" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] -name = "atomic-waker" -version = "1.1.2" +name = "futures-task" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] -name = "atspi" -version = "0.25.0" +name = "futures-util" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c83247582e7508838caf5f316c00791eee0e15c0bf743e6880585b867e16815c" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ - "atspi-common", - "atspi-connection", - "atspi-proxies", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "slab", ] [[package]] -name = "atspi-common" -version = "0.9.0" +name = "generic-array" +version = "0.14.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33dfc05e7cdf90988a197803bf24f5788f94f7c94a69efa95683e8ffe76cfdfb" +checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" dependencies = [ - "enumflags2", - "serde", - "static_assertions", - "zbus", - "zbus-lockstep", - "zbus-lockstep-macros", - "zbus_names", - "zvariant", + "typenum", + "version_check", ] [[package]] -name = "atspi-connection" -version = "0.9.0" +name = "getrandom" +version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4193d51303d8332304056ae0004714256b46b6635a5c556109b319c0d3784938" +checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" dependencies = [ - "atspi-common", - "atspi-proxies", - "futures-lite", - "zbus", -] - + "cfg-if", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + [[package]] -name = "atspi-proxies" -version = "0.9.0" +name = "getrandom" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2eebcb9e7e76f26d0bcfd6f0295e1cd1e6f33bedbc5698a971db8dc43d7751c" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" dependencies = [ - "atspi-common", - "serde", - "zbus", + "cfg-if", + "js-sys", + "libc", + "r-efi", + "wasip2", + "wasm-bindgen", ] [[package]] -name = "autocfg" -version = "1.5.0" +name = "hound" +version = "3.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" [[package]] -name = "aws-lc-rs" -version = "1.16.0" +name = "http" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9a7b350e3bb1767102698302bc37256cbd48422809984b98d292c40e2579aa9" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" dependencies = [ - "aws-lc-sys", - "zeroize", + "bytes", + "itoa", ] [[package]] -name = "aws-lc-sys" -version = "0.37.1" +name = "http-body" +version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b092fe214090261288111db7a2b2c2118e5a7f30dc2569f1732c4069a6840549" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ - "cc", - "cmake", - "dunce", - "fs_extra", + "bytes", + "http", ] [[package]] -name = "backtrace" -version = "0.3.76" +name = "http-body-util" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ - "addr2line", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", - "windows-link 0.2.1", + "bytes", + "futures-core", + "http", + "http-body", + "pin-project-lite", ] [[package]] -name = "base64" -version = "0.22.1" +name = "httparse" +version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" +checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] -name = "bit-set" -version = "0.8.0" +name = "hyper" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08807e080ed7f9d5433fa9b275196cfc35414f66a0c79d864dc51a0d825231a3" +checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" dependencies = [ - "bit-vec", + "atomic-waker", + "bytes", + "futures-channel", + "futures-core", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "pin-utils", + "smallvec", + "tokio", + "want", ] [[package]] -name = "bit-vec" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e764a1d40d510daf35e07be9eb06e75770908c27d411ee6c92109c9840eaaf7" - -[[package]] -name = "bitflags" -version = "1.3.2" +name = "hyper-rustls" +version = "0.27.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +dependencies = [ + "http", + "hyper", + "hyper-util", + "rustls", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] [[package]] -name = "bitflags" -version = "2.11.0" +name = "hyper-util" +version = "0.1.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" dependencies = [ - "serde_core", + "base64", + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "ipnet", + "libc", + "percent-encoding", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", ] [[package]] -name = "block" -version = "0.1.6" +name = "icu_collections" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" +checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" +dependencies = [ + "displaydoc", + "potential_utf", + "yoke", + "zerofrom", + "zerovec", +] [[package]] -name = "block-buffer" -version = "0.10.4" +name = "icu_locale_core" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ - "generic-array", + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", ] [[package]] -name = "block2" -version = "0.5.1" +name = "icu_normalizer" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" +checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ - "objc2 0.5.2", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "zerovec", ] [[package]] -name = "block2" -version = "0.6.2" +name = "icu_normalizer_data" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cdeb9d870516001442e364c5220d3574d2da8dc765554b4a617230d33fa58ef5" -dependencies = [ - "objc2 0.6.4", -] +checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] -name = "blocking" -version = "1.6.2" +name = "icu_properties" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ - "async-channel", - "async-task", - "futures-io", - "futures-lite", - "piper", + "icu_collections", + "icu_locale_core", + "icu_properties_data", + "icu_provider", + "zerotrie", + "zerovec", ] [[package]] -name = "bumpalo" -version = "3.20.2" +name = "icu_properties_data" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] -name = "bytemuck" -version = "1.25.0" +name = "icu_provider" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c8efb64bd706a16a1bdde310ae86b351e4d21550d98d056f22f8a7f7a2183fec" +checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ - "bytemuck_derive", + "displaydoc", + "icu_locale_core", + "writeable", + "yoke", + "zerofrom", + "zerotrie", + "zerovec", ] [[package]] -name = "bytemuck_derive" -version = "1.10.2" +name = "idna" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9abbd1bc6865053c427f7198e6af43bfdedc55ab791faed4fbd361d789575ff" +checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", + "idna_adapter", + "smallvec", + "utf8_iter", ] [[package]] -name = "byteorder-lite" -version = "0.1.0" +name = "idna_adapter" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" +checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" +dependencies = [ + "icu_normalizer", + "icu_properties", +] [[package]] -name = "bytes" -version = "1.11.1" +name = "ipnet" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" +checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" [[package]] -name = "cairo-rs" -version = "0.18.5" +name = "iri-string" +version = "0.7.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" +checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" dependencies = [ - "bitflags 2.11.0", - "cairo-sys-rs", - "glib", - "libc", - "once_cell", - "thiserror 1.0.69", + "memchr", + "serde", ] [[package]] -name = "cairo-sys-rs" -version = "0.18.2" +name = "itoa" +version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "685c9fa8e590b8b3d678873528d83411db17242a73fccaed827770ea0fedda51" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] +checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] -name = "calloop" -version = "0.13.0" +name = "jni" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" dependencies = [ - "bitflags 2.11.0", + "cesu8", + "cfg-if", + "combine", + "jni-sys", "log", - "polling", - "rustix 0.38.44", - "slab", "thiserror 1.0.69", + "walkdir", + "windows-sys 0.45.0", ] [[package]] -name = "calloop" -version = "0.14.4" +name = "jni-sys" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4dbf9978365bac10f54d1d4b04f7ce4427e51f71d61f2fe15e3fed5166474df7" -dependencies = [ - "bitflags 2.11.0", - "polling", - "rustix 1.1.4", - "slab", - "tracing", -] +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" [[package]] -name = "calloop-wayland-source" -version = "0.3.0" +name = "jobserver" +version = "0.1.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" +checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" dependencies = [ - "calloop 0.13.0", - "rustix 0.38.44", - "wayland-backend", - "wayland-client", + "getrandom 0.3.4", + "libc", ] [[package]] -name = "calloop-wayland-source" -version = "0.4.1" +name = "js-sys" +version = "0.3.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "138efcf0940a02ebf0cc8d1eff41a1682a46b431630f4c52450d6265876021fa" +checksum = "14dc6f6450b3f6d4ed5b16327f38fed626d375a886159ca555bd7822c0c3a5a6" dependencies = [ - "calloop 0.14.4", - "rustix 1.1.4", - "wayland-backend", - "wayland-client", + "once_cell", + "wasm-bindgen", ] [[package]] -name = "camino" -version = "1.2.2" +name = "keyring" +version = "3.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629a66d692cb9ff1a1c664e41771b3dcaf961985a9774c0eb0bd1b51cf60a48" +checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c" dependencies = [ - "serde_core", + "log", + "security-framework 2.11.1", + "security-framework 3.7.0", + "zeroize", ] [[package]] -name = "cargo-platform" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87a0c0e6148f11f01f32650a2ea02d532b2ad4e81d8bd41e6e565b5adc5e6082" -dependencies = [ - "serde", - "serde_core", -] +name = "libc" +version = "0.2.182" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" [[package]] -name = "cargo_metadata" -version = "0.23.1" +name = "libredox" +version = "0.1.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef987d17b0a113becdd19d3d0022d04d7ef41f9efe4f3fb63ac44ba61df3ade9" +checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" dependencies = [ - "camino", - "cargo-platform", - "semver", - "serde", - "serde_json", - "thiserror 2.0.18", + "bitflags", + "libc", ] [[package]] -name = "cc" -version = "1.2.56" +name = "litemap" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" -dependencies = [ - "find-msvc-tools", - "jobserver", - "libc", - "shlex", -] +checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] -name = "cesu8" -version = "1.1.0" +name = "log" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] -name = "cfg-expr" -version = "0.15.8" +name = "lru-slab" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" -dependencies = [ - "smallvec", - "target-lexicon", -] +checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" [[package]] -name = "cfg-if" -version = "1.0.4" +name = "memchr" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" +checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] -name = "cfg_aliases" -version = "0.2.1" +name = "mime" +version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] -name = "cgl" -version = "0.3.2" +name = "mime_guess" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" dependencies = [ - "libc", + "mime", + "unicase", ] [[package]] -name = "clipboard-win" -version = "5.4.1" +name = "mio" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ - "error-code", + "libc", + "wasi", + "windows-sys 0.61.2", ] [[package]] -name = "cmake" -version = "0.1.57" +name = "ndk-context" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" -dependencies = [ - "cc", -] +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" [[package]] -name = "codespan-reporting" -version = "0.12.0" +name = "objc2" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe6d2e5af09e8c8ad56c969f2157a3d4238cebc7c55f0a517728c38f7b200f81" +checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" dependencies = [ - "serde", - "termcolor", - "unicode-width", + "objc2-encode", ] [[package]] -name = "color-eyre" -version = "0.6.5" +name = "objc2-app-kit" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5920befb47832a6d61ee3a3a846565cfa39b331331e68a3b1d1116630f2f26d" +checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ - "backtrace", - "color-spantrace", - "eyre", - "indenter", - "once_cell", - "owo-colors", - "tracing-error", + "bitflags", + "objc2", + "objc2-foundation", ] [[package]] -name = "color-spantrace" -version = "0.3.0" +name = "objc2-audio-toolbox" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8b88ea9df13354b55bc7234ebcce36e6ef896aca2e42a15de9e10edce01b427" +checksum = "6948501a91121d6399b79abaa33a8aa4ea7857fe019f341b8c23ad6e81b79b08" dependencies = [ - "once_cell", - "owo-colors", - "tracing-core", - "tracing-error", + "bitflags", + "block2", + "dispatch2", + "libc", + "objc2", + "objc2-core-audio", + "objc2-core-audio-types", + "objc2-core-foundation", + "objc2-foundation", ] [[package]] -name = "combine" -version = "4.6.7" +name = "objc2-av-foundation" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +checksum = "478ae33fcac9df0a18db8302387c666b8ef08a3e2d62b510ca4fc278a384b6c0" dependencies = [ - "bytes", - "memchr", + "bitflags", + "block2", + "objc2", + "objc2-foundation", ] [[package]] -name = "concurrent-queue" -version = "2.5.0" +name = "objc2-core-audio" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +checksum = "e1eebcea8b0dbff5f7c8504f3107c68fc061a3eb44932051c8cf8a68d969c3b2" dependencies = [ - "crossbeam-utils", + "dispatch2", + "objc2", + "objc2-core-audio-types", + "objc2-core-foundation", ] [[package]] -name = "core-foundation" -version = "0.9.4" +name = "objc2-core-audio-types" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +checksum = "5a89f2ec274a0cf4a32642b2991e8b351a404d290da87bb6a9a9d8632490bd1c" dependencies = [ - "core-foundation-sys", - "libc", + "bitflags", + "objc2", ] [[package]] -name = "core-foundation" -version = "0.10.1" +name = "objc2-core-foundation" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "core-foundation-sys", - "libc", + "bitflags", + "dispatch2", + "objc2", ] [[package]] -name = "core-foundation-sys" -version = "0.8.7" +name = "objc2-encode" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" [[package]] -name = "core-graphics" -version = "0.23.2" +name = "objc2-foundation" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.4", - "core-graphics-types 0.1.3", - "foreign-types", - "libc", + "bitflags", + "objc2", ] [[package]] -name = "core-graphics" -version = "0.25.0" +name = "objc2-local-authentication" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" +checksum = "e48e0b8b339e0d9d2ed4416b7f93f9d4daadff7d4dd797f89867cde11aeac607" dependencies = [ - "bitflags 2.11.0", - "core-foundation 0.10.1", - "core-graphics-types 0.2.0", - "foreign-types", - "libc", + "objc2", + "objc2-foundation", ] [[package]] -name = "core-graphics-types" -version = "0.1.3" +name = "once_cell" +version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" -dependencies = [ - "bitflags 1.3.2", - "core-foundation 0.9.4", - "libc", -] +checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" + +[[package]] +name = "openssl-probe" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] -name = "core-graphics-types" +name = "option-ext" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" -dependencies = [ - "bitflags 2.11.0", - "core-foundation 0.10.1", - "libc", -] +checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" [[package]] -name = "coreaudio-rs" -version = "0.14.0" +name = "percent-encoding" +version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d15c3c3cee7c087938f7ad1c3098840b3ef1f1bdc7f6e496336c3b1e7a6f3914" -dependencies = [ - "bitflags 2.11.0", - "libc", - "objc2-audio-toolbox", - "objc2-core-audio", - "objc2-core-audio-types", - "objc2-core-foundation", -] +checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] -name = "cpufeatures" +name = "pin-project-lite" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" -dependencies = [ - "libc", -] +checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] -name = "crc32fast" -version = "1.5.0" +name = "pin-utils" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9481c1c90cbf2ac953f07c8d4a58aa3945c425b7185c9154d67a65e4230da511" -dependencies = [ - "cfg-if", -] +checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" [[package]] -name = "crossbeam-channel" -version = "0.5.15" +name = "potential_utf" +version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" +checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ - "crossbeam-utils", + "zerovec", ] [[package]] -name = "crossbeam-utils" -version = "0.8.21" +name = "ppv-lite86" +version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" +checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" +dependencies = [ + "zerocopy", +] [[package]] -name = "crunchy" -version = "0.2.4" +name = "proc-macro2" +version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" +checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" +dependencies = [ + "unicode-ident", +] [[package]] -name = "crypto-common" -version = "0.1.7" +name = "quinn" +version = "0.11.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a" +checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" dependencies = [ - "generic-array", - "typenum", + "bytes", + "cfg_aliases", + "pin-project-lite", + "quinn-proto", + "quinn-udp", + "rustc-hash", + "rustls", + "socket2", + "thiserror 2.0.18", + "tokio", + "tracing", + "web-time", ] [[package]] -name = "cursor-icon" -version = "1.2.0" +name = "quinn-proto" +version = "0.11.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f27ae1dd37df86211c42e150270f82743308803d90a6f6e6651cd730d5e1732f" +checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" +dependencies = [ + "aws-lc-rs", + "bytes", + "getrandom 0.3.4", + "lru-slab", + "rand", + "ring", + "rustc-hash", + "rustls", + "rustls-pki-types", + "slab", + "thiserror 2.0.18", + "tinyvec", + "tracing", + "web-time", +] [[package]] -name = "darling" -version = "0.20.11" +name = "quinn-udp" +version = "0.5.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" +checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" dependencies = [ - "darling_core", - "darling_macro", + "cfg_aliases", + "libc", + "once_cell", + "socket2", + "tracing", + "windows-sys 0.60.2", ] [[package]] -name = "darling_core" -version = "0.20.11" +name = "quote" +version = "1.0.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" +checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" dependencies = [ - "fnv", - "ident_case", "proc-macro2", - "quote", - "strsim", - "syn 2.0.117", ] [[package]] -name = "darling_macro" -version = "0.20.11" +name = "r-efi" +version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" -dependencies = [ - "darling_core", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "data-encoding" -version = "2.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" - -[[package]] -name = "deranged" -version = "0.5.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd812cc2bc1d69d4764bd80df88b4317eaef9e773c75226407d9bc0876b211c" -dependencies = [ - "powerfmt", -] - -[[package]] -name = "derive_builder" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" -dependencies = [ - "derive_builder_macro", -] - -[[package]] -name = "derive_builder_core" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "derive_builder_macro" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" -dependencies = [ - "derive_builder_core", - "syn 2.0.117", -] - -[[package]] -name = "digest" -version = "0.10.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] - -[[package]] -name = "directories" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "16f5094c54661b38d03bd7e50df373292118db60b585c08a411c6d840017fe7d" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" -dependencies = [ - "dirs-sys", -] - -[[package]] -name = "dirs-sys" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" -dependencies = [ - "libc", - "option-ext", - "redox_users", - "windows-sys 0.61.2", -] - -[[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.0", - "objc2 0.6.4", -] +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" [[package]] -name = "displaydoc" -version = "0.2.5" +name = "rand" +version = "0.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", + "rand_chacha", + "rand_core", ] [[package]] -name = "dlib" -version = "0.5.3" +name = "rand_chacha" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab8ecd87370524b461f8557c119c405552c396ed91fc0a8eec68679eab26f94a" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" dependencies = [ - "libloading 0.8.9", + "ppv-lite86", + "rand_core", ] [[package]] -name = "document-features" -version = "0.2.12" +name = "rand_core" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" dependencies = [ - "litrs", + "getrandom 0.3.4", ] [[package]] -name = "downcast-rs" -version = "1.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" - -[[package]] -name = "dpi" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b14ccef22fc6f5a8f4d7d768562a182c04ce9a3b3157b91390b52ddfdf1a76" - -[[package]] -name = "dunce" -version = "1.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92773504d58c093f6de2459af4af33faa518c13451eb8f2b5698ed3d36e7c813" - -[[package]] -name = "ecolor" -version = "0.33.3" +name = "redox_users" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71ddb8ac7643d1dba1bb02110e804406dd459a838efcb14011ced10556711a8e" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ - "bytemuck", - "emath", + "getrandom 0.2.17", + "libredox", + "thiserror 2.0.18", ] [[package]] -name = "eframe" -version = "0.33.3" +name = "reqwest" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "457481173e6db5ca9fa2be93a58df8f4c7be639587aeb4853b526c6cf87db4e6" +checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" dependencies = [ - "ahash", - "bytemuck", - "document-features", - "egui", - "egui-wgpu", - "egui-winit", - "egui_glow", - "glow", - "glutin", - "glutin-winit", - "image", + "base64", + "bytes", + "futures-channel", + "futures-core", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-util", "js-sys", "log", - "objc2 0.5.2", - "objc2-app-kit 0.2.2", - "objc2-foundation 0.2.2", - "parking_lot", + "mime_guess", "percent-encoding", - "profiling", - "raw-window-handle", - "static_assertions", + "pin-project-lite", + "quinn", + "rustls", + "rustls-pki-types", + "rustls-platform-verifier", + "serde", + "serde_json", + "sync_wrapper", + "tokio", + "tokio-rustls", + "tower", + "tower-http", + "tower-service", + "url", "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "web-time", - "windows-sys 0.61.2", - "winit", ] [[package]] -name = "egui" -version = "0.33.3" +name = "ring" +version = "0.17.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a9b567d356674e9a5121ed3fedfb0a7c31e059fe71f6972b691bcd0bfc284e3" +checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" dependencies = [ - "accesskit", - "ahash", - "bitflags 2.11.0", - "emath", - "epaint", - "log", - "nohash-hasher", - "profiling", - "smallvec", - "unicode-segmentation", + "cc", + "cfg-if", + "getrandom 0.2.17", + "libc", + "untrusted", + "windows-sys 0.52.0", ] [[package]] -name = "egui-wgpu" -version = "0.33.3" +name = "rustc-hash" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e4d209971c84b2352a06174abdba701af1e552ce56b144d96f2bd50a3c91236" -dependencies = [ - "ahash", - "bytemuck", - "document-features", - "egui", - "epaint", - "log", - "profiling", - "thiserror 2.0.18", - "type-map", - "web-time", - "wgpu", - "winit", -] +checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] -name = "egui-winit" -version = "0.33.3" +name = "rustls" +version = "0.23.37" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec6687e5bb551702f4ad10ac428bab12acf9d53047ebb1082d4a0ed8c6251a29" +checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" dependencies = [ - "accesskit_winit", - "arboard", - "bytemuck", - "egui", - "log", - "objc2 0.5.2", - "objc2-foundation 0.2.2", - "objc2-ui-kit", - "profiling", - "raw-window-handle", - "smithay-clipboard", - "web-time", - "webbrowser", - "winit", + "aws-lc-rs", + "once_cell", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", ] [[package]] -name = "egui_glow" -version = "0.33.3" +name = "rustls-native-certs" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6420863ea1d90e750f75075231a260030ad8a9f30a7cef82cdc966492dc4c4eb" +checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" dependencies = [ - "bytemuck", - "egui", - "glow", - "log", - "memoffset", - "profiling", - "wasm-bindgen", - "web-sys", - "winit", + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework 3.7.0", ] [[package]] -name = "emath" -version = "0.33.3" +name = "rustls-pki-types" +version = "1.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "491bdf728bf25ddd9ad60d4cf1c48588fa82c013a2440b91aa7fc43e34a07c32" +checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" dependencies = [ - "bytemuck", + "web-time", + "zeroize", ] [[package]] -name = "endi" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" - -[[package]] -name = "enigo" -version = "0.6.1" +name = "rustls-platform-verifier" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71c6c56e50f7acae2906a0dcbb34529ca647e40421119ad5d12e7f8ba6e50010" +checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" dependencies = [ "core-foundation 0.10.1", - "core-graphics 0.25.0", - "foreign-types-shared", - "libc", + "core-foundation-sys", + "jni", "log", - "nom", - "objc2 0.6.4", - "objc2-app-kit 0.3.2", - "objc2-foundation 0.3.2", - "windows 0.61.3", - "x11rb", - "xkbcommon", - "xkeysym", -] - -[[package]] -name = "enumflags2" -version = "0.7.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1027f7680c853e056ebcec683615fb6fbbc07dbaa13b4d5d9442b146ded4ecef" -dependencies = [ - "enumflags2_derive", - "serde", + "once_cell", + "rustls", + "rustls-native-certs", + "rustls-platform-verifier-android", + "rustls-webpki", + "security-framework 3.7.0", + "security-framework-sys", + "webpki-root-certs", + "windows-sys 0.61.2", ] [[package]] -name = "enumflags2_derive" -version = "0.7.12" +name = "rustls-platform-verifier-android" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "67c78a4d8fdf9953a5c9d458f9efe940fd97a0cab0941c075a813ac594733827" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] +checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] -name = "epaint" -version = "0.33.3" +name = "rustls-webpki" +version = "0.103.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "009d0dd3c2163823a0abdb899451ecbc78798dec545ee91b43aff1fa790bab62" +checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" dependencies = [ - "ab_glyph", - "ahash", - "bytemuck", - "ecolor", - "emath", - "epaint_default_fonts", - "log", - "nohash-hasher", - "parking_lot", - "profiling", + "aws-lc-rs", + "ring", + "rustls-pki-types", + "untrusted", ] [[package]] -name = "epaint_default_fonts" -version = "0.33.3" +name = "rustversion" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c4fbe202b6578d3d56428fa185cdf114a05e49da05f477b3c7f0fbb221f1862" +checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] -name = "equivalent" -version = "1.0.2" +name = "same-file" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] [[package]] -name = "errno" -version = "0.3.14" +name = "schannel" +version = "0.1.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" +checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" dependencies = [ - "libc", "windows-sys 0.61.2", ] [[package]] -name = "error-code" -version = "3.3.2" +name = "security-framework" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" - -[[package]] -name = "event-listener" -version = "5.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" -dependencies = [ - "concurrent-queue", - "parking", - "pin-project-lite", -] - -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener", - "pin-project-lite", -] - -[[package]] -name = "eyre" -version = "0.6.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cd915d99f24784cdc19fd37ef22b97e3ff0ae756c7e492e9fbfe897d61e2aec" -dependencies = [ - "indenter", - "once_cell", -] - -[[package]] -name = "fastrand" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" - -[[package]] -name = "fax" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" -dependencies = [ - "fax_derive", -] - -[[package]] -name = "fax_derive" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "fdeflate" -version = "0.3.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" -dependencies = [ - "simd-adler32", -] - -[[package]] -name = "field-offset" -version = "0.3.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38e2275cc4e4fc009b0669731a1e5ab7ebf11f469eaede2bab9309a5b4d6057f" -dependencies = [ - "memoffset", - "rustc_version", -] - -[[package]] -name = "find-msvc-tools" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" - -[[package]] -name = "flate2" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c" -dependencies = [ - "crc32fast", - "miniz_oxide", -] - -[[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "foldhash" -version = "0.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" - -[[package]] -name = "foldhash" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" - -[[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 2.0.117", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42703706b716c37f96a77aea830392ad231f44c9e9a67872fa5548707e11b11c" - -[[package]] -name = "futures-channel" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" -dependencies = [ - "futures-core", - "futures-sink", -] - -[[package]] -name = "futures-core" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" - -[[package]] -name = "futures-executor" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" -dependencies = [ - "futures-core", - "futures-task", - "futures-util", -] - -[[package]] -name = "futures-io" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" - -[[package]] -name = "futures-lite" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f78e10609fe0e0b3f4157ffab1876319b5b0db102a2c60dc4626306dc46b44ad" -dependencies = [ - "fastrand", - "futures-core", - "futures-io", - "parking", - "pin-project-lite", -] - -[[package]] -name = "futures-macro" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "futures-sink" -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-io", - "futures-macro", - "futures-sink", - "futures-task", - "memchr", - "pin-project-lite", - "slab", -] - -[[package]] -name = "gdk" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9f245958c627ac99d8e529166f9823fb3b838d1d41fd2b297af3075093c2691" -dependencies = [ - "cairo-rs", - "gdk-pixbuf", - "gdk-sys", - "gio", - "glib", - "libc", - "pango", -] - -[[package]] -name = "gdk-pixbuf" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50e1f5f1b0bfb830d6ccc8066d18db35c487b1b2b1e8589b5dfe9f07e8defaec" -dependencies = [ - "gdk-pixbuf-sys", - "gio", - "glib", - "libc", - "once_cell", -] - -[[package]] -name = "gdk-pixbuf-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9839ea644ed9c97a34d129ad56d38a25e6756f99f3a88e15cd39c20629caf7" -dependencies = [ - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gdk-sys" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c2d13f38594ac1e66619e188c6d5a1adb98d11b2fcf7894fc416ad76aa2f3f7" -dependencies = [ - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "pkg-config", - "system-deps", -] - -[[package]] -name = "generic-array" -version = "0.14.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" -dependencies = [ - "typenum", - "version_check", -] - -[[package]] -name = "gethostname" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" -dependencies = [ - "rustix 1.1.4", - "windows-link 0.2.1", -] - -[[package]] -name = "getrandom" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff2abc00be7fca6ebc474524697ae276ad847ad0a6b3faa4bcb027e9a4614ad0" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "wasi", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" -dependencies = [ - "cfg-if", - "js-sys", - "libc", - "r-efi", - "wasip2", - "wasm-bindgen", -] - -[[package]] -name = "getrandom" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "139ef39800118c7683f2fd3c98c1b23c09ae076556b435f8e9064ae108aaeeec" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "cfg-if", + "bitflags", + "core-foundation 0.9.4", + "core-foundation-sys", "libc", - "r-efi", - "wasip2", - "wasip3", -] - -[[package]] -name = "gimli" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" - -[[package]] -name = "gio" -version = "0.18.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4fc8f532f87b79cbc51a79748f16a6828fb784be93145a322fa14d06d354c73" -dependencies = [ - "futures-channel", - "futures-core", - "futures-io", - "futures-util", - "gio-sys", - "glib", - "libc", - "once_cell", - "pin-project-lite", - "smallvec", - "thiserror 1.0.69", -] - -[[package]] -name = "gio-sys" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37566df850baf5e4cb0dfb78af2e4b9898d817ed9263d1090a2df958c64737d2" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", - "winapi", -] - -[[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 = "glib" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" -dependencies = [ - "bitflags 2.11.0", - "futures-channel", - "futures-core", - "futures-executor", - "futures-task", - "futures-util", - "gio-sys", - "glib-macros", - "glib-sys", - "gobject-sys", - "libc", - "memchr", - "once_cell", - "smallvec", - "thiserror 1.0.69", -] - -[[package]] -name = "glib-macros" -version = "0.18.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" -dependencies = [ - "heck 0.4.1", - "proc-macro-crate 2.0.2", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "glib-sys" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "063ce2eb6a8d0ea93d2bf8ba1957e78dbab6be1c2220dd3daca57d5a9d869898" -dependencies = [ - "libc", - "system-deps", -] - -[[package]] -name = "global-hotkey" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9247516746aa8e53411a0db9b62b0e24efbcf6a76e0ba73e5a91b512ddabed7" -dependencies = [ - "crossbeam-channel", - "keyboard-types", - "objc2 0.6.4", - "objc2-app-kit 0.3.2", - "once_cell", - "thiserror 2.0.18", - "windows-sys 0.59.0", - "x11rb", - "xkeysym", -] - -[[package]] -name = "glow" -version = "0.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5e5ea60d70410161c8bf5da3fdfeaa1c72ed2c15f8bbb9d19fe3a4fad085f08" -dependencies = [ - "js-sys", - "slotmap", - "wasm-bindgen", - "web-sys", -] - -[[package]] -name = "glutin" -version = "0.32.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12124de845cacfebedff80e877bb37b5b75c34c5a4c89e47e1cdd67fb6041325" -dependencies = [ - "bitflags 2.11.0", - "cfg_aliases", - "cgl", - "dispatch2", - "glutin_egl_sys", - "glutin_glx_sys", - "glutin_wgl_sys", - "libloading 0.8.9", - "objc2 0.6.4", - "objc2-app-kit 0.3.2", - "objc2-core-foundation", - "objc2-foundation 0.3.2", - "once_cell", - "raw-window-handle", - "wayland-sys", - "windows-sys 0.52.0", - "x11-dl", -] - -[[package]] -name = "glutin-winit" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85edca7075f8fc728f28cb8fbb111a96c3b89e930574369e3e9c27eb75d3788f" -dependencies = [ - "cfg_aliases", - "glutin", - "raw-window-handle", - "winit", -] - -[[package]] -name = "glutin_egl_sys" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c4680ba6195f424febdc3ba46e7a42a0e58743f2edb115297b86d7f8ecc02d2" -dependencies = [ - "gl_generator", - "windows-sys 0.52.0", -] - -[[package]] -name = "glutin_glx_sys" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a7bb2938045a88b612499fbcba375a77198e01306f52272e692f8c1f3751185" -dependencies = [ - "gl_generator", - "x11-dl", -] - -[[package]] -name = "glutin_wgl_sys" -version = "0.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c4ee00b289aba7a9e5306d57c2d05499b2e5dc427f84ac708bd2c090212cf3e" -dependencies = [ - "gl_generator", -] - -[[package]] -name = "gobject-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0850127b514d1c4a4654ead6dedadb18198999985908e6ffe4436f53c785ce44" -dependencies = [ - "glib-sys", - "libc", - "system-deps", -] - -[[package]] -name = "gpu-alloc" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fbcd2dba93594b227a1f57ee09b8b9da8892c34d55aa332e034a228d0fe6a171" -dependencies = [ - "bitflags 2.11.0", - "gpu-alloc-types", -] - -[[package]] -name = "gpu-alloc-types" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98ff03b468aa837d70984d55f5d3f846f6ec31fe34bbb97c4f85219caeee1ca4" -dependencies = [ - "bitflags 2.11.0", -] - -[[package]] -name = "gpu-allocator" -version = "0.27.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c151a2a5ef800297b4e79efa4f4bec035c5f51d5ae587287c9b952bdf734cacd" -dependencies = [ - "log", - "presser", - "thiserror 1.0.69", - "windows 0.58.0", -] - -[[package]] -name = "gpu-descriptor" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b89c83349105e3732062a895becfc71a8f921bb71ecbbdd8ff99263e3b53a0ca" -dependencies = [ - "bitflags 2.11.0", - "gpu-descriptor-types", - "hashbrown 0.15.5", -] - -[[package]] -name = "gpu-descriptor-types" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdf242682df893b86f33a73828fb09ca4b2d3bb6cc95249707fc684d27484b91" -dependencies = [ - "bitflags 2.11.0", -] - -[[package]] -name = "gtk" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" -dependencies = [ - "atk", - "cairo-rs", - "field-offset", - "futures-channel", - "gdk", - "gdk-pixbuf", - "gio", - "glib", - "gtk-sys", - "gtk3-macros", - "libc", - "pango", - "pkg-config", -] - -[[package]] -name = "gtk-sys" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" -dependencies = [ - "atk-sys", - "cairo-sys-rs", - "gdk-pixbuf-sys", - "gdk-sys", - "gio-sys", - "glib-sys", - "gobject-sys", - "libc", - "pango-sys", - "system-deps", -] - -[[package]] -name = "gtk3-macros" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ff3c5b21f14f0736fed6dcfc0bfb4225ebf5725f3c0209edeec181e4d73e9d" -dependencies = [ - "proc-macro-crate 1.3.1", - "proc-macro-error", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "half" -version = "2.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" -dependencies = [ - "cfg-if", - "crunchy", - "num-traits", - "zerocopy", -] - -[[package]] -name = "hashbrown" -version = "0.15.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" -dependencies = [ - "foldhash 0.1.5", -] - -[[package]] -name = "hashbrown" -version = "0.16.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" -dependencies = [ - "foldhash 0.2.0", -] - -[[package]] -name = "heck" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" - -[[package]] -name = "heck" -version = "0.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" - -[[package]] -name = "hermit-abi" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc0fef456e4baa96da950455cd02c081ca953b141298e41db3fc7e36b1da849c" - -[[package]] -name = "hex" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" - -[[package]] -name = "hexf-parse" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfa686283ad6dd069f105e5ab091b04c62850d3e4cf5d67debad1933f55023df" - -[[package]] -name = "hound" -version = "3.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62adaabb884c94955b19907d60019f4e145d091c75345379e70d1ee696f7854f" - -[[package]] -name = "http" -version = "1.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" -dependencies = [ - "bytes", - "itoa", -] - -[[package]] -name = "http-body" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" -dependencies = [ - "bytes", - "http", -] - -[[package]] -name = "http-body-util" -version = "0.1.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" -dependencies = [ - "bytes", - "futures-core", - "http", - "http-body", - "pin-project-lite", -] - -[[package]] -name = "httparse" -version = "1.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" - -[[package]] -name = "hyper" -version = "1.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" -dependencies = [ - "atomic-waker", - "bytes", - "futures-channel", - "futures-core", - "http", - "http-body", - "httparse", - "itoa", - "pin-project-lite", - "pin-utils", - "smallvec", - "tokio", - "want", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" -dependencies = [ - "http", - "hyper", - "hyper-util", - "rustls", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower-service", -] - -[[package]] -name = "hyper-util" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96547c2556ec9d12fb1578c4eaf448b04993e7fb79cbaad930a656880a6bdfa0" -dependencies = [ - "base64", - "bytes", - "futures-channel", - "futures-util", - "http", - "http-body", - "hyper", - "ipnet", - "libc", - "percent-encoding", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", -] - -[[package]] -name = "icu_collections" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" -dependencies = [ - "displaydoc", - "potential_utf", - "yoke", - "zerofrom", - "zerovec", -] - -[[package]] -name = "icu_locale_core" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" -dependencies = [ - "displaydoc", - "litemap", - "tinystr", - "writeable", - "zerovec", -] - -[[package]] -name = "icu_normalizer" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" -dependencies = [ - "icu_collections", - "icu_normalizer_data", - "icu_properties", - "icu_provider", - "smallvec", - "zerovec", -] - -[[package]] -name = "icu_normalizer_data" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" - -[[package]] -name = "icu_properties" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" -dependencies = [ - "icu_collections", - "icu_locale_core", - "icu_properties_data", - "icu_provider", - "zerotrie", - "zerovec", -] - -[[package]] -name = "icu_properties_data" -version = "2.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" - -[[package]] -name = "icu_provider" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" -dependencies = [ - "displaydoc", - "icu_locale_core", - "writeable", - "yoke", - "zerofrom", - "zerotrie", - "zerovec", -] - -[[package]] -name = "id-arena" -version = "2.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d3067d79b975e8844ca9eb072e16b31c3c1c36928edf9c6789548c524d0d954" - -[[package]] -name = "ident_case" -version = "1.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" - -[[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.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e6506c6c10786659413faa717ceebcb8f70731c0a60cbae39795fdf114519c1a" -dependencies = [ - "bytemuck", - "byteorder-lite", - "moxcms", - "num-traits", - "png 0.18.1", - "tiff", -] - -[[package]] -name = "indenter" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "964de6e86d545b246d84badc0fef527924ace5134f30641c203ef52ba83f58d5" - -[[package]] -name = "indexmap" -version = "2.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" -dependencies = [ - "equivalent", - "hashbrown 0.16.1", - "serde", - "serde_core", -] - -[[package]] -name = "ipnet" -version = "2.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "469fb0b9cefa57e3ef31275ee7cacb78f2fdca44e4765491884a2b119d4eb130" - -[[package]] -name = "iri-string" -version = "0.7.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91338f0783edbd6195decb37bae672fd3b165faffb89bf7b9e6942f8b1a731a" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "itoa" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" - -[[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", - "log", - "thiserror 1.0.69", - "walkdir", - "windows-sys 0.45.0", -] - -[[package]] -name = "jni-sys" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" - -[[package]] -name = "jobserver" -version = "0.1.34" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9afb3de4395d6b3e67a780b6de64b51c978ecf11cb9a462c66be7d4ca9039d33" -dependencies = [ - "getrandom 0.3.4", - "libc", -] - -[[package]] -name = "js-sys" -version = "0.3.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14dc6f6450b3f6d4ed5b16327f38fed626d375a886159ca555bd7822c0c3a5a6" -dependencies = [ - "once_cell", - "wasm-bindgen", -] - -[[package]] -name = "keyboard-types" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" -dependencies = [ - "bitflags 2.11.0", - "serde", - "unicode-segmentation", -] - -[[package]] -name = "keyring" -version = "3.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eebcc3aff044e5944a8fbaf69eb277d11986064cba30c468730e8b9909fb551c" -dependencies = [ - "log", - "security-framework 2.11.1", - "security-framework 3.7.0", - "zeroize", -] - -[[package]] -name = "khronos-egl" -version = "6.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6aae1df220ece3c0ada96b8153459b67eebe9ae9212258bb0134ae60416fdf76" -dependencies = [ - "libc", - "libloading 0.8.9", - "pkg-config", -] - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" - -[[package]] -name = "leb128fmt" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09edd9e8b54e49e587e4f6295a7d29c3ea94d469cb40ab8ca70b288248a81db2" - -[[package]] -name = "libappindicator" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03589b9607c868cc7ae54c0b2a22c8dc03dd41692d48f2d7df73615c6a95dc0a" -dependencies = [ - "glib", - "gtk", - "gtk-sys", - "libappindicator-sys", - "log", -] - -[[package]] -name = "libappindicator-sys" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e9ec52138abedcc58dc17a7c6c0c00a2bdb4f3427c7f63fa97fd0d859155caf" -dependencies = [ - "gtk-sys", - "libloading 0.7.4", - "once_cell", -] - -[[package]] -name = "libc" -version = "0.2.182" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6800badb6cb2082ffd7b6a67e6125bb39f18782f793520caee8cb8846be06112" - -[[package]] -name = "libloading" -version = "0.7.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f" -dependencies = [ - "cfg-if", - "winapi", -] - -[[package]] -name = "libloading" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7c4b02199fee7c5d21a5ae7d8cfa79a6ef5bb2fc834d6e9058e89c825efdc55" -dependencies = [ - "cfg-if", - "windows-link 0.2.1", -] - -[[package]] -name = "libm" -version = "0.2.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" - -[[package]] -name = "libredox" -version = "0.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d0b95e02c851351f877147b7deea7b1afb1df71b63aa5f8270716e0c5720616" -dependencies = [ - "bitflags 2.11.0", - "libc", - "redox_syscall 0.7.3", -] - -[[package]] -name = "libxdo" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00333b8756a3d28e78def82067a377de7fa61b24909000aeaa2b446a948d14db" -dependencies = [ - "libxdo-sys", -] - -[[package]] -name = "libxdo-sys" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db23b9e7e2b7831bbd8aac0bbeeeb7b68cbebc162b227e7052e8e55829a09212" -dependencies = [ - "libc", - "x11", -] - -[[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.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" - -[[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" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "lru-slab" -version = "0.1.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "112b39cec0b298b6c1999fee3e31427f74f676e4cb9879ed1a121b43661a4154" - -[[package]] -name = "malloc_buf" -version = "0.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" -dependencies = [ - "libc", -] - -[[package]] -name = "matchers" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d1525a2a28c7f4fa0fc98bb91ae755d1e2d1505079e05539e35bc876b5d65ae9" -dependencies = [ - "regex-automata", -] - -[[package]] -name = "memchr" -version = "2.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" - -[[package]] -name = "memmap2" -version = "0.9.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "714098028fe011992e1c3962653c96b2d578c4b4bce9036e15ff220319b1e0e3" -dependencies = [ - "libc", -] - -[[package]] -name = "memoffset" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" -dependencies = [ - "autocfg", -] - -[[package]] -name = "metal" -version = "0.32.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00c15a6f673ff72ddcc22394663290f870fb224c1bfce55734a75c414150e605" -dependencies = [ - "bitflags 2.11.0", - "block", - "core-graphics-types 0.2.0", - "foreign-types", - "log", - "objc", - "paste", -] - -[[package]] -name = "mime" -version = "0.3.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" - -[[package]] -name = "mime_guess" -version = "2.0.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" -dependencies = [ - "mime", - "unicase", -] - -[[package]] -name = "miniz_oxide" -version = "0.8.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" -dependencies = [ - "adler2", - "simd-adler32", -] - -[[package]] -name = "mio" -version = "1.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" -dependencies = [ - "libc", - "wasi", - "windows-sys 0.61.2", -] - -[[package]] -name = "moxcms" -version = "0.7.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac9557c559cd6fc9867e122e20d2cbefc9ca29d80d027a8e39310920ed2f0a97" -dependencies = [ - "num-traits", - "pxfm", -] - -[[package]] -name = "muda" -version = "0.17.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "01c1738382f66ed56b3b9c8119e794a2e23148ac8ea214eda86622d4cb9d415a" -dependencies = [ - "crossbeam-channel", - "dpi", - "gtk", - "keyboard-types", - "libxdo", - "objc2 0.6.4", - "objc2-app-kit 0.3.2", - "objc2-core-foundation", - "objc2-foundation 0.3.2", - "once_cell", - "png 0.17.16", - "thiserror 2.0.18", - "windows-sys 0.60.2", -] - -[[package]] -name = "naga" -version = "27.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "066cf25f0e8b11ee0df221219010f213ad429855f57c494f995590c861a9a7d8" -dependencies = [ - "arrayvec", - "bit-set", - "bitflags 2.11.0", - "cfg-if", - "cfg_aliases", - "codespan-reporting", - "half", - "hashbrown 0.16.1", - "hexf-parse", - "indexmap", - "libm", - "log", - "num-traits", - "once_cell", - "rustc-hash 1.1.0", - "spirv", - "thiserror 2.0.18", - "unicode-ident", -] - -[[package]] -name = "ndk" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" -dependencies = [ - "bitflags 2.11.0", - "jni-sys", - "log", - "ndk-sys", - "num_enum", - "raw-window-handle", - "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.6.0+11769913" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" -dependencies = [ - "jni-sys", -] - -[[package]] -name = "nohash-hasher" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bf50223579dc7cdcfb3bfcacf7069ff68243f8c363f62ffa99cf000a6b9c451" - -[[package]] -name = "nom" -version = "8.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" -dependencies = [ - "memchr", -] - -[[package]] -name = "nu-ansi-term" -version = "0.50.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5" -dependencies = [ - "windows-sys 0.61.2", -] - -[[package]] -name = "num-conv" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf97ec579c3c42f953ef76dbf8d55ac91fb219dde70e49aa4a6b7d74e9919050" - -[[package]] -name = "num-traits" -version = "0.2.19" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" -dependencies = [ - "autocfg", - "libm", -] - -[[package]] -name = "num_enum" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b1207a7e20ad57b847bbddc6776b968420d38292bbfe2089accff5e19e82454c" -dependencies = [ - "num_enum_derive", - "rustversion", -] - -[[package]] -name = "num_enum_derive" -version = "0.7.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff32365de1b6743cb203b710788263c44a03de03802daf96092f2da4fe6ba4d7" -dependencies = [ - "proc-macro-crate 3.4.0", - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "num_threads" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9" -dependencies = [ - "libc", -] - -[[package]] -name = "objc" -version = "0.2.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "915b1b472bc21c53464d6c8461c9d3af805ba1ef837e1cac254428f4a77177b1" -dependencies = [ - "malloc_buf", -] - -[[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.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46a785d4eeff09c14c487497c162e92766fbb3e4059a71840cecc03d9a50b804" -dependencies = [ - "objc-sys", - "objc2-encode", -] - -[[package]] -name = "objc2" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a12a8ed07aefc768292f076dc3ac8c48f3781c8f2d5851dd3d98950e8c5a89f" -dependencies = [ - "objc2-encode", -] - -[[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.0", - "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.0", - "objc2 0.6.4", - "objc2-core-foundation", - "objc2-core-graphics", - "objc2-foundation 0.3.2", -] - -[[package]] -name = "objc2-audio-toolbox" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6948501a91121d6399b79abaa33a8aa4ea7857fe019f341b8c23ad6e81b79b08" -dependencies = [ - "bitflags 2.11.0", - "block2 0.6.2", - "dispatch2", - "libc", - "objc2 0.6.4", - "objc2-core-audio", - "objc2-core-audio-types", - "objc2-core-foundation", - "objc2-foundation 0.3.2", -] - -[[package]] -name = "objc2-av-foundation" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "478ae33fcac9df0a18db8302387c666b8ef08a3e2d62b510ca4fc278a384b6c0" -dependencies = [ - "bitflags 2.11.0", - "block2 0.6.2", - "objc2 0.6.4", - "objc2-foundation 0.3.2", -] - -[[package]] -name = "objc2-cloud-kit" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" -dependencies = [ - "bitflags 2.11.0", - "block2 0.5.1", - "objc2 0.5.2", - "objc2-core-location", - "objc2-foundation 0.2.2", -] - -[[package]] -name = "objc2-contacts" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" -dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation 0.2.2", -] - -[[package]] -name = "objc2-core-audio" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e1eebcea8b0dbff5f7c8504f3107c68fc061a3eb44932051c8cf8a68d969c3b2" -dependencies = [ - "dispatch2", - "objc2 0.6.4", - "objc2-core-audio-types", - "objc2-core-foundation", -] - -[[package]] -name = "objc2-core-audio-types" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a89f2ec274a0cf4a32642b2991e8b351a404d290da87bb6a9a9d8632490bd1c" -dependencies = [ - "bitflags 2.11.0", - "objc2 0.6.4", -] - -[[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.0", - "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.0", - "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.0", - "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-core-location" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" -dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", - "objc2-contacts", - "objc2-foundation 0.2.2", -] - -[[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.0", - "block2 0.5.1", - "dispatch", - "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.0", - "block2 0.6.2", - "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.0", - "objc2 0.6.4", - "objc2-core-foundation", -] - -[[package]] -name = "objc2-link-presentation" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" -dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", - "objc2-app-kit 0.2.2", - "objc2-foundation 0.2.2", -] - -[[package]] -name = "objc2-local-authentication" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e48e0b8b339e0d9d2ed4416b7f93f9d4daadff7d4dd797f89867cde11aeac607" -dependencies = [ - "objc2 0.6.4", - "objc2-foundation 0.3.2", -] - -[[package]] -name = "objc2-metal" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" -dependencies = [ - "bitflags 2.11.0", - "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.0", - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation 0.2.2", - "objc2-metal", -] - -[[package]] -name = "objc2-symbols" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" -dependencies = [ - "objc2 0.5.2", - "objc2-foundation 0.2.2", -] - -[[package]] -name = "objc2-ui-kit" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" -dependencies = [ - "bitflags 2.11.0", - "block2 0.5.1", - "objc2 0.5.2", - "objc2-cloud-kit", - "objc2-core-data", - "objc2-core-image", - "objc2-core-location", - "objc2-foundation 0.2.2", - "objc2-link-presentation", - "objc2-quartz-core", - "objc2-symbols", - "objc2-uniform-type-identifiers", - "objc2-user-notifications", -] - -[[package]] -name = "objc2-uniform-type-identifiers" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" -dependencies = [ - "block2 0.5.1", - "objc2 0.5.2", - "objc2-foundation 0.2.2", -] - -[[package]] -name = "objc2-user-notifications" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" -dependencies = [ - "bitflags 2.11.0", - "block2 0.5.1", - "objc2 0.5.2", - "objc2-core-location", - "objc2-foundation 0.2.2", -] - -[[package]] -name = "object" -version = "0.37.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" -dependencies = [ - "memchr", -] - -[[package]] -name = "once_cell" -version = "1.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" - -[[package]] -name = "openssl-probe" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" - -[[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.50" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52ad2c6bae700b7aa5d1cc30c59bdd3a1c180b09dbaea51e2ae2b8e1cf211fdd" -dependencies = [ - "libc", - "libredox", -] - -[[package]] -name = "ordered-float" -version = "5.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4779c6901a562440c3786d08192c6fbda7c1c2060edd10006b05ee35d10f2d" -dependencies = [ - "num-traits", -] - -[[package]] -name = "ordered-stream" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aa2b01e1d916879f73a53d01d1d6cee68adbb31d6d9177a8cfce093cced1d50" -dependencies = [ - "futures-core", - "pin-project-lite", -] - -[[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 = "owo-colors" -version = "4.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d211803b9b6b570f68772237e415a029d5a50c65d382910b879fb19d3271f94d" - -[[package]] -name = "pango" -version = "0.18.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ca27ec1eb0457ab26f3036ea52229edbdb74dee1edd29063f5b9b010e7ebee4" -dependencies = [ - "gio", - "glib", - "libc", - "once_cell", - "pango-sys", -] - -[[package]] -name = "pango-sys" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "436737e391a843e5933d6d9aa102cb126d501e815b83601365a948a518555dc5" -dependencies = [ - "glib-sys", - "gobject-sys", - "libc", - "system-deps", -] - -[[package]] -name = "parking" -version = "2.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" - -[[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 0.2.1", -] - -[[package]] -name = "paste" -version = "1.0.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" - -[[package]] -name = "percent-encoding" -version = "2.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" - -[[package]] -name = "pin-project" -version = "1.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "pin-project-lite" -version = "0.2.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" - -[[package]] -name = "pin-utils" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" - -[[package]] -name = "piper" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96c8c490f422ef9a4efd2cb5b42b76c8613d7e7dfc1caf667b8a3350a5acc066" -dependencies = [ - "atomic-waker", - "fastrand", - "futures-io", -] - -[[package]] -name = "pkg-config" -version = "0.3.32" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" - -[[package]] -name = "png" -version = "0.17.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - -[[package]] -name = "png" -version = "0.18.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" -dependencies = [ - "bitflags 2.11.0", - "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 = "portable-atomic" -version = "1.13.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" - -[[package]] -name = "portable-atomic-util" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a9db96d7fa8782dd8c15ce32ffe8680bbd1e978a43bf51a34d39483540495f5" -dependencies = [ - "portable-atomic", -] - -[[package]] -name = "potential_utf" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" -dependencies = [ - "zerovec", -] - -[[package]] -name = "powerfmt" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" - -[[package]] -name = "ppv-lite86" -version = "0.2.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85eae3c4ed2f50dcfe72643da4befc30deadb458a9b590d720cde2f2b1e97da9" -dependencies = [ - "zerocopy", -] - -[[package]] -name = "presser" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8cf8e6a8aa66ce33f63993ffc4ea4271eb5b0530a9002db8455ea6050c77bfa" - -[[package]] -name = "prettyplease" -version = "0.2.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "479ca8adacdd7ce8f1fb39ce9ecccbfe93a3f1344b3d0d97f20bc0196208f62b" -dependencies = [ - "proc-macro2", - "syn 2.0.117", -] - -[[package]] -name = "proc-macro-crate" -version = "1.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" -dependencies = [ - "once_cell", - "toml_edit 0.19.15", -] - -[[package]] -name = "proc-macro-crate" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" -dependencies = [ - "toml_datetime 0.6.3", - "toml_edit 0.20.2", -] - -[[package]] -name = "proc-macro-crate" -version = "3.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" -dependencies = [ - "toml_edit 0.23.10+spec-1.0.0", -] - -[[package]] -name = "proc-macro-error" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c" -dependencies = [ - "proc-macro-error-attr", - "proc-macro2", - "quote", - "syn 1.0.109", - "version_check", -] - -[[package]] -name = "proc-macro-error-attr" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869" -dependencies = [ - "proc-macro2", - "quote", - "version_check", -] - -[[package]] -name = "proc-macro2" -version = "1.0.106" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" -dependencies = [ - "unicode-ident", -] - -[[package]] -name = "profiling" -version = "1.0.17" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3eb8486b569e12e2c32ad3e204dbaba5e4b5b216e9367044f25f1dba42341773" - -[[package]] -name = "pxfm" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7186d3822593aa4393561d186d1393b3923e9d6163d3fbfd6e825e3e6cf3e6a8" -dependencies = [ - "num-traits", -] - -[[package]] -name = "quick-error" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" - -[[package]] -name = "quick-xml" -version = "0.38.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" -dependencies = [ - "memchr", - "serde", -] - -[[package]] -name = "quinn" -version = "0.11.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e20a958963c291dc322d98411f541009df2ced7b5a4f2bd52337638cfccf20" -dependencies = [ - "bytes", - "cfg_aliases", - "pin-project-lite", - "quinn-proto", - "quinn-udp", - "rustc-hash 2.1.1", - "rustls", - "socket2", - "thiserror 2.0.18", - "tokio", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-proto" -version = "0.11.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1906b49b0c3bc04b5fe5d86a77925ae6524a19b816ae38ce1e426255f1d8a31" -dependencies = [ - "aws-lc-rs", - "bytes", - "getrandom 0.3.4", - "lru-slab", - "rand", - "ring", - "rustc-hash 2.1.1", - "rustls", - "rustls-pki-types", - "slab", - "thiserror 2.0.18", - "tinyvec", - "tracing", - "web-time", -] - -[[package]] -name = "quinn-udp" -version = "0.5.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "addec6a0dcad8a8d96a771f815f0eaf55f9d1805756410b39f5fa81332574cbd" -dependencies = [ - "cfg_aliases", - "libc", - "once_cell", - "socket2", - "tracing", - "windows-sys 0.60.2", -] - -[[package]] -name = "quote" -version = "1.0.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4" -dependencies = [ - "proc-macro2", -] - -[[package]] -name = "r-efi" -version = "5.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" - -[[package]] -name = "rand" -version = "0.9.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" -dependencies = [ - "rand_chacha", - "rand_core", -] - -[[package]] -name = "rand_chacha" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" -dependencies = [ - "ppv-lite86", - "rand_core", -] - -[[package]] -name = "rand_core" -version = "0.9.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76afc826de14238e6e8c374ddcc1fa19e374fd8dd986b0d2af0d02377261d83c" -dependencies = [ - "getrandom 0.3.4", -] - -[[package]] -name = "range-alloc" -version = "0.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3d6831663a5098ea164f89cff59c6284e95f4e3c76ce9848d4529f5ccca9bde" - -[[package]] -name = "raw-window-handle" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" - -[[package]] -name = "redox_syscall" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -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.0", -] - -[[package]] -name = "redox_syscall" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" -dependencies = [ - "bitflags 2.11.0", -] - -[[package]] -name = "redox_users" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" -dependencies = [ - "getrandom 0.2.17", - "libredox", - "thiserror 2.0.18", -] - -[[package]] -name = "regex" -version = "1.12.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" -dependencies = [ - "aho-corasick", - "memchr", - "regex-automata", - "regex-syntax", -] - -[[package]] -name = "regex-automata" -version = "0.4.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" -dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", -] - -[[package]] -name = "regex-syntax" -version = "0.8.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" - -[[package]] -name = "renderdoc-sys" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19b30a45b0cd0bcca8037f3d0dc3421eaf95327a17cad11964fb8179b4fc4832" - -[[package]] -name = "reqwest" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" -dependencies = [ - "base64", - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "http", - "http-body", - "http-body-util", - "hyper", - "hyper-rustls", - "hyper-util", - "js-sys", - "log", - "mime_guess", - "percent-encoding", - "pin-project-lite", - "quinn", - "rustls", - "rustls-pki-types", - "rustls-platform-verifier", - "serde", - "serde_json", - "sync_wrapper", - "tokio", - "tokio-rustls", - "tower", - "tower-http", - "tower-service", - "url", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", -] - -[[package]] -name = "ring" -version = "0.17.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4689e6c2294d81e88dc6261c768b63bc4fcdb852be6d1352498b114f61383b7" -dependencies = [ - "cc", - "cfg-if", - "getrandom 0.2.17", - "libc", - "untrusted", - "windows-sys 0.52.0", -] - -[[package]] -name = "rustc-demangle" -version = "0.1.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" - -[[package]] -name = "rustc-hash" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" - -[[package]] -name = "rustc-hash" -version = "2.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" - -[[package]] -name = "rustc_version" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" -dependencies = [ - "semver", -] - -[[package]] -name = "rustix" -version = "0.38.44" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" -dependencies = [ - "bitflags 2.11.0", - "errno", - "libc", - "linux-raw-sys 0.4.15", - "windows-sys 0.59.0", -] - -[[package]] -name = "rustix" -version = "1.1.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" -dependencies = [ - "bitflags 2.11.0", - "errno", - "libc", - "linux-raw-sys 0.12.1", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls" -version = "0.23.37" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "758025cb5fccfd3bc2fd74708fd4682be41d99e5dff73c377c0646c6012c73a4" -dependencies = [ - "aws-lc-rs", - "once_cell", - "rustls-pki-types", - "rustls-webpki", - "subtle", - "zeroize", -] - -[[package]] -name = "rustls-native-certs" -version = "0.8.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "612460d5f7bea540c490b2b6395d8e34a953e52b491accd6c86c8164c5932a63" -dependencies = [ - "openssl-probe", - "rustls-pki-types", - "schannel", - "security-framework 3.7.0", -] - -[[package]] -name = "rustls-pki-types" -version = "1.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" -dependencies = [ - "web-time", - "zeroize", -] - -[[package]] -name = "rustls-platform-verifier" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" -dependencies = [ - "core-foundation 0.10.1", - "core-foundation-sys", - "jni", - "log", - "once_cell", - "rustls", - "rustls-native-certs", - "rustls-platform-verifier-android", - "rustls-webpki", - "security-framework 3.7.0", - "security-framework-sys", - "webpki-root-certs", - "windows-sys 0.61.2", -] - -[[package]] -name = "rustls-platform-verifier-android" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" - -[[package]] -name = "rustls-webpki" -version = "0.103.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7df23109aa6c1567d1c575b9952556388da57401e4ace1d15f79eedad0d8f53" -dependencies = [ - "aws-lc-rs", - "ring", - "rustls-pki-types", - "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 = "schannel" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "891d81b926048e76efe18581bf793546b4c0eaf8448d72be8de2bbee5fd166e1" -dependencies = [ - "windows-sys 0.61.2", -] - -[[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 = "sctk-adwaita" -version = "0.10.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6277f0217056f77f1d8f49f2950ac6c278c0d607c45f5ee99328d792ede24ec" -dependencies = [ - "ab_glyph", - "log", - "memmap2", - "smithay-client-toolkit 0.19.2", - "tiny-skia", -] - -[[package]] -name = "security-framework" -version = "2.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" -dependencies = [ - "bitflags 2.11.0", - "core-foundation 0.9.4", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework" -version = "3.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" -dependencies = [ - "bitflags 2.11.0", - "core-foundation 0.10.1", - "core-foundation-sys", - "libc", - "security-framework-sys", -] - -[[package]] -name = "security-framework-sys" -version = "2.17.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" -dependencies = [ - "core-foundation-sys", - "libc", -] - -[[package]] -name = "semver" -version = "1.0.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" -dependencies = [ - "serde", - "serde_core", -] - -[[package]] -name = "serde" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" -dependencies = [ - "serde_core", - "serde_derive", -] - -[[package]] -name = "serde_core" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" -dependencies = [ - "serde_derive", -] - -[[package]] -name = "serde_derive" -version = "1.0.228" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_json" -version = "1.0.149" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" -dependencies = [ - "itoa", - "memchr", - "serde", - "serde_core", - "zmij", -] - -[[package]] -name = "serde_repr" -version = "0.1.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "175ee3e80ae9982737ca543e96133087cbd9a485eecc3bc4de9c1a37b47ea59c" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "serde_spanned" -version = "0.6.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf41e0cfaf7226dca15e8197172c295a782857fcb97fad1808a166870dee75a3" -dependencies = [ - "serde", -] - -[[package]] -name = "sha1" -version = "0.10.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sha2" -version = "0.10.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" -dependencies = [ - "cfg-if", - "cpufeatures", - "digest", -] - -[[package]] -name = "sharded-slab" -version = "0.1.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" -dependencies = [ - "lazy_static", -] - -[[package]] -name = "shlex" -version = "1.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" - -[[package]] -name = "signal-hook-registry" -version = "1.4.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" -dependencies = [ - "errno", - "libc", -] - -[[package]] -name = "simd-adler32" -version = "0.3.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" - -[[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.19.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" -dependencies = [ - "bitflags 2.11.0", - "calloop 0.13.0", - "calloop-wayland-source 0.3.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", - "wayland-protocols-wlr", - "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.0", - "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", - "wayland-protocols-experimental", - "wayland-protocols-misc", - "wayland-protocols-wlr", - "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.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" -dependencies = [ - "libc", - "windows-sys 0.60.2", -] - -[[package]] -name = "spirv" -version = "0.3.0+sdk-1.3.268.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eda41003dc44290527a59b13432d4a0379379fa074b70174882adfbdfd917844" -dependencies = [ - "bitflags 2.11.0", -] - -[[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 = "strict-num" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6637bab7722d379c8b41ba849228d680cc12d0a45ba1fa2b48f2a30577a06731" - -[[package]] -name = "strsim" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" - -[[package]] -name = "subtle" -version = "2.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" - -[[package]] -name = "syn" -version = "1.0.109" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" -dependencies = [ - "proc-macro2", - "unicode-ident", -] - -[[package]] -name = "syn" -version = "2.0.117" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" -dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", -] - -[[package]] -name = "sync_wrapper" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" -dependencies = [ - "futures-core", -] - -[[package]] -name = "synstructure" -version = "0.13.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "system-deps" -version = "6.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" -dependencies = [ - "cfg-expr", - "heck 0.5.0", - "pkg-config", - "toml", - "version-compare", -] - -[[package]] -name = "target-lexicon" -version = "0.12.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" - -[[package]] -name = "tempfile" -version = "3.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82a72c767771b47409d2345987fda8628641887d5466101319899796367354a0" -dependencies = [ - "fastrand", - "getrandom 0.4.1", - "once_cell", - "rustix 1.1.4", - "windows-sys 0.61.2", -] - -[[package]] -name = "termcolor" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755" -dependencies = [ - "winapi-util", -] - -[[package]] -name = "thiserror" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" -dependencies = [ - "thiserror-impl 1.0.69", -] - -[[package]] -name = "thiserror" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" -dependencies = [ - "thiserror-impl 2.0.18", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.69" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "thiserror-impl" -version = "2.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "thread_local" -version = "1.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f60246a4944f24f6e018aa17cdeffb7818b76356965d03b07d6a9886e8962185" -dependencies = [ - "cfg-if", -] - -[[package]] -name = "tiff" -version = "0.10.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f" -dependencies = [ - "fax", - "flate2", - "half", - "quick-error", - "weezl", - "zune-jpeg", -] - -[[package]] -name = "time" -version = "0.3.47" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c" -dependencies = [ - "deranged", - "itoa", - "libc", - "num-conv", - "num_threads", - "powerfmt", - "serde_core", - "time-core", - "time-macros", -] - -[[package]] -name = "time-core" -version = "0.1.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7694e1cfe791f8d31026952abf09c69ca6f6fa4e1a1229e18988f06a04a12dca" - -[[package]] -name = "time-macros" -version = "0.2.27" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e70e4c5a0e0a8a4823ad65dfe1a6930e4f4d756dcd9dd7939022b5e8c501215" -dependencies = [ - "num-conv", - "time-core", -] - -[[package]] -name = "tiny-skia" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "83d13394d44dae3207b52a326c0c85a8bf87f1541f23b0d143811088497b09ab" -dependencies = [ - "arrayref", - "arrayvec", - "bytemuck", - "cfg-if", - "log", - "tiny-skia-path", -] - -[[package]] -name = "tiny-skia-path" -version = "0.11.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c9e7fc0c2e86a30b117d0462aa261b72b7a99b7ebd7deb3a14ceda95c5bdc93" -dependencies = [ - "arrayref", - "bytemuck", - "strict-num", -] - -[[package]] -name = "tinystr" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" -dependencies = [ - "displaydoc", - "zerovec", -] - -[[package]] -name = "tinyvec" -version = "1.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" -dependencies = [ - "tinyvec_macros", -] - -[[package]] -name = "tinyvec_macros" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" - -[[package]] -name = "tokio" -version = "1.49.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" -dependencies = [ - "bytes", - "libc", - "mio", - "pin-project-lite", - "socket2", - "windows-sys 0.61.2", -] - -[[package]] -name = "tokio-rustls" -version = "0.26.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" -dependencies = [ - "rustls", - "tokio", -] - -[[package]] -name = "tokio-tungstenite" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" -dependencies = [ - "futures-util", - "log", - "rustls", - "rustls-native-certs", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tungstenite", -] - -[[package]] -name = "toml" -version = "0.8.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime 0.6.3", - "toml_edit 0.20.2", -] - -[[package]] -name = "toml_datetime" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" -dependencies = [ - "serde", -] - -[[package]] -name = "toml_datetime" -version = "0.7.5+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" -dependencies = [ - "serde_core", -] - -[[package]] -name = "toml_edit" -version = "0.19.15" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" -dependencies = [ - "indexmap", - "toml_datetime 0.6.3", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.20.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" -dependencies = [ - "indexmap", - "serde", - "serde_spanned", - "toml_datetime 0.6.3", - "winnow 0.5.40", -] - -[[package]] -name = "toml_edit" -version = "0.23.10+spec-1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" -dependencies = [ - "indexmap", - "toml_datetime 0.7.5+spec-1.1.0", - "toml_parser", - "winnow 0.7.14", -] - -[[package]] -name = "toml_parser" -version = "1.0.9+spec-1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702d4415e08923e7e1ef96cd5727c0dfed80b4d2fa25db9647fe5eb6f7c5a4c4" -dependencies = [ - "winnow 0.7.14", -] - -[[package]] -name = "tower" -version = "0.5.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" -dependencies = [ - "futures-core", - "futures-util", - "pin-project-lite", - "sync_wrapper", - "tokio", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-http" -version = "0.6.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" -dependencies = [ - "bitflags 2.11.0", - "bytes", - "futures-util", - "http", - "http-body", - "iri-string", - "pin-project-lite", - "tower", - "tower-layer", - "tower-service", -] - -[[package]] -name = "tower-layer" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" - -[[package]] -name = "tower-service" -version = "0.3.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" - -[[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", -] - -[[package]] -name = "tracing-appender" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "786d480bce6247ab75f005b14ae1624ad978d3029d9113f0a22fa1ac773faeaf" -dependencies = [ - "crossbeam-channel", - "thiserror 2.0.18", - "time", - "tracing-subscriber", -] - -[[package]] -name = "tracing-attributes" -version = "0.1.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "tracing-core" -version = "0.1.36" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" -dependencies = [ - "once_cell", - "valuable", -] - -[[package]] -name = "tracing-error" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b1581020d7a273442f5b45074a6a57d5757ad0a47dac0e9f0bd57b81936f3db" -dependencies = [ - "tracing", - "tracing-subscriber", -] - -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - -[[package]] -name = "tracing-subscriber" -version = "0.3.22" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f30143827ddab0d256fd843b7a66d164e9f271cfa0dde49142c5ca0ca291f1e" -dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex-automata", - "sharded-slab", - "smallvec", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", -] - -[[package]] -name = "tray-icon" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5e85aa143ceb072062fc4d6356c1b520a51d636e7bc8e77ec94be3608e5e80c" -dependencies = [ - "crossbeam-channel", - "dirs", - "libappindicator", - "muda", - "objc2 0.6.4", - "objc2-app-kit 0.3.2", - "objc2-core-foundation", - "objc2-core-graphics", - "objc2-foundation 0.3.2", - "once_cell", - "png 0.17.16", - "thiserror 2.0.18", - "windows-sys 0.60.2", -] - -[[package]] -name = "try-lock" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" - -[[package]] -name = "ttf-parser" -version = "0.25.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2df906b07856748fa3f6e0ad0cbaa047052d4a7dd609e231c4f72cee8c36f31" - -[[package]] -name = "tungstenite" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" -dependencies = [ - "bytes", - "data-encoding", - "http", - "httparse", - "log", - "rand", - "rustls", - "rustls-pki-types", - "sha1", - "thiserror 2.0.18", - "utf-8", -] - -[[package]] -name = "type-map" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cb30dbbd9036155e74adad6812e9898d03ec374946234fbcebd5dfc7b9187b90" -dependencies = [ - "rustc-hash 2.1.1", -] - -[[package]] -name = "typenum" -version = "1.19.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" - -[[package]] -name = "uds_windows" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" -dependencies = [ - "memoffset", - "tempfile", - "winapi", -] - -[[package]] -name = "unicase" -version = "2.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" - -[[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.12.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" - -[[package]] -name = "unicode-width" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" - -[[package]] -name = "unicode-xid" -version = "0.2.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" - -[[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 = "utf-8" -version = "0.7.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" - -[[package]] -name = "utf8_iter" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" - -[[package]] -name = "uuid" -version = "1.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b672338555252d43fd2240c714dc444b8c6fb0a5c5335e65a07bba7742735ddb" -dependencies = [ - "js-sys", - "serde_core", - "wasm-bindgen", -] - -[[package]] -name = "valuable" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" - -[[package]] -name = "vergen" -version = "9.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b849a1f6d8639e8de261e81ee0fc881e3e3620db1af9f2e0da015d4382ceaf75" -dependencies = [ - "anyhow", - "cargo_metadata", - "derive_builder", - "regex", - "rustversion", - "vergen-lib", + "security-framework-sys", ] [[package]] -name = "vergen-gitcl" -version = "9.1.0" +name = "security-framework" +version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77ff3b5300a085d6bcd8fc96a507f706a28ae3814693236c9b409db71a1d15b9" +checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "anyhow", - "derive_builder", - "rustversion", - "time", - "vergen", - "vergen-lib", + "bitflags", + "core-foundation 0.10.1", + "core-foundation-sys", + "libc", + "security-framework-sys", ] [[package]] -name = "vergen-lib" -version = "9.1.0" +name = "security-framework-sys" +version = "2.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b34a29ba7e9c59e62f229ae1932fb1b8fb8a6fdcc99215a641913f5f5a59a569" +checksum = "6ce2691df843ecc5d231c0b14ece2acc3efb62c0a398c7e1d875f3983ce020e3" dependencies = [ - "anyhow", - "derive_builder", - "rustversion", + "core-foundation-sys", + "libc", ] [[package]] -name = "version-compare" -version = "0.2.1" +name = "serde" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03c2856837ef78f57382f06b2b8563a2f512f7185d732608fd9176cb3b8edf0e" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" +dependencies = [ + "serde_core", + "serde_derive", +] [[package]] -name = "version_check" -version = "0.9.5" +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" - -[[package]] -name = "voxit" -version = "0.1.0" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ - "arboard", - "color-eyre", - "directories", - "eframe", - "enigo", - "global-hotkey", - "tracing", - "tracing-appender", - "tracing-subscriber", - "tray-icon", - "vergen-gitcl", - "voxit-audio", - "voxit-core", - "voxit-macos", + "serde_derive", ] [[package]] -name = "voxit-audio" -version = "0.1.0" +name = "serde_derive" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ - "coreaudio-rs", - "hound", - "objc2-audio-toolbox", - "tracing", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "voxit-core" -version = "0.1.0" +name = "serde_json" +version = "1.0.149" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ - "base64", - "core-foundation 0.10.1", - "core-foundation-sys", - "directories", - "futures-util", - "hound", - "http", - "keyring", - "objc2-foundation 0.3.2", - "objc2-local-authentication", - "reqwest", - "security-framework-sys", + "itoa", + "memchr", "serde", - "serde_json", - "sha2", - "tokio", - "tokio-tungstenite", - "tracing", - "url", - "voxit-audio", - "webbrowser", + "serde_core", + "zmij", ] [[package]] -name = "voxit-macos" -version = "0.1.0" +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" dependencies = [ - "block2 0.6.2", - "objc2-app-kit 0.3.2", - "objc2-av-foundation", - "tracing", + "cfg-if", + "cpufeatures", + "digest", ] [[package]] -name = "walkdir" -version = "2.5.0" +name = "sha2" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ - "same-file", - "winapi-util", + "cfg-if", + "cpufeatures", + "digest", ] [[package]] -name = "want" -version = "0.3.1" +name = "shlex" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -dependencies = [ - "try-lock", -] +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] -name = "wasi" -version = "0.11.1+wasi-snapshot-preview1" +name = "slab" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" +checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] -name = "wasip2" -version = "1.0.2+wasi-0.2.9" +name = "smallvec" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" -dependencies = [ - "wit-bindgen", -] +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] -name = "wasip3" -version = "0.4.0+wasi-0.3.0-rc-2026-01-06" +name = "socket2" +version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" +checksum = "86f4aa3ad99f2088c990dfa82d367e19cb29268ed67c574d10d0a4bfe71f07e0" dependencies = [ - "wit-bindgen", + "libc", + "windows-sys 0.60.2", ] [[package]] -name = "wasm-bindgen" -version = "0.2.113" +name = "stable_deref_trait" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60722a937f594b7fde9adb894d7c092fc1bb6612897c46368d18e7a20208eff2" +checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" + +[[package]] +name = "subtle" +version = "2.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13c2bddecc57b384dee18652358fb23172facb8a2c51ccc10d74c157bdea3292" + +[[package]] +name = "syn" +version = "2.0.117" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ - "cfg-if", - "once_cell", - "rustversion", - "wasm-bindgen-macro", - "wasm-bindgen-shared", + "proc-macro2", + "quote", + "unicode-ident", ] [[package]] -name = "wasm-bindgen-futures" -version = "0.4.63" +name = "sync_wrapper" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a89f4650b770e4521aa6573724e2aed4704372151bd0de9d16a3bbabb87441a" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" dependencies = [ - "cfg-if", - "futures-util", - "js-sys", - "once_cell", - "wasm-bindgen", - "web-sys", + "futures-core", ] [[package]] -name = "wasm-bindgen-macro" -version = "0.2.113" +name = "synstructure" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fac8c6395094b6b91c4af293f4c79371c163f9a6f56184d2c9a85f5a95f3950" +checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ + "proc-macro2", "quote", - "wasm-bindgen-macro-support", + "syn", ] [[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.113" +name = "thiserror" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ab3fabce6159dc20728033842636887e4877688ae94382766e00b180abac9d60" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" dependencies = [ - "bumpalo", - "proc-macro2", - "quote", - "syn 2.0.117", - "wasm-bindgen-shared", + "thiserror-impl 1.0.69", ] [[package]] -name = "wasm-bindgen-shared" -version = "0.2.113" +name = "thiserror" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "de0e091bdb824da87dc01d967388880d017a0a9bc4f3bdc0d86ee9f9336e3bb5" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "unicode-ident", + "thiserror-impl 2.0.18", ] [[package]] -name = "wasm-encoder" -version = "0.244.0" +name = "thiserror-impl" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "990065f2fe63003fe337b932cfb5e3b80e0b4d0f5ff650e6985b1048f62c8319" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "leb128fmt", - "wasmparser", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "wasm-metadata" -version = "0.244.0" +name = "thiserror-impl" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bb0e353e6a2fbdc176932bbaab493762eb1255a7900fe0fea1a2f96c296cc909" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ - "anyhow", - "indexmap", - "wasm-encoder", - "wasmparser", + "proc-macro2", + "quote", + "syn", ] [[package]] -name = "wasmparser" -version = "0.244.0" +name = "tinystr" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" +checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ - "bitflags 2.11.0", - "hashbrown 0.15.5", - "indexmap", - "semver", + "displaydoc", + "zerovec", ] [[package]] -name = "wayland-backend" -version = "0.3.12" +name = "tinyvec" +version = "1.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fee64194ccd96bf648f42a65a7e589547096dfa702f7cadef84347b66ad164f9" +checksum = "bfa5fdc3bce6191a1dbc8c02d5c8bffcf557bafa17c124c5264a458f1b0613fa" dependencies = [ - "cc", - "downcast-rs", - "rustix 1.1.4", - "scoped-tls", - "smallvec", - "wayland-sys", + "tinyvec_macros", ] [[package]] -name = "wayland-client" -version = "0.31.12" +name = "tinyvec_macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" + +[[package]] +name = "tokio" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8e6faa537fbb6c186cb9f1d41f2f811a4120d1b57ec61f50da451a0c5122bec" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ - "bitflags 2.11.0", - "rustix 1.1.4", - "wayland-backend", - "wayland-scanner", + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "windows-sys 0.61.2", ] [[package]] -name = "wayland-csd-frame" -version = "0.3.0" +name = "tokio-rustls" +version = "0.26.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +checksum = "1729aa945f29d91ba541258c8df89027d5792d85a8841fb65e8bf0f4ede4ef61" dependencies = [ - "bitflags 2.11.0", - "cursor-icon", - "wayland-backend", + "rustls", + "tokio", ] [[package]] -name = "wayland-cursor" -version = "0.31.12" +name = "tokio-tungstenite" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5864c4b5b6064b06b1e8b74ead4a98a6c45a285fe7a0e784d24735f011fdb078" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" dependencies = [ - "rustix 1.1.4", - "wayland-client", - "xcursor", + "futures-util", + "log", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tungstenite", ] [[package]] -name = "wayland-protocols" -version = "0.32.10" +name = "tower" +version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baeda9ffbcfc8cd6ddaade385eaf2393bd2115a69523c735f12242353c3df4f3" +checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ - "bitflags 2.11.0", - "wayland-backend", - "wayland-client", - "wayland-scanner", + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tower-layer", + "tower-service", ] [[package]] -name = "wayland-protocols-experimental" -version = "20250721.0.1" +name = "tower-http" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40a1f863128dcaaec790d7b4b396cc9b9a7a079e878e18c47e6c2d2c5a8dcbb1" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ - "bitflags 2.11.0", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "wayland-scanner", + "bitflags", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", ] [[package]] -name = "wayland-protocols-misc" -version = "0.3.10" +name = "tower-layer" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "791c58fdeec5406aa37169dd815327d1e47f334219b523444bc26d70ceb4c34e" -dependencies = [ - "bitflags 2.11.0", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "wayland-scanner", -] +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] -name = "wayland-protocols-plasma" -version = "0.3.10" +name = "tower-service" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa98634619300a535a9a97f338aed9a5ff1e01a461943e8346ff4ae26007306b" -dependencies = [ - "bitflags 2.11.0", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "wayland-scanner", -] +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] -name = "wayland-protocols-wlr" -version = "0.3.10" +name = "tracing" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9597cdf02cf0c34cd5823786dce6b5ae8598f05c2daf5621b6e178d4f7345f3" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ - "bitflags 2.11.0", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "wayland-scanner", + "pin-project-lite", + "tracing-attributes", + "tracing-core", ] [[package]] -name = "wayland-scanner" -version = "0.31.8" +name = "tracing-attributes" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5423e94b6a63e68e439803a3e153a9252d5ead12fd853334e2ad33997e3889e3" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", - "quick-xml", "quote", + "syn", ] [[package]] -name = "wayland-sys" -version = "0.31.8" +name = "tracing-core" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e6dbfc3ac5ef974c92a2235805cc0114033018ae1290a72e474aa8b28cbbdfd" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ - "dlib", - "log", "once_cell", - "pkg-config", -] - -[[package]] -name = "web-sys" -version = "0.3.90" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "705eceb4ce901230f8625bd1d665128056ccbe4b7408faa625eec1ba80f59a97" -dependencies = [ - "js-sys", - "wasm-bindgen", ] [[package]] -name = "web-time" -version = "1.1.0" +name = "try-lock" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" -dependencies = [ - "js-sys", - "wasm-bindgen", -] +checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" [[package]] -name = "webbrowser" -version = "1.1.0" +name = "tungstenite" +version = "0.28.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f00bb839c1cf1e3036066614cbdcd035ecf215206691ea646aa3c60a24f68f2" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" dependencies = [ - "core-foundation 0.10.1", - "jni", + "bytes", + "data-encoding", + "http", + "httparse", "log", - "ndk-context", - "objc2 0.6.4", - "objc2-foundation 0.3.2", - "url", - "web-sys", -] - -[[package]] -name = "webpki-root-certs" -version = "1.0.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" -dependencies = [ + "rand", + "rustls", "rustls-pki-types", + "sha1", + "thiserror 2.0.18", + "utf-8", ] [[package]] -name = "weezl" -version = "0.1.12" +name = "typenum" +version = "1.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" +checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" [[package]] -name = "wgpu" -version = "27.0.1" +name = "unicase" +version = "2.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bfe68bac7cde125de7a731c3400723cadaaf1703795ad3f4805f187459cd7a77" -dependencies = [ - "arrayvec", - "bitflags 2.11.0", - "cfg-if", - "cfg_aliases", - "document-features", - "hashbrown 0.16.1", - "js-sys", - "log", - "naga", - "parking_lot", - "portable-atomic", - "profiling", - "raw-window-handle", - "smallvec", - "static_assertions", - "wasm-bindgen", - "wasm-bindgen-futures", - "web-sys", - "wgpu-core", - "wgpu-hal", - "wgpu-types", -] +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" [[package]] -name = "wgpu-core" -version = "27.0.3" +name = "unicode-ident" +version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27a75de515543b1897b26119f93731b385a19aea165a1ec5f0e3acecc229cae7" -dependencies = [ - "arrayvec", - "bit-set", - "bit-vec", - "bitflags 2.11.0", - "bytemuck", - "cfg_aliases", - "document-features", - "hashbrown 0.16.1", - "indexmap", - "log", - "naga", - "once_cell", - "parking_lot", - "portable-atomic", - "profiling", - "raw-window-handle", - "rustc-hash 1.1.0", - "smallvec", - "thiserror 2.0.18", - "wgpu-core-deps-apple", - "wgpu-core-deps-emscripten", - "wgpu-core-deps-windows-linux-android", - "wgpu-hal", - "wgpu-types", -] +checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] -name = "wgpu-core-deps-apple" -version = "27.0.0" +name = "untrusted" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0772ae958e9be0c729561d5e3fd9a19679bcdfb945b8b1a1969d9bfe8056d233" -dependencies = [ - "wgpu-hal", -] +checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1" [[package]] -name = "wgpu-core-deps-emscripten" -version = "27.0.0" +name = "url" +version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b06ac3444a95b0813ecfd81ddb2774b66220b264b3e2031152a4a29fda4da6b5" +checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ - "wgpu-hal", + "form_urlencoded", + "idna", + "percent-encoding", + "serde", ] [[package]] -name = "wgpu-core-deps-windows-linux-android" -version = "27.0.0" +name = "utf-8" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "71197027d61a71748e4120f05a9242b2ad142e3c01f8c1b47707945a879a03c3" -dependencies = [ - "wgpu-hal", -] +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" [[package]] -name = "wgpu-hal" -version = "27.0.4" +name = "utf8_iter" +version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b21cb61c57ee198bc4aff71aeadff4cbb80b927beb912506af9c780d64313ce" -dependencies = [ - "android_system_properties", - "arrayvec", - "ash", - "bit-set", - "bitflags 2.11.0", - "block", - "bytemuck", - "cfg-if", - "cfg_aliases", - "core-graphics-types 0.2.0", - "glow", - "glutin_wgl_sys", - "gpu-alloc", - "gpu-allocator", - "gpu-descriptor", - "hashbrown 0.16.1", - "js-sys", - "khronos-egl", - "libc", - "libloading 0.8.9", - "log", - "metal", - "naga", - "ndk-sys", - "objc", - "once_cell", - "ordered-float", - "parking_lot", - "portable-atomic", - "portable-atomic-util", - "profiling", - "range-alloc", - "raw-window-handle", - "renderdoc-sys", - "smallvec", - "thiserror 2.0.18", - "wasm-bindgen", - "web-sys", - "wgpu-types", - "windows 0.58.0", - "windows-core 0.58.0", -] +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] -name = "wgpu-types" -version = "27.0.1" +name = "version_check" +version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "afdcf84c395990db737f2dd91628706cb31e86d72e53482320d368e52b5da5eb" -dependencies = [ - "bitflags 2.11.0", - "bytemuck", - "js-sys", - "log", - "thiserror 2.0.18", - "web-sys", -] +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" [[package]] -name = "winapi" -version = "0.3.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +name = "voxit-audio" +version = "0.1.0" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "coreaudio-rs", + "hound", + "objc2-audio-toolbox", + "tracing", ] [[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" +name = "voxit-core" +version = "0.1.0" dependencies = [ - "windows-sys 0.61.2", + "base64", + "core-foundation 0.10.1", + "core-foundation-sys", + "directories", + "futures-util", + "hound", + "http", + "keyring", + "objc2-foundation", + "objc2-local-authentication", + "reqwest", + "security-framework-sys", + "serde", + "serde_json", + "sha2", + "tokio", + "tokio-tungstenite", + "tracing", + "url", + "voxit-audio", + "webbrowser", ] [[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" -version = "0.58.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +name = "voxit-host-ffi" +version = "0.1.0" dependencies = [ - "windows-core 0.58.0", - "windows-targets 0.52.6", + "voxit-core", ] [[package]] -name = "windows" -version = "0.61.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" +name = "voxit-macos" +version = "0.1.0" dependencies = [ - "windows-collections", - "windows-core 0.61.2", - "windows-future", - "windows-link 0.1.3", - "windows-numerics", + "block2", + "objc2-app-kit", + "objc2-av-foundation", + "tracing", ] [[package]] -name = "windows-collections" -version = "0.2.0" +name = "walkdir" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" dependencies = [ - "windows-core 0.61.2", + "same-file", + "winapi-util", ] [[package]] -name = "windows-core" -version = "0.58.0" +name = "want" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" dependencies = [ - "windows-implement 0.58.0", - "windows-interface 0.58.0", - "windows-result 0.2.0", - "windows-strings 0.1.0", - "windows-targets 0.52.6", + "try-lock", ] [[package]] -name = "windows-core" -version = "0.61.2" +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0fdd3ddb90610c7638aa2b3a3ab2904fb9e5cdbecc643ddb3647212781c4ae3" -dependencies = [ - "windows-implement 0.60.2", - "windows-interface 0.59.3", - "windows-link 0.1.3", - "windows-result 0.3.4", - "windows-strings 0.4.2", -] +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] -name = "windows-future" -version = "0.2.1" +name = "wasip2" +version = "1.0.2+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" +checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", - "windows-threading", + "wit-bindgen", ] [[package]] -name = "windows-implement" -version = "0.58.0" +name = "wasm-bindgen" +version = "0.2.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +checksum = "60722a937f594b7fde9adb894d7c092fc1bb6612897c46368d18e7a20208eff2" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", ] [[package]] -name = "windows-implement" -version = "0.60.2" +name = "wasm-bindgen-futures" +version = "0.4.63" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +checksum = "8a89f4650b770e4521aa6573724e2aed4704372151bd0de9d16a3bbabb87441a" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", + "cfg-if", + "futures-util", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", ] [[package]] -name = "windows-interface" -version = "0.58.0" +name = "wasm-bindgen-macro" +version = "0.2.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +checksum = "0fac8c6395094b6b91c4af293f4c79371c163f9a6f56184d2c9a85f5a95f3950" dependencies = [ - "proc-macro2", "quote", - "syn 2.0.117", + "wasm-bindgen-macro-support", ] [[package]] -name = "windows-interface" -version = "0.59.3" +name = "wasm-bindgen-macro-support" +version = "0.2.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +checksum = "ab3fabce6159dc20728033842636887e4877688ae94382766e00b180abac9d60" dependencies = [ + "bumpalo", "proc-macro2", "quote", - "syn 2.0.117", + "syn", + "wasm-bindgen-shared", ] [[package]] -name = "windows-link" -version = "0.1.3" +name = "wasm-bindgen-shared" +version = "0.2.113" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e6ad25900d524eaabdbbb96d20b4311e1e7ae1699af4fb28c17ae66c80d798a" +checksum = "de0e091bdb824da87dc01d967388880d017a0a9bc4f3bdc0d86ee9f9336e3bb5" +dependencies = [ + "unicode-ident", +] [[package]] -name = "windows-link" -version = "0.2.1" +name = "web-sys" +version = "0.3.90" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" +checksum = "705eceb4ce901230f8625bd1d665128056ccbe4b7408faa625eec1ba80f59a97" +dependencies = [ + "js-sys", + "wasm-bindgen", +] [[package]] -name = "windows-numerics" -version = "0.2.0" +name = "web-time" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" dependencies = [ - "windows-core 0.61.2", - "windows-link 0.1.3", + "js-sys", + "wasm-bindgen", ] [[package]] -name = "windows-result" -version = "0.2.0" +name = "webbrowser" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +checksum = "3f00bb839c1cf1e3036066614cbdcd035ecf215206691ea646aa3c60a24f68f2" dependencies = [ - "windows-targets 0.52.6", + "core-foundation 0.10.1", + "jni", + "log", + "ndk-context", + "objc2", + "objc2-foundation", + "url", + "web-sys", ] [[package]] -name = "windows-result" -version = "0.3.4" +name = "webpki-root-certs" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56f42bd332cc6c8eac5af113fc0c1fd6a8fd2aa08a0119358686e5160d0586c6" +checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" dependencies = [ - "windows-link 0.1.3", + "rustls-pki-types", ] [[package]] -name = "windows-strings" -version = "0.1.0" +name = "winapi-util" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" dependencies = [ - "windows-result 0.2.0", - "windows-targets 0.52.6", + "windows-sys 0.61.2", ] [[package]] -name = "windows-strings" -version = "0.4.2" +name = "windows-link" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56e6c93f3a0c3b36176cb1327a4958a0353d5d166c2a35cb268ace15e91d3b57" -dependencies = [ - "windows-link 0.1.3", -] +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-sys" @@ -5737,15 +1817,6 @@ dependencies = [ "windows-targets 0.52.6", ] -[[package]] -name = "windows-sys" -version = "0.59.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" -dependencies = [ - "windows-targets 0.52.6", -] - [[package]] name = "windows-sys" version = "0.60.2" @@ -5761,7 +1832,7 @@ version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ - "windows-link 0.2.1", + "windows-link", ] [[package]] @@ -5801,7 +1872,7 @@ version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows-link 0.2.1", + "windows-link", "windows_aarch64_gnullvm 0.53.1", "windows_aarch64_msvc 0.53.1", "windows_i686_gnu 0.53.1", @@ -5812,15 +1883,6 @@ dependencies = [ "windows_x86_64_msvc 0.53.1", ] -[[package]] -name = "windows-threading" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66463ad2e0ea3bbf808b7f1d371311c80e115c0b71d60efc142cafbcfb057a6" -dependencies = [ - "windows-link 0.1.3", -] - [[package]] name = "windows_aarch64_gnullvm" version = "0.42.2" @@ -5959,163 +2021,11 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" -[[package]] -name = "winit" -version = "0.30.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c66d4b9ed69c4009f6321f762d6e61ad8a2389cd431b97cb1e146812e9e6c732" -dependencies = [ - "ahash", - "android-activity", - "atomic-waker", - "bitflags 2.11.0", - "block2 0.5.1", - "bytemuck", - "calloop 0.13.0", - "cfg_aliases", - "concurrent-queue", - "core-foundation 0.9.4", - "core-graphics 0.23.2", - "cursor-icon", - "dpi", - "js-sys", - "libc", - "memmap2", - "ndk", - "objc2 0.5.2", - "objc2-app-kit 0.2.2", - "objc2-foundation 0.2.2", - "objc2-ui-kit", - "orbclient", - "percent-encoding", - "pin-project", - "raw-window-handle", - "redox_syscall 0.4.1", - "rustix 0.38.44", - "sctk-adwaita", - "smithay-client-toolkit 0.19.2", - "smol_str", - "tracing", - "unicode-segmentation", - "wasm-bindgen", - "wasm-bindgen-futures", - "wayland-backend", - "wayland-client", - "wayland-protocols", - "wayland-protocols-plasma", - "web-sys", - "web-time", - "windows-sys 0.52.0", - "x11-dl", - "x11rb", - "xkbcommon-dl", -] - -[[package]] -name = "winnow" -version = "0.5.40" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f593a95398737aeed53e489c785df13f3618e41dbcd6718c6addbf1395aa6876" -dependencies = [ - "memchr", -] - -[[package]] -name = "winnow" -version = "0.7.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" -dependencies = [ - "memchr", -] - [[package]] name = "wit-bindgen" version = "0.51.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7249219f66ced02969388cf2bb044a09756a083d0fab1e566056b04d9fbcaa5" -dependencies = [ - "wit-bindgen-rust-macro", -] - -[[package]] -name = "wit-bindgen-core" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea61de684c3ea68cb082b7a88508a8b27fcc8b797d738bfc99a82facf1d752dc" -dependencies = [ - "anyhow", - "heck 0.5.0", - "wit-parser", -] - -[[package]] -name = "wit-bindgen-rust" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7c566e0f4b284dd6561c786d9cb0142da491f46a9fbed79ea69cdad5db17f21" -dependencies = [ - "anyhow", - "heck 0.5.0", - "indexmap", - "prettyplease", - "syn 2.0.117", - "wasm-metadata", - "wit-bindgen-core", - "wit-component", -] - -[[package]] -name = "wit-bindgen-rust-macro" -version = "0.51.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c0f9bfd77e6a48eccf51359e3ae77140a7f50b1e2ebfe62422d8afdaffab17a" -dependencies = [ - "anyhow", - "prettyplease", - "proc-macro2", - "quote", - "syn 2.0.117", - "wit-bindgen-core", - "wit-bindgen-rust", -] - -[[package]] -name = "wit-component" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" -dependencies = [ - "anyhow", - "bitflags 2.11.0", - "indexmap", - "log", - "serde", - "serde_derive", - "serde_json", - "wasm-encoder", - "wasm-metadata", - "wasmparser", - "wit-parser", -] - -[[package]] -name = "wit-parser" -version = "0.244.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ecc8ac4bc1dc3381b7f59c34f00b67e18f910c2c0f50015669dde7def656a736" -dependencies = [ - "anyhow", - "id-arena", - "indexmap", - "log", - "semver", - "serde", - "serde_derive", - "serde_json", - "unicode-xid", - "wasmparser", -] [[package]] name = "writeable" @@ -6123,90 +2033,6 @@ version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" -[[package]] -name = "x11" -version = "2.21.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "502da5464ccd04011667b11c435cb992822c2c0dbde1770c988480d312a0db2e" -dependencies = [ - "libc", - "pkg-config", -] - -[[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 0.8.9", - "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 = "xcursor" -version = "0.3.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec9e4a500ca8864c5b47b8b482a73d62e4237670e5b5f1d6b9e3cae50f28f2b" - -[[package]] -name = "xkbcommon" -version = "0.9.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a974f48060a14e95705c01f24ad9c3345022f4d97441b8a36beb7ed5c4a02d" -dependencies = [ - "libc", - "memmap2", - "xkeysym", -] - -[[package]] -name = "xkbcommon-dl" -version = "0.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" -dependencies = [ - "bitflags 2.11.0", - "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 = "yoke" version = "0.8.1" @@ -6226,107 +2052,10 @@ checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", "synstructure", ] -[[package]] -name = "zbus" -version = "5.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca82f95dbd3943a40a53cfded6c2d0a2ca26192011846a1810c4256ef92c60bc" -dependencies = [ - "async-broadcast", - "async-executor", - "async-io", - "async-lock", - "async-process", - "async-recursion", - "async-task", - "async-trait", - "blocking", - "enumflags2", - "event-listener", - "futures-core", - "futures-lite", - "hex", - "libc", - "ordered-stream", - "rustix 1.1.4", - "serde", - "serde_repr", - "tracing", - "uds_windows", - "uuid", - "windows-sys 0.61.2", - "winnow 0.7.14", - "zbus_macros", - "zbus_names", - "zvariant", -] - -[[package]] -name = "zbus-lockstep" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6998de05217a084b7578728a9443d04ea4cd80f2a0839b8d78770b76ccd45863" -dependencies = [ - "zbus_xml", - "zvariant", -] - -[[package]] -name = "zbus-lockstep-macros" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10da05367f3a7b7553c8cdf8fa91aee6b64afebe32b51c95177957efc47ca3a0" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", - "zbus-lockstep", - "zbus_xml", - "zvariant", -] - -[[package]] -name = "zbus_macros" -version = "5.14.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "897e79616e84aac4b2c46e9132a4f63b93105d54fe8c0e8f6bffc21fa8d49222" -dependencies = [ - "proc-macro-crate 3.4.0", - "proc-macro2", - "quote", - "syn 2.0.117", - "zbus_names", - "zvariant", - "zvariant_utils", -] - -[[package]] -name = "zbus_names" -version = "4.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" -dependencies = [ - "serde", - "winnow 0.7.14", - "zvariant", -] - -[[package]] -name = "zbus_xml" -version = "5.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "441a0064125265655bccc3a6af6bef56814d9277ac83fce48b1cd7e160b80eac" -dependencies = [ - "quick-xml", - "serde", - "zbus_names", - "zvariant", -] - [[package]] name = "zerocopy" version = "0.8.40" @@ -6344,7 +2073,7 @@ checksum = "f65c489a7071a749c849713807783f70672b28094011623e200cb86dcb835953" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -6364,7 +2093,7 @@ checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", "synstructure", ] @@ -6404,7 +2133,7 @@ checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", - "syn 2.0.117", + "syn", ] [[package]] @@ -6412,58 +2141,3 @@ name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" - -[[package]] -name = "zune-core" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" - -[[package]] -name = "zune-jpeg" -version = "0.4.21" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" -dependencies = [ - "zune-core", -] - -[[package]] -name = "zvariant" -version = "5.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5708299b21903bbe348e94729f22c49c55d04720a004aa350f1f9c122fd2540b" -dependencies = [ - "endi", - "enumflags2", - "serde", - "winnow 0.7.14", - "zvariant_derive", - "zvariant_utils", -] - -[[package]] -name = "zvariant_derive" -version = "5.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b59b012ebe9c46656f9cc08d8da8b4c726510aef12559da3e5f1bf72780752c" -dependencies = [ - "proc-macro-crate 3.4.0", - "proc-macro2", - "quote", - "syn 2.0.117", - "zvariant_utils", -] - -[[package]] -name = "zvariant_utils" -version = "3.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" -dependencies = [ - "proc-macro2", - "quote", - "serde", - "syn 2.0.117", - "winnow 0.7.14", -] diff --git a/Cargo.toml b/Cargo.toml index ec407f9..81ecb79 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["apps/*", "packages/*"] +members = ["packages/*"] resolver = "3" [workspace.package] diff --git a/Makefile.toml b/Makefile.toml index c49a0a1..0a035a0 100644 --- a/Makefile.toml +++ b/Makefile.toml @@ -7,6 +7,8 @@ # | lint-fix | composite | | # | lint-rust | command | | # | lint-fix-rust | extend | | +# | lint-swift | composite | | +# | lint-fix-swift | composite | | # | lint-vstyle | command | | # | lint-fix-vstyle | command | | @@ -14,6 +16,7 @@ workspace = false dependencies = [ "lint-rust", + "lint-swift", "lint-vstyle", ] @@ -21,6 +24,7 @@ dependencies = [ workspace = false dependencies = [ "lint-fix-rust", + "lint-fix-swift", "lint-fix-vstyle", ] @@ -51,6 +55,32 @@ args = [ "warnings", ] +[tasks.lint-swift] +workspace = false +dependencies = [ + "lint-swift-format", + "build-swift-strict", +] + +[tasks.lint-fix-swift] +workspace = false +dependencies = [ + "fmt-swift", + "build-swift-strict", +] + +[tasks.lint-swift-format] +workspace = false +command = "swift" +args = [ + "format", + "lint", + "--recursive", + "--strict", + "--parallel", + "native/macos-host/Sources", +] + [tasks.lint-fix-rust] extend = "lint-rust" args = [ @@ -104,13 +134,19 @@ args = [ # Test # | task | type | cwd | # | --------- | --------- | --- | -# | test | composite | | -# | test-rust | command | | +# | test | composite | | +# | test-rust | command | | +# | test-host-ffi-header | command | | +# | test-macos-native-host | command | | +# | test-macos-native-host-stage | command | | [tasks.test] workspace = false dependencies = [ "test-rust", + "test-host-ffi-header", + "test-macos-native-host", + "test-macos-native-host-stage", ] [tasks.test-rust] @@ -124,6 +160,67 @@ args = [ "--all-features", ] +[tasks.test-host-ffi-header] +workspace = false +command = "clang" +args = [ + "-std=c11", + "-fsyntax-only", + "-I", + "packages/voxit-host-ffi/include", + "packages/voxit-host-ffi/tests/header_smoke.c", +] + +[tasks.build-host-ffi-staticlib] +workspace = false +script = ''' +MACOSX_DEPLOYMENT_TARGET=14.0 cargo build -p voxit-host-ffi +''' + +[tasks.build-swift-strict] +workspace = false +dependencies = [ + "build-host-ffi-staticlib", +] +script = ''' +VOXIT_HOST_FFI_LIB_DIR="$PWD/target/debug" \ +swift build --package-path native/macos-host \ + --explicit-target-dependency-import-check error \ + -Xswiftc -warnings-as-errors \ + -Xswiftc -strict-concurrency=complete +''' + +[tasks.test-macos-native-host] +workspace = false +dependencies = [ + "build-host-ffi-staticlib", +] +script = ''' +VOXIT_HOST_FFI_LIB_DIR="$PWD/target/debug" \ +swift run --package-path native/macos-host VoxitHostBridgeProbe +''' + +[tasks.test-macos-native-host-stage] +workspace = false +script = ''' +./scripts/build_and_run.sh stage +COMMON_ROOT="$(cd "$(git rev-parse --git-common-dir)/.." && pwd)" +STAGE_DIR="${VOXIT_NATIVE_HOST_STAGE_DIR:-$COMMON_ROOT/target/voxit-native-host}" +APP_PATH="$STAGE_DIR/Voxit.app" +test -d "$APP_PATH" +test -x "$APP_PATH/Contents/MacOS/VoxitNativeHost" +test -f "$APP_PATH/Contents/Info.plist" +test -f "$APP_PATH/Contents/Resources/AppIcon.icns" +test -f "$APP_PATH/Contents/Resources/StatusBarIcon.png" +codesign --verify --deep --strict "$APP_PATH" +codesign -dv --verbose=4 "$APP_PATH" 2>&1 | grep -Eq '^(TeamIdentifier=|Signature=adhoc)' +plutil -extract CFBundleName raw "$APP_PATH/Contents/Info.plist" | grep -qx 'Voxit' +plutil -extract CFBundleDisplayName raw "$APP_PATH/Contents/Info.plist" | grep -qx 'Voxit' +plutil -extract CFBundleIdentifier raw "$APP_PATH/Contents/Info.plist" | grep -qx 'ink.hack.voxit' +plutil -extract CFBundleIconFile raw "$APP_PATH/Contents/Info.plist" | grep -qx 'AppIcon' +plutil -extract LSUIElement raw "$APP_PATH/Contents/Info.plist" | grep -qx 'true' +''' + # Format # | task | type | cwd | @@ -132,6 +229,8 @@ args = [ # | fmt-check | composite | | # | fmt-rust | command | | # | fmt-rust-check | extend | | +# | fmt-swift | command | | +# | fmt-swift-check| command | | # | fmt-toml | command | | # | fmt-toml-check | extend | | @@ -139,6 +238,7 @@ args = [ workspace = false dependencies = [ "fmt-rust", + "fmt-swift", "fmt-toml", ] @@ -146,6 +246,7 @@ dependencies = [ workspace = false dependencies = [ "fmt-rust-check", + "fmt-swift-check", "fmt-toml-check", ] @@ -157,6 +258,22 @@ script = "cargo +nightly fmt --all" extend = "fmt-rust" script = "cargo +nightly fmt --all -- --check" +[tasks.fmt-swift] +workspace = false +command = "swift" +args = [ + "format", + "format", + "--in-place", + "--recursive", + "--parallel", + "native/macos-host/Sources", +] + +[tasks.fmt-swift-check] +workspace = false +extend = "lint-swift-format" + [tasks.fmt-toml] workspace = false command = "taplo" diff --git a/README.md b/README.md index 4bee064..c224e74 100644 --- a/README.md +++ b/README.md @@ -18,8 +18,8 @@ AI dictation App for macOS (MVP scaffold). ### What is implemented in v1 -- Menubar dictation app on macOS with start/stop hotkey control. -- ChatGPT login flow with browser OAuth as default and device-code fallback (if needed). +- Swift menu bar dictation app on macOS with start/stop hotkey control. +- ChatGPT login flow through OAuth device-code authorization. - Real-time pass-1 transcription from mic with committed/draft streaming assembly. - Pass-2 finalize pass using `gpt-4o-transcribe` for better punctuation and stability. - Optional Pass-3 rewrite for cleaner English output with numeric/proper noun protection. @@ -37,7 +37,7 @@ V1 target is **macOS-first** and aligned to the English-only voice input design. - Scope: ✅ Native macOS mic capture + OpenAI model pipeline only. - Limitation: ✅ Linux/Windows build is intentionally disabled. - Limitation: ⚠️ Known gaps are documented in the - [Runtime Spec](docs/spec/runtime.md) (hotkey configurability, tray menu behavior, + [Runtime Spec](docs/spec/runtime.md) (runtime action wiring, config write-through, CPAL fallback robustness, and rollout cleanup items). ## Usage @@ -61,14 +61,11 @@ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-t sudo apt-get update sudo apt-get install -# Build the voxit package, and the binary will be available at `target/release/voxit`. -cargo build --release -p voxit +# Build the Swift native host and stage `Voxit.app`. +./scripts/build_and_run.sh stage -# If you are a macOS user and want to have a `Voxit.app`, run the following command. -# Install `cargo-bundle` to pack the binary into an app. -cargo install cargo-bundle -# Pack the app, and the it will be available at `target/release/bundle/osx/Voxit.app`. -cargo bundle --release -p voxit +# Or build, stage, and launch the local app bundle. +./scripts/build_and_run.sh run ``` #### Download Pre-built Binary @@ -138,7 +135,9 @@ First-run onboarding checklist: For the full guided sequence, see [First Run](docs/runbook/first-run.md). -The app saves updates to the same `config.toml` path when settings are changed. +Runtime configuration remains sourced from `config.toml`. The current Swift Settings +window persists shell preferences in macOS `UserDefaults`; writing those settings back +through the Rust config path is a tracked runtime gap. ### Interaction @@ -166,7 +165,9 @@ The app saves updates to the same `config.toml` path when settings are changed. ### Implementation snapshot -- `eframe/egui` panel + menubar entrypoint. +- Current app: Swift/SwiftUI menu bar host under `native/macos-host/`. +- Rust Core remains the runtime owner, exposed to Swift through C ABI glue under + `packages/voxit-host-ffi/`. - Dedicated auth/session/config/rewrite/paste pipeline and typed application state. - macOS frontmost-app capture + clipboard/command-paste integration. diff --git a/apps/voxit/Cargo.toml b/apps/voxit/Cargo.toml deleted file mode 100644 index 85863cf..0000000 --- a/apps/voxit/Cargo.toml +++ /dev/null @@ -1,45 +0,0 @@ -[package] -authors.workspace = true -build = "build.rs" -categories = [] -description.workspace = true -edition.workspace = true -homepage.workspace = true -keywords = [] -license.workspace = true -name = "voxit" -readme.workspace = true -repository.workspace = true -resolver = "3" -version.workspace = true - -[package.metadata.docs.rs] -all-features = true - -[package.metadata.bundle] -identifier = "ink.hack.voxit" -name = "Voxit" -osx_info_plist_exts = ["apps/voxit/osx/Info.plist.ext"] - -[features] -default = [] - -[dependencies] -color-eyre = { version = "0.6" } -directories = { version = "6.0" } -eframe = { version = "0.33" } -global-hotkey = { version = "0.7", default-features = false } -tracing = { version = "0.1" } -tracing-appender = { version = "0.2" } -tracing-subscriber = { version = "0.3", features = ["env-filter"] } -voxit-audio = { path = "../../packages/voxit-audio" } -voxit-core = { path = "../../packages/voxit-core" } -voxit-macos = { path = "../../packages/voxit-macos" } - -[build-dependencies] -vergen-gitcl = { version = "9.1", features = ["cargo"] } - -[target.'cfg(target_os = "macos")'.dependencies] -arboard = "3.6.1" -enigo = "0.6.1" -tray-icon = "0.21" diff --git a/apps/voxit/build.rs b/apps/voxit/build.rs deleted file mode 100644 index 6cbe3eb..0000000 --- a/apps/voxit/build.rs +++ /dev/null @@ -1,20 +0,0 @@ -#![allow(missing_docs)] - -use std::error::Error; - -use vergen_gitcl::{CargoBuilder, Emitter, GitclBuilder}; - -fn main() -> Result<(), Box> { - let mut emitter = Emitter::default(); - - emitter.add_instructions(&CargoBuilder::default().target_triple(true).build()?)?; - - // Disable the git version if installed from . - if emitter.add_instructions(&GitclBuilder::default().sha(true).build()?).is_err() { - println!("cargo:rustc-env=VERGEN_GIT_SHA=crates.io"); - } - - emitter.emit()?; - - Ok(()) -} diff --git a/apps/voxit/osx/Info.plist.ext b/apps/voxit/osx/Info.plist.ext deleted file mode 100644 index 39c7ea6..0000000 --- a/apps/voxit/osx/Info.plist.ext +++ /dev/null @@ -1,4 +0,0 @@ - LSUIElement - - NSMicrophoneUsageDescription - Voxit needs microphone access to transcribe your speech. diff --git a/apps/voxit/src/hotkey_macos.rs b/apps/voxit/src/hotkey_macos.rs deleted file mode 100644 index f0cbc03..0000000 --- a/apps/voxit/src/hotkey_macos.rs +++ /dev/null @@ -1,64 +0,0 @@ -use std::{ - sync::{ - Arc, - atomic::{AtomicU8, Ordering}, - mpsc::Sender, - }, - thread, -}; - -use global_hotkey::{ - GlobalHotKeyEvent, GlobalHotKeyEventReceiver, GlobalHotKeyManager, HotKeyState, - hotkey::{Code, HotKey, Modifiers}, -}; - -use crate::{AppCommand, HotkeyMode, prelude::Result}; - -pub(crate) fn spawn_global_hotkey_listener( - command_tx: Sender, - mode: Arc, -) -> Result { - let manager = GlobalHotKeyManager::new().map_err(|err| { - crate::prelude::eyre!("Failed to initialize global hotkey manager: {err}") - })?; - let hotkey = HotKey::new(Some(Modifiers::CONTROL | Modifiers::SHIFT), Code::Space); - - manager.register(hotkey).map_err(|err| { - crate::prelude::eyre!("Failed to register global hotkey (Ctrl+Shift+Space): {err}") - })?; - - let hotkey_id = hotkey.id(); - let receiver: GlobalHotKeyEventReceiver = GlobalHotKeyEvent::receiver().clone(); - - thread::spawn(move || { - let mut is_holding = false; - - while let Ok(event) = receiver.recv() { - if event.id() != hotkey_id { - continue; - } - - match event.state() { - HotKeyState::Pressed => match HotkeyMode::from_u8(mode.load(Ordering::Acquire)) { - HotkeyMode::Toggle => { - let _ = command_tx.send(AppCommand::ToggleRecording); - }, - HotkeyMode::Hold => - if !is_holding { - is_holding = true; - - let _ = command_tx.send(AppCommand::StartRecording); - }, - }, - HotKeyState::Released => - if is_holding { - is_holding = false; - - let _ = command_tx.send(AppCommand::StopRecording); - }, - } - } - }); - - Ok(manager) -} diff --git a/apps/voxit/src/main.rs b/apps/voxit/src/main.rs deleted file mode 100644 index 5644b6c..0000000 --- a/apps/voxit/src/main.rs +++ /dev/null @@ -1,1565 +0,0 @@ -//! Voxit app entrypoint. - -#[cfg(target_os = "macos")] mod hotkey_macos; -mod prelude { - pub use color_eyre::eyre::{Result, eyre}; -} - -use std::{ - env, fs, panic, - path::Path, - process, - sync::{ - Arc, - atomic::{AtomicU8, Ordering}, - mpsc::{self, Receiver, Sender}, - }, - thread, - time::{Duration, Instant}, -}; - -use arboard::Clipboard; -use directories::ProjectDirs; -use eframe::{ - App, Frame, - egui::{ - self, Button, CentralPanel, ComboBox, Context, ScrollArea, Ui, ViewportBuilder, - ViewportCommand, - }, -}; -#[cfg(target_os = "macos")] use enigo::{Direction, Enigo, Key, Keyboard, Settings}; -#[cfg(target_os = "macos")] use global_hotkey::GlobalHotKeyManager; -use tracing_appender::rolling::{RollingFileAppender, Rotation}; -use tracing_subscriber::EnvFilter; -#[cfg(target_os = "macos")] -use tray_icon::{ - TrayIcon, TrayIconBuilder, - menu::{ - Menu, MenuEvent, MenuItem, PredefinedMenuItem, - accelerator::{Accelerator, Code, Modifiers}, - }, -}; - -use crate::prelude::Result; -use voxit_audio::{InputDevice, Recorder}; -use voxit_core::{ - auth::{self, AuthRecord, AuthStatus}, - config::Config, - inference::{self, RewriteState}, - realtime::{self, RealtimeEvent, RealtimeSession}, - transcript::TranscriptAssembler, -}; -use voxit_macos::{MicrophonePermissionState, PermissionSettingsPane, TargetApp}; - -#[cfg(target_os = "macos")] -const TRAY_MENU_ITEM_SHOW: &str = "show_window"; -#[cfg(target_os = "macos")] -const TRAY_MENU_ITEM_QUIT: &str = "quit_voxit"; -const PERMISSION_POLL_INTERVAL: Duration = Duration::from_millis(500); -const PERMISSION_POLL_TIMEOUT: Duration = Duration::from_secs(7); - -#[derive(Clone, Copy, Debug)] -struct PermissionPoll { - target: PermissionSettingsPane, - next_check_at: Instant, - deadline: Instant, -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -enum HotkeyMode { - Toggle, - Hold, -} -impl HotkeyMode { - const fn as_u8(self) -> u8 { - match self { - Self::Toggle => 0, - Self::Hold => 1, - } - } - - const fn from_u8(raw: u8) -> Self { - match raw { - 1 => Self::Hold, - _ => Self::Toggle, - } - } - - const fn as_label(self) -> &'static str { - match self { - Self::Toggle => "toggle", - Self::Hold => "hold", - } - } -} - -#[derive(Debug)] -enum AppCommand { - ToggleRecording, - StartRecording, - StopRecording, - ShowWindow, - Quit, -} - -#[derive(Debug)] -enum AuthEvent { - SignedIn(AuthRecord), - StatusChecked(AuthStatus), - DeviceCodeInfo { user_code: String, verification_uri: String }, - Failed(String), -} - -struct VoxitApp { - config: Config, - command_rx: Receiver, - auth_event_rx: Receiver, - auth_event_tx: Sender, - realtime_event_rx: Receiver, - realtime_event_tx: Sender, - inference_tx: Sender, - inference_rx: Receiver, - is_recording: bool, - is_window_visible: bool, - state: String, - is_finalizing: bool, - is_rewriting: bool, - ignore_rewrite_result: bool, - auth_status_refresh_started: bool, - auth_status_checked_once: bool, - status: String, - auth_status: String, - auth_signed_in: bool, - auth_busy: bool, - hotkey_mode: HotkeyMode, - hotkey_mode_u8: Arc, - stream_committed: String, - stream_draft: String, - transcript_assembler: TranscriptAssembler, - transcription_result: String, - rewritten_result: String, - rewrite_enabled: bool, - microphone_permission_state: MicrophonePermissionState, - microphone_checked: bool, - accessibility_checked: bool, - permission_poll: Option, - audio_input_devices: Vec, - device_code_user_code: Option, - device_code_verification_uri: Option, - recording: Option, - realtime_session: Option, - target_app: Option, - #[cfg(target_os = "macos")] - _hotkey_manager: Option, - #[cfg(target_os = "macos")] - _tray_icon: TrayIcon, -} -impl VoxitApp { - #[cfg(not(target_os = "macos"))] - #[allow(clippy::too_many_arguments)] - fn new( - config: Config, - command_rx: Receiver, - auth_event_rx: Receiver, - auth_event_tx: Sender, - realtime_event_rx: Receiver, - realtime_event_tx: Sender, - inference_tx: Sender, - inference_rx: Receiver, - hotkey_mode_u8: Arc, - ) -> Self { - let start_hidden = config.ui.start_hidden; - let hotkey_mode = match config.hotkey.mode.as_str() { - "hold" => HotkeyMode::Hold, - _ => HotkeyMode::Toggle, - }; - let rewrite_enabled = config.rewrite.enabled; - let mut app = Self { - config, - auth_event_tx, - auth_event_rx, - realtime_event_rx, - realtime_event_tx, - inference_tx, - inference_rx, - command_rx, - hotkey_mode_u8, - is_recording: false, - is_window_visible: !start_hidden, - is_finalizing: false, - is_rewriting: false, - ignore_rewrite_result: false, - auth_status_refresh_started: false, - auth_status_checked_once: false, - hotkey_mode, - state: "Ready to listen.".to_string(), - status: "No action yet.".to_string(), - auth_status: "Checking auth...".to_string(), - auth_signed_in: false, - auth_busy: true, - stream_committed: String::new(), - stream_draft: String::new(), - transcript_assembler: TranscriptAssembler::new(), - transcription_result: String::new(), - rewritten_result: String::new(), - rewrite_enabled, - microphone_permission_state: voxit_macos::microphone_permission_state(), - microphone_checked: false, - accessibility_checked: voxit_macos::permission_is_granted( - PermissionSettingsPane::Accessibility, - ), - permission_poll: None, - audio_input_devices: Vec::new(), - device_code_user_code: None, - device_code_verification_uri: None, - recording: None, - realtime_session: None, - target_app: None, - }; - - app.refresh_input_devices(); - app.refresh_permission_checks(); - app.refresh_auth_status_if_needed(); - - app - } - - fn handle_commands(&mut self, ctx: &Context) { - self.handle_auth_events(ctx); - self.handle_realtime_events(); - self.handle_inference_events(); - - while let Ok(command) = self.command_rx.try_recv() { - match command { - AppCommand::ToggleRecording => self.toggle_recording(), - AppCommand::StartRecording => self.start_recording(), - AppCommand::StopRecording => self.stop_recording(), - AppCommand::ShowWindow => self.show_window(ctx), - AppCommand::Quit => self.quit_app(ctx), - } - } - - if self.is_window_visible && !self.auth_status_checked_once { - self.refresh_auth_status_if_needed(); - } - } - - fn set_hotkey_mode(&mut self, mode: HotkeyMode) { - self.hotkey_mode = mode; - self.config.hotkey.mode = mode.as_label().to_string(); - - self.persist_config(); - } - - fn persist_config(&self) { - if let Err(err) = self.config.save() { - tracing::warn!(error = %err, "failed to persist config"); - } - } - - fn configured_input_device_id(&self) -> Option { - (self.config.audio.input_device_id != 0).then_some(self.config.audio.input_device_id) - } - - fn sync_input_device_name(&mut self) { - let Some(selected_id) = self.configured_input_device_id() else { - self.config.audio.input_device_name.clear(); - - return; - }; - - if let Some(device) = - self.audio_input_devices.iter().find(|device| device.device_id == selected_id) - { - self.config.audio.input_device_name = device.name.clone(); - } - } - - fn refresh_input_devices(&mut self) { - match voxit_audio::list_input_devices() { - Ok(devices) => { - self.audio_input_devices = devices; - - self.sync_input_device_name(); - - self.status = "Microphone list refreshed.".to_string(); - }, - Err(err) => { - self.audio_input_devices.clear(); - - tracing::warn!(error = %err, "failed to list input audio devices"); - - if self.status == "No action yet." { - self.status = "Microphone list unavailable in this environment.".to_string(); - } - }, - } - } - - fn selected_input_device_label(&self) -> String { - let selected_id = self.config.audio.input_device_id; - - if selected_id == 0 { - return "System default".to_string(); - } - - if let Some(device) = - self.audio_input_devices.iter().find(|device| device.device_id == selected_id) - { - return format!("{} ({})", device.name, selected_id); - } - - if self.config.audio.input_device_name.is_empty() { - format!("Device #{selected_id}") - } else { - format!("{} ({})", self.config.audio.input_device_name, selected_id) - } - } - - fn refresh_auth_status_if_needed(&mut self) { - if self.auth_status_refresh_started { - return; - } - - tracing::info!("auth status refresh starting"); - - self.auth_status_refresh_started = true; - self.auth_busy = true; - self.auth_status = "Checking auth...".to_string(); - - #[cfg(target_os = "macos")] - let _ = voxit_macos::activate_current_application(); - let tx = self.auth_event_tx.clone(); - let _ = thread::spawn(move || { - let status = panic::catch_unwind(auth::status).unwrap_or_else(|_| { - tracing::error!("auth status check panicked"); - - AuthStatus { signed_in: false, account_id: None } - }); - let _ = tx.send(AuthEvent::StatusChecked(status)); - }); - } - - fn handle_auth_events(&mut self, ctx: &Context) { - let mut did_update = false; - - while let Ok(event) = self.auth_event_rx.try_recv() { - did_update = true; - - match event { - AuthEvent::SignedIn(record) => { - self.device_code_user_code = None; - self.device_code_verification_uri = None; - self.auth_busy = false; - self.auth_signed_in = true; - self.auth_status = record - .account_id - .map_or_else(|| "Signed in".to_string(), |id| format!("Signed in: {id}")); - }, - AuthEvent::StatusChecked(status) => { - tracing::info!( - signed_in = status.signed_in, - has_account_id = status.account_id.is_some(), - "auth status refresh completed" - ); - - self.auth_status_refresh_started = false; - self.auth_status_checked_once = true; - self.auth_busy = false; - self.auth_signed_in = status.signed_in; - self.auth_status = if status.signed_in { - status.account_id.map_or_else( - || "Signed in".to_string(), - |id| format!("Signed in: {id}"), - ) - } else { - "Not signed in".to_string() - }; - }, - AuthEvent::DeviceCodeInfo { user_code, verification_uri } => { - self.device_code_user_code = Some(user_code); - self.device_code_verification_uri = Some(verification_uri); - self.auth_busy = true; - self.status = - "Device code shown in panel. Open the URL and enter the code.".to_string(); - }, - AuthEvent::Failed(err) => { - self.device_code_user_code = None; - self.device_code_verification_uri = None; - self.auth_busy = false; - self.status = format!("Auth failed: {err}"); - }, - } - } - - if did_update { - ctx.request_repaint(); - } - } - - fn refresh_permission_checks(&mut self) { - self.microphone_permission_state = voxit_macos::microphone_permission_state(); - self.microphone_checked = - voxit_macos::permission_is_granted(PermissionSettingsPane::Microphone); - self.accessibility_checked = - voxit_macos::permission_is_granted(PermissionSettingsPane::Accessibility); - } - - fn should_poll_permission(&self, target: PermissionSettingsPane) -> bool { - match target { - PermissionSettingsPane::Microphone => !matches!( - self.microphone_permission_state, - MicrophonePermissionState::Granted - | MicrophonePermissionState::Denied - | MicrophonePermissionState::Restricted - ), - PermissionSettingsPane::Accessibility => !self.accessibility_checked, - } - } - - fn start_permission_poll_if_needed(&mut self, target: PermissionSettingsPane) { - if self.should_poll_permission(target) { - let now = Instant::now(); - - self.permission_poll = Some(PermissionPoll { - target, - next_check_at: now, - deadline: now + PERMISSION_POLL_TIMEOUT, - }); - } else { - self.permission_poll = None; - } - } - - fn stop_permission_poll(&mut self) { - self.permission_poll = None; - } - - fn is_permission_polling_done(&self, target: PermissionSettingsPane) -> bool { - match target { - PermissionSettingsPane::Microphone => { - matches!( - self.microphone_permission_state, - MicrophonePermissionState::Granted - | MicrophonePermissionState::Denied - | MicrophonePermissionState::Restricted - ) - }, - PermissionSettingsPane::Accessibility => self.accessibility_checked, - } - } - - fn handle_permission_poll(&mut self, ctx: &Context) { - let Some(poll) = self.permission_poll else { - return; - }; - let now = Instant::now(); - - if now >= poll.deadline { - self.stop_permission_poll(); - - return; - } - if now < poll.next_check_at { - ctx.request_repaint_after(poll.next_check_at - now); - - return; - } - - self.refresh_permission_checks(); - - if self.is_permission_polling_done(poll.target) { - self.stop_permission_poll(); - } else { - self.permission_poll = Some(PermissionPoll { - target: poll.target, - next_check_at: now + PERMISSION_POLL_INTERVAL, - deadline: poll.deadline, - }); - - ctx.request_repaint_after(PERMISSION_POLL_INTERVAL); - } - } - - fn microphone_status_text(&self) -> &'static str { - match self.microphone_permission_state { - MicrophonePermissionState::Granted => "granted", - MicrophonePermissionState::NotDetermined => "not requested", - MicrophonePermissionState::Denied => "denied", - MicrophonePermissionState::Restricted => "restricted", - MicrophonePermissionState::Prompted => "prompted", - MicrophonePermissionState::Unknown => "unknown", - } - } - - fn start_sign_in_with_chatgpt(&mut self) { - if self.auth_busy { - return; - } - - self.auth_busy = true; - self.device_code_user_code = None; - self.device_code_verification_uri = None; - self.status = "Starting ChatGPT device code login...".to_string(); - - let tx = self.auth_event_tx.clone(); - #[cfg(target_os = "macos")] - let _ = voxit_macos::activate_current_application(); - - thread::spawn(move || { - let event = match auth::sign_in_with_chatgpt(|user_code, verification_uri| { - let _ = tx.send(AuthEvent::DeviceCodeInfo { - user_code: user_code.to_string(), - verification_uri: verification_uri.to_string(), - }); - }) { - Ok(record) => AuthEvent::SignedIn(record), - Err(err) => AuthEvent::Failed(err), - }; - let _ = tx.send(event); - }); - } - - fn sign_out(&mut self) { - self.device_code_user_code = None; - self.device_code_verification_uri = None; - self.auth_status = match auth::sign_out() { - Ok(()) => { - self.auth_signed_in = false; - - "Not signed in".to_string() - }, - Err(err) => { - self.status = format!("Sign out failed: {err}"); - - self.auth_status.clone() - }, - }; - } - - fn handle_realtime_events(&mut self) { - while let Ok(event) = self.realtime_event_rx.try_recv() { - match event { - realtime::RealtimeEvent::Draft(transcript) - | realtime::RealtimeEvent::Committed(transcript) => { - self.transcript_assembler.apply(transcript); - - let snapshot = self.transcript_assembler.snapshot(); - - self.stream_committed = snapshot.committed; - self.stream_draft = snapshot.draft; - }, - realtime::RealtimeEvent::StreamError(err) => { - self.status = format!("Realtime stream degraded: {err}"); - }, - } - } - } - - fn handle_inference_events(&mut self) { - while let Ok(event) = self.inference_rx.try_recv() { - match event { - inference::InferenceEvent::Pass2Completed { total_ms, raw_transcript } => { - self.is_finalizing = false; - self.transcription_result = raw_transcript; - self.state = "FinalizingPass2".to_string(); - self.status = format!( - "Pass2 completed in {total_ms} ms ({} chars).", - self.transcription_result.len() - ); - - if self.rewrite_enabled && self.config.rewrite.auto { - self.start_rewrite_pass(); - } else { - let text = self.transcription_result.clone(); - let paste_status = self.paste_transcript(&text); - - self.state = "Done".to_string(); - self.status = format!("Pass2 completed in {total_ms} ms. {paste_status}"); - } - }, - inference::InferenceEvent::RewriteCompleted { total_ms, result } => { - self.is_rewriting = false; - - if self.ignore_rewrite_result { - self.status = format!( - "Rewrite finished in {total_ms} ms but was ignored because raw transcript was already pasted." - ); - - continue; - } - - match result.state { - RewriteState::Applied => { - self.rewritten_result = result.rewritten_transcript.unwrap_or_default(); - - let final_text = if self.rewritten_result.is_empty() { - self.transcription_result.clone() - } else { - self.rewritten_result.clone() - }; - let paste_status = self.paste_transcript(&final_text); - - self.state = "Done".to_string(); - self.status = format!( - "Pass3 completed in {total_ms} ms. {paste_status} (raw {} chars, rewritten {} chars)", - self.transcription_result.len(), - self.rewritten_result.len() - ); - }, - RewriteState::Rejected | RewriteState::Skipped => { - let fallback_text = self.transcription_result.clone(); - let paste_status = self.paste_transcript(&fallback_text); - - self.state = "Done".to_string(); - self.status = format!( - "Pass3 skipped/rejected in {total_ms} ms. {} {}", - result.reason.unwrap_or_default(), - paste_status - ); - }, - } - }, - inference::InferenceEvent::Failed(err) => { - self.is_finalizing = false; - self.is_rewriting = false; - self.status = format!("OpenAI failed: {err}"); - }, - } - } - } - - fn toggle_recording(&mut self) { - if self.is_recording { - self.stop_recording(); - } else { - self.start_recording(); - } - } - - fn start_recording(&mut self) { - if self.is_recording { - return; - } - - self.refresh_permission_checks(); - - if !self.microphone_checked { - self.status = - "Microphone permission not granted. Open Preferences and request it first." - .to_string(); - - return; - } - - self.transcript_assembler.reset(); - self.stream_committed.clear(); - self.stream_draft.clear(); - self.transcription_result.clear(); - self.rewritten_result.clear(); - - self.ignore_rewrite_result = false; - self.target_app = if self.config.paste.lock_frontmost_app { - voxit_macos::capture_frontmost_app() - } else { - None - }; - - match voxit_audio::start_recording_with_stream(64, self.configured_input_device_id()) { - Ok((recorder, chunk_rx, device_selection)) => { - self.recording = Some(recorder); - - let used_fallback = device_selection.fallback_to_default - && device_selection.requested_device_id.is_some(); - let fallback_prefix = if used_fallback { - format!( - "Selected microphone unavailable. Falling back to default: {}.", - device_selection.selected_device_name - ) - } else { - String::new() - }; - - if used_fallback { - tracing::warn!( - requested_device_id = device_selection.requested_device_id, - selected_device_id = device_selection.selected_device_id, - selected_device_name = device_selection.selected_device_name.as_str(), - "input microphone unavailable; fell back to system default" - ); - } - - let realtime_config = realtime::RealtimeSessionConfig { - model: self.config.openai.realtime_model.clone(), - sample_rate_hz: self.config.audio.realtime_target_rate_hz, - noise_reduction: self.config.openai.realtime.noise_reduction.clone(), - }; - let realtime_status = match inference::start_realtime_session( - realtime_config, - chunk_rx, - self.realtime_event_tx.clone(), - ) { - Ok(session) => { - self.realtime_session = Some(session); - - None - }, - Err(err) => Some(format!( - "Realtime unavailable ({err}). Recording continues with Pass2 finalize." - )), - }; - - self.is_recording = true; - self.is_finalizing = false; - self.is_rewriting = false; - self.state = "Listening".to_string(); - - let mut started_status = - "Recording started. Pass1 streaming will appear in the panel.".to_string(); - - if used_fallback { - started_status = format!("{fallback_prefix} {started_status}"); - } - - if let Some(realtime_status) = realtime_status { - started_status = format!("{started_status} {realtime_status}"); - } - - self.status = started_status; - }, - Err(err) => { - self.is_recording = false; - self.status = format!("Failed to start recording: {err}"); - - tracing::error!(error = %err, "start recording failed"); - }, - } - } - - fn stop_recording(&mut self) { - if !self.is_recording { - self.state = "Idle".to_string(); - self.status = "Not recording. Press start to capture again.".to_string(); - - return; - } - - self.is_recording = false; - - if let Some(session) = self.realtime_session.take() { - session.stop(); - } - - #[cfg(target_os = "macos")] - { - let Some(recorder) = self.recording.take() else { - self.status = "No active recording session.".to_string(); - - return; - }; - let (frames_captured, wav) = match voxit_audio::stop_recording(recorder) { - Ok(result) => { - self.state = "Stopped".to_string(); - self.status = format!( - "Captured {} frames in {} ms at {}Hz, {} ch.", - result.frames, result.duration_ms, result.sample_rate, result.channels - ); - - tracing::info!( - duration_ms = result.duration_ms, - frames = result.frames, - sample_rate = result.sample_rate, - channels = result.channels, - wav_bytes = result.wav.len(), - "Recorded audio" - ); - - (result.frames, result.wav) - }, - Err(err) => { - let err_msg = err.to_string(); - - self.status = format!("Failed to stop recording: {err_msg}"); - - return; - }, - }; - let is_empty_capture = frames_captured == 0 || wav.len() <= 44 || wav.is_empty(); - - if is_empty_capture { - self.status = "No valid audio captured; skipping transcription.".to_string(); - - return; - } - - let tx = self.inference_tx.clone(); - let model = self.config.openai.finalize_model.clone(); - - self.is_finalizing = true; - self.status = "Finalizing Pass2 transcript...".to_string(); - thread::spawn(move || { - let outcome = inference::transcribe_only(&wav, &model) - .map(|(raw_transcript, total_ms)| inference::InferenceEvent::Pass2Completed { - total_ms, - raw_transcript, - }) - .unwrap_or_else(inference::InferenceEvent::Failed); - let _ = tx.send(outcome); - }); - } - #[cfg(not(target_os = "macos"))] - { - self.status = "This build does not support recording.".to_string(); - } - } - - fn start_rewrite_pass(&mut self) { - if self.transcription_result.is_empty() { - return; - } - - self.is_rewriting = true; - self.state = "RewritingPass3".to_string(); - self.status = "Running Pass3 rewrite...".to_string(); - - let tx = self.inference_tx.clone(); - let model = self.config.openai.rewrite_model.clone(); - let raw = self.transcription_result.clone(); - - thread::spawn(move || { - let outcome = inference::rewrite_only(&raw, &model) - .map(|(result, total_ms)| inference::InferenceEvent::RewriteCompleted { - total_ms, - result, - }) - .unwrap_or_else(inference::InferenceEvent::Failed); - let _ = tx.send(outcome); - }); - } - - fn paste_transcript(&mut self, text: &str) -> String { - if text.is_empty() { - return "No text to paste.".to_string(); - } - if self.config.paste.lock_frontmost_app - && let Some(target_app) = self.target_app.as_ref() - { - let activated = voxit_macos::activate_target(target_app, 4, Duration::from_millis(80)); - - if !activated { - tracing::warn!("failed to re-activate captured frontmost app before paste"); - } - } - - match paste_text(text) { - Ok(()) => { - self.accessibility_checked = true; - - "Pasted transcript into target app.".to_string() - }, - Err(err) => format!("Paste failed: {err}"), - } - } - - fn request_permission(&mut self, pane: PermissionSettingsPane) { - tracing::info!( - ?pane, - microphone_checked = self.microphone_checked, - accessibility_checked = self.accessibility_checked, - "requesting macOS permission" - ); - - let pane_name = pane.display_name(); - - match pane { - PermissionSettingsPane::Microphone => { - let mic_state = voxit_macos::request_microphone_permission(); - - self.refresh_permission_checks(); - - self.microphone_permission_state = voxit_macos::microphone_permission_state(); - - if self.microphone_permission_state == MicrophonePermissionState::Unknown { - self.microphone_permission_state = mic_state; - } - - self.status = match mic_state { - MicrophonePermissionState::Granted => { - format!("{pane_name} permission granted.") - }, - MicrophonePermissionState::Prompted => format!( - "{pane_name} permission prompt shown. Approve it in macOS, then reopen Preferences." - ), - MicrophonePermissionState::NotDetermined => format!( - "{pane_name} permission still not requested. Please respond to system prompt." - ), - MicrophonePermissionState::Denied => format!( - "{pane_name} permission denied. Open System Settings > Privacy & Security and enable it." - ), - MicrophonePermissionState::Restricted => { - format!("{pane_name} permission is restricted by system policy.") - }, - MicrophonePermissionState::Unknown => { - format!("{pane_name} permission status is unknown.") - }, - }; - - self.start_permission_poll_if_needed(PermissionSettingsPane::Microphone); - }, - PermissionSettingsPane::Accessibility => { - let granted = if self.accessibility_checked { - true - } else { - voxit_macos::request_permission(PermissionSettingsPane::Accessibility) - }; - - self.accessibility_checked = granted; - - self.refresh_permission_checks(); - - self.status = if granted { - format!("{pane_name} permission granted.") - } else { - format!("{pane_name} permission not granted. Enable it in System Settings.") - }; - - self.start_permission_poll_if_needed(PermissionSettingsPane::Accessibility); - }, - } - } - - fn render_auth_section(&mut self, ui: &mut Ui) { - ui.heading("Voxit"); - ui.label("macOS tray, global hotkey, Pass1 stream, Pass2 finalize, Pass3 guarded rewrite."); - ui.separator(); - - ui.horizontal(|ui| { - ui.label(format!("Auth: {}", self.auth_status)); - - if let (Some(code), Some(uri)) = ( - self.device_code_user_code.as_deref(), - self.device_code_verification_uri.as_deref(), - ) { - ui.separator(); - ui.label(format!("Device code: {code}")); - ui.label(format!("Verify at: {uri}")); - } - - if self.auth_busy { - ui.separator(); - ui.label("Authenticating..."); - } - }); - ui.horizontal_wrapped(|ui| { - let can_auth = !self.auth_busy; - - if self.auth_signed_in { - if ui.add_enabled(can_auth, Button::new("Sign out")).clicked() { - self.sign_out(); - } - } else { - if ui.add_enabled(can_auth, Button::new("Sign in with ChatGPT")).clicked() { - self.start_sign_in_with_chatgpt(); - } - } - }); - } - - fn render_runtime_controls(&mut self, ui: &mut Ui) { - ui.separator(); - ui.horizontal(|ui| { - ui.label(format!("Status: {}", self.state)); - ui.separator(); - ui.label(format!("Mode: {}", self.hotkey_mode.as_label())); - }); - ui.separator(); - self.render_permissions(ui); - ui.separator(); - self.render_microphone_input(ui); - self.render_dictation_controls(ui); - ui.separator(); - self.render_hotkey_controls(ui); - } - - fn render_permissions(&mut self, ui: &mut Ui) { - ui.label("Permissions:"); - ui.label(format!("Microphone: {}", self.microphone_status_text())); - - if !self.microphone_checked && ui.button("Request Microphone permission").clicked() { - self.request_permission(PermissionSettingsPane::Microphone); - } - - ui.label(format!( - "Accessibility (Cmd+V): {}", - if self.accessibility_checked { "granted" } else { "missing" } - )); - - if !self.accessibility_checked && ui.button("Request Accessibility permission").clicked() { - self.request_permission(PermissionSettingsPane::Accessibility); - } - } - - fn render_microphone_input(&mut self, ui: &mut Ui) { - ui.label("Microphone input:"); - - ui.horizontal(|ui| { - if ui.button("Refresh microphones").clicked() { - self.refresh_input_devices(); - } - - let mut selected_device_id = self.config.audio.input_device_id; - let mut changed = false; - - ComboBox::from_label("Input device") - .selected_text(self.selected_input_device_label()) - .show_ui(ui, |ui| { - if ui.selectable_value(&mut selected_device_id, 0, "System default").clicked() { - changed = true; - } - - for device in &self.audio_input_devices { - if ui - .selectable_value( - &mut selected_device_id, - device.device_id, - format!("{} ({})", device.name, device.device_id), - ) - .clicked() - { - changed = true; - } - } - }); - - if changed { - self.config.audio.input_device_id = selected_device_id; - self.config.audio.input_device_name = if selected_device_id == 0 { - String::new() - } else { - self.audio_input_devices - .iter() - .find(|device| device.device_id == selected_device_id) - .map(|device| device.name.clone()) - .unwrap_or_else(|| format!("Device #{selected_device_id}")) - }; - - self.persist_config(); - } - }); - } - - fn render_dictation_controls(&mut self, ui: &mut Ui) { - let button_text = if self.is_recording { "Stop Dictation" } else { "Start Dictation" }; - - if ui.button(button_text).clicked() { - self.toggle_recording(); - } - if self.is_finalizing { - ui.label("Pass2 finalizing in progress..."); - } - if self.is_rewriting { - ui.label("Pass3 rewrite in progress..."); - } - if (self.is_finalizing || self.is_rewriting) - && !self.transcription_result.is_empty() - && ui.button("Paste raw now (skip rewrite)").clicked() - { - self.ignore_rewrite_result = true; - self.is_finalizing = false; - self.is_rewriting = false; - - let fallback = self.transcription_result.clone(); - let paste_status = self.paste_transcript(&fallback); - - self.state = "Done".to_string(); - self.status = format!("Raw transcript pasted. {paste_status}"); - } - } - - fn render_hotkey_controls(&mut self, ui: &mut Ui) { - ui.horizontal_wrapped(|ui| { - ui.label(format!("Hotkey mode: {}", self.hotkey_mode.as_label())); - ui.separator(); - - let original = self.rewrite_enabled; - - ui.checkbox(&mut self.rewrite_enabled, "Auto rewrite after Pass2"); - - if original != self.rewrite_enabled { - self.config.rewrite.enabled = self.rewrite_enabled; - - self.persist_config(); - } - if ui.selectable_label(self.hotkey_mode == HotkeyMode::Toggle, "toggle").clicked() { - self.set_hotkey_mode(HotkeyMode::Toggle); - } - if ui.selectable_label(self.hotkey_mode == HotkeyMode::Hold, "hold").clicked() { - self.set_hotkey_mode(HotkeyMode::Hold); - } - }); - } - - fn render_output_section(&mut self, ui: &mut Ui) { - ui.separator(); - ui.label("Pass1 Stream (Committed):"); - ui.label(self.stream_committed.as_str()); - - if !self.stream_draft.is_empty() { - ui.label("Pass1 Draft:"); - ui.weak(self.stream_draft.as_str()); - } - - ui.separator(); - ui.label("Pass2 Raw Transcript:"); - ui.label(self.transcription_result.as_str()); - - if ui.button("Test Paste").clicked() { - self.accessibility_checked = true; - - let paste_status = self.paste_transcript("Voxit paste test"); - - self.status = format!("Test paste: {paste_status}"); - } - - ui.separator(); - ui.label(format!("Latest: {}", self.status)); - - if !self.rewritten_result.is_empty() { - ui.separator(); - ui.label("Pass3 Rewritten:"); - ui.label(self.rewritten_result.as_str()); - } - } - - fn show_window(&mut self, ctx: &Context) { - self.refresh_permission_checks(); - - self.is_window_visible = true; - - ctx.send_viewport_cmd(ViewportCommand::Visible(true)); - ctx.send_viewport_cmd(ViewportCommand::Focus); - - self.auth_status_refresh_started = false; - self.auth_status_checked_once = false; - self.auth_busy = true; - self.auth_status = "Checking auth...".to_string(); - } - - fn quit_app(&mut self, ctx: &Context) { - ctx.send_viewport_cmd(ViewportCommand::Close); - } -} - -#[cfg(target_os = "macos")] -impl VoxitApp { - #[allow(clippy::too_many_arguments)] - fn new( - config: Config, - command_rx: Receiver, - auth_event_rx: Receiver, - auth_event_tx: Sender, - realtime_event_rx: Receiver, - realtime_event_tx: Sender, - inference_tx: Sender, - inference_rx: Receiver, - hotkey_mode_u8: Arc, - _hotkey_manager: Option, - _tray_icon: TrayIcon, - ) -> Self { - let start_hidden = config.ui.start_hidden; - let hotkey_mode = match config.hotkey.mode.as_str() { - "hold" => HotkeyMode::Hold, - _ => HotkeyMode::Toggle, - }; - let rewrite_enabled = config.rewrite.enabled; - let mut app = Self { - config, - auth_event_tx, - auth_event_rx, - realtime_event_rx, - realtime_event_tx, - inference_tx, - inference_rx, - hotkey_mode_u8, - is_recording: false, - is_window_visible: !start_hidden, - is_finalizing: false, - is_rewriting: false, - ignore_rewrite_result: false, - auth_status_refresh_started: false, - auth_status_checked_once: false, - state: "Ready to listen.".to_string(), - status: "No action yet.".to_string(), - auth_status: "Checking auth...".to_string(), - auth_signed_in: false, - auth_busy: true, - stream_committed: String::new(), - stream_draft: String::new(), - transcript_assembler: TranscriptAssembler::new(), - transcription_result: String::new(), - rewritten_result: String::new(), - rewrite_enabled, - microphone_permission_state: voxit_macos::microphone_permission_state(), - microphone_checked: false, - accessibility_checked: voxit_macos::permission_is_granted( - PermissionSettingsPane::Accessibility, - ), - permission_poll: None, - audio_input_devices: Vec::new(), - device_code_user_code: None, - device_code_verification_uri: None, - command_rx, - hotkey_mode, - recording: None, - realtime_session: None, - target_app: None, - _hotkey_manager, - _tray_icon, - }; - - app.refresh_input_devices(); - app.refresh_permission_checks(); - app.refresh_auth_status_if_needed(); - - app - } -} - -impl App for VoxitApp { - fn update(&mut self, ctx: &Context, _frame: &mut Frame) { - self.handle_commands(ctx); - self.handle_permission_poll(ctx); - #[cfg(target_os = "macos")] - self.hotkey_mode_u8.store(self.hotkey_mode.as_u8(), Ordering::Release); - - CentralPanel::default().show(ctx, |ui| { - ScrollArea::vertical().show(ui, |ui| { - self.render_auth_section(ui); - self.render_runtime_controls(ui); - self.render_output_section(ui); - }); - }); - - if self.is_window_visible && (self.auth_busy || self.is_finalizing || self.is_rewriting) { - ctx.request_repaint_after(Duration::from_millis(100)); - } - } -} - -fn ensure_app_data_dir(app_root: &Path) -> Result<()> { - fs::create_dir_all(app_root).map_err(|err| { - crate::prelude::eyre!("Failed to create app data directory {}: {err}", app_root.display()) - })?; - - Ok(()) -} - -fn main() -> Result<()> { - color_eyre::install()?; - - let project_dirs = ProjectDirs::from("", "hack.ink", "voxit") - .ok_or_else(|| crate::prelude::eyre!("Failed to resolve project directories."))?; - let app_root = project_dirs.data_dir(); - - ensure_app_data_dir(app_root)?; - - let (non_blocking, _guard) = tracing_appender::non_blocking( - RollingFileAppender::builder() - .rotation(Rotation::WEEKLY) - .max_log_files(3) - .filename_suffix("log") - .build(app_root)?, - ); - let filter = EnvFilter::try_from_default_env().unwrap_or_else(|_| EnvFilter::new("info")); - - tracing_subscriber::fmt() - .with_env_filter(filter) - .with_ansi(false) - .with_writer(non_blocking) - .init(); - - let default_hook = panic::take_hook(); - let abort_on_panic = env::var("VOXIT_ABORT_ON_PANIC").is_ok_and(|v| v == "1"); - - panic::set_hook(Box::new(move |p| { - default_hook(p); - - if abort_on_panic { - process::abort(); - } - })); - - let outcome = run_ui(); - - if let Err(err) = outcome.as_ref() { - tracing::error!(error = %err, "ui startup failed"); - } - - outcome -} - -#[cfg(target_os = "macos")] -fn create_tray_icon() -> Result { - let icon = build_tray_icon(); - let menu = create_tray_menu()?; - let tray_icon = TrayIconBuilder::new() - .with_tooltip("Voxit") - .with_icon(icon) - .with_menu(Box::new(menu)) - .build()?; - - Ok(tray_icon) -} - -#[cfg(target_os = "macos")] -fn create_tray_menu() -> Result { - let menu = Menu::new(); - let show_item = MenuItem::with_id( - TRAY_MENU_ITEM_SHOW, - "Preferences…", - true, - Some(Accelerator::new(Some(Modifiers::META), Code::Comma)), - ); - let quit_item = MenuItem::with_id( - TRAY_MENU_ITEM_QUIT, - "Quit Voxit", - true, - Some(Accelerator::new(Some(Modifiers::META), Code::KeyQ)), - ); - - menu.append(&show_item)?; - menu.append(&PredefinedMenuItem::separator())?; - menu.append(&quit_item)?; - - Ok(menu) -} - -#[cfg(target_os = "macos")] -fn build_tray_icon() -> tray_icon::Icon { - let mut pixels = vec![0_u8; 16 * 16 * 4]; - - for (idx, chunk) in pixels.chunks_exact_mut(4).enumerate() { - let x = (idx % 16) as u8; - let y = ((idx / 16) % 16) as u8; - let band = if y.is_multiple_of(2) { 0x12 } else { 0x34 }; - - chunk[0] = 0x66; - chunk[1] = 0x88 + band; - chunk[2] = 0xCC; - chunk[3] = 0xFF; - chunk[0] = chunk[0].saturating_add(x); - chunk[1] = chunk[1].saturating_add(y); - } - - tray_icon::Icon::from_rgba(pixels, 16, 16).expect("tray icon pixels must be valid RGBA") -} - -#[cfg(target_os = "macos")] -fn spawn_tray_listener(command_tx: Sender) { - let receiver = MenuEvent::receiver().clone(); - - thread::spawn(move || { - while let Ok(event) = receiver.recv() { - match event.id.as_ref() { - TRAY_MENU_ITEM_SHOW => { - let _ = command_tx.send(AppCommand::ShowWindow); - }, - TRAY_MENU_ITEM_QUIT => { - let _ = command_tx.send(AppCommand::Quit); - }, - _ => {}, - } - } - }); -} - -#[cfg(target_os = "macos")] -fn spawn_global_hotkey_listener( - command_tx: Sender, - mode: Arc, -) -> Result { - hotkey_macos::spawn_global_hotkey_listener(command_tx, mode) -} - -#[cfg(target_os = "macos")] -fn run_ui() -> Result<()> { - let app_config = Config::load().unwrap_or_else(|err| { - tracing::warn!(error = %err, "failed to load config; using defaults"); - - Config::default() - }); - let (command_tx, command_rx) = mpsc::channel::(); - let (auth_event_tx, auth_event_rx) = mpsc::channel::(); - let (realtime_event_tx, realtime_event_rx) = mpsc::channel::(); - let (inference_tx, inference_rx) = mpsc::channel::(); - let initial_hotkey = - if app_config.hotkey.mode == "hold" { HotkeyMode::Hold } else { HotkeyMode::Toggle }; - let hotkey_mode = Arc::new(AtomicU8::new(initial_hotkey.as_u8())); - let tray_icon = create_tray_icon()?; - - spawn_tray_listener(command_tx.clone()); - - let hotkey_manager = - match spawn_global_hotkey_listener(command_tx.clone(), Arc::clone(&hotkey_mode)) { - Ok(manager) => Some(manager), - Err(err) => { - tracing::warn!(error = %err, "global hotkey disabled (permission missing?)"); - - None - }, - }; - let mut app = VoxitApp::new( - app_config.clone(), - command_rx, - auth_event_rx, - auth_event_tx, - realtime_event_rx, - realtime_event_tx, - inference_tx, - inference_rx, - Arc::clone(&hotkey_mode), - hotkey_manager, - tray_icon, - ); - - if app._hotkey_manager.is_none() { - app.status = - "Global hotkey unavailable. Ensure Accessibility is granted, then restart Voxit." - .to_string(); - } - - let options = eframe::NativeOptions { - viewport: ViewportBuilder::default() - .with_inner_size(egui::vec2( - app_config.ui.panel_width_px as f32, - app_config.ui.panel_height_px as f32, - )) - .with_visible(!app_config.ui.start_hidden), - ..Default::default() - }; - - tracing::info!( - pid = process::id(), - start_hidden = app_config.ui.start_hidden, - panel_width_px = app_config.ui.panel_width_px, - panel_height_px = app_config.ui.panel_height_px, - "starting voxit ui" - ); - - if let Err(err) = eframe::run_native("Voxit", options, Box::new(|_cc| Ok(Box::new(app)))) { - return Err(crate::prelude::eyre!(format!("eframe startup failed: {err}"))); - } - - Ok(()) -} - -#[cfg(not(target_os = "macos"))] -fn run_ui() -> Result<()> { - let app_config = Config::load().unwrap_or_else(|err| { - tracing::warn!(error = %err, "failed to load config; using defaults"); - - Config::default() - }); - let (_command_tx, command_rx) = mpsc::channel::(); - let (auth_event_tx, auth_event_rx) = mpsc::channel::(); - let (realtime_event_tx, realtime_event_rx) = mpsc::channel::(); - let (inference_tx, inference_rx) = mpsc::channel::(); - let initial_hotkey = - if app_config.hotkey.mode == "hold" { HotkeyMode::Hold } else { HotkeyMode::Toggle }; - let hotkey_mode = Arc::new(AtomicU8::new(initial_hotkey.as_u8())); - let app = VoxitApp::new( - app_config.clone(), - command_rx, - auth_event_rx, - auth_event_tx, - realtime_event_rx, - realtime_event_tx, - inference_tx, - inference_rx, - Arc::clone(&hotkey_mode), - ); - let options = eframe::NativeOptions { - viewport: ViewportBuilder::default() - .with_inner_size(egui::vec2( - app_config.ui.panel_width_px as f32, - app_config.ui.panel_height_px as f32, - )) - .with_visible(!app_config.ui.start_hidden), - ..Default::default() - }; - - tracing::info!( - pid = process::id(), - start_hidden = app_config.ui.start_hidden, - panel_width_px = app_config.ui.panel_width_px, - panel_height_px = app_config.ui.panel_height_px, - "starting voxit ui" - ); - - if let Err(err) = eframe::run_native("Voxit", options, Box::new(|_cc| Ok(Box::new(app)))) { - return Err(crate::prelude::eyre!(format!("eframe startup failed: {err}"))); - } - - Ok(()) -} - -#[cfg(target_os = "macos")] -fn paste_text(text: &str) -> Result<(), String> { - let mut clipboard = Clipboard::new().map_err(|err| err.to_string())?; - - clipboard.set_text(text.to_string()).map_err(|err| err.to_string())?; - - simulate_cmd_v().map_err(|err| format!("clipboard set, but paste failed: {err}"))?; - - Ok(()) -} - -#[cfg(target_os = "macos")] -fn simulate_cmd_v() -> Result<(), String> { - let mut enigo = Enigo::new(&Settings::default()).map_err(|err| err.to_string())?; - - enigo.key(Key::Meta, Direction::Press).map_err(|err| err.to_string())?; - - thread::sleep(Duration::from_millis(10)); - - enigo.key(Key::Unicode('v'), Direction::Click).map_err(|err| err.to_string())?; - - thread::sleep(Duration::from_millis(10)); - - enigo.key(Key::Meta, Direction::Release).map_err(|err| err.to_string())?; - - Ok(()) -} - -#[cfg(not(target_os = "macos"))] -fn paste_text(_text: &str) -> Result<(), String> { - Ok(()) -} - -#[cfg(test)] -mod tests { - use std::{ - env, fs, - time::{SystemTime, UNIX_EPOCH}, - }; - - #[test] - fn ensure_app_data_dir_creates_missing_directory() { - let nonce = - SystemTime::now().duration_since(UNIX_EPOCH).expect("time went backwards").as_nanos(); - let base = env::temp_dir().join(format!("voxit-test-{nonce}")); - let app_root = base.join("hack.ink.voxit"); - - if base.exists() { - let _ = fs::remove_dir_all(&base); - } - - assert!(!app_root.exists()); - - crate::ensure_app_data_dir(&app_root).unwrap(); - - assert!(app_root.is_dir()); - - let _ = fs::remove_dir_all(&base); - } -} diff --git a/assets/app-icon/composer/AppIcon.icon/Assets/dock-icon-composer-layer.png b/assets/app-icon/composer/AppIcon.icon/Assets/dock-icon-composer-layer.png new file mode 100644 index 0000000..350a792 Binary files /dev/null and b/assets/app-icon/composer/AppIcon.icon/Assets/dock-icon-composer-layer.png differ diff --git a/assets/app-icon/composer/AppIcon.icon/icon.json b/assets/app-icon/composer/AppIcon.icon/icon.json new file mode 100644 index 0000000..44f6f12 --- /dev/null +++ b/assets/app-icon/composer/AppIcon.icon/icon.json @@ -0,0 +1,34 @@ +{ + "groups" : [ + { + "layers" : [ + { + "blend-mode" : "normal", + "image-name" : "dock-icon-composer-layer.png", + "name" : "dock-icon-composer-layer", + "position" : { + "scale" : 1, + "translation-in-points" : [ + 0, + 0 + ] + } + } + ], + "shadow" : { + "kind" : "neutral", + "opacity" : 0 + }, + "translucency" : { + "enabled" : false, + "value" : 0 + } + } + ], + "supported-platforms" : { + "circles" : [ + "watchOS" + ], + "squares" : "shared" + } +} diff --git a/assets/app-icon/generated/app-icon-default-preview.png b/assets/app-icon/generated/app-icon-default-preview.png new file mode 100644 index 0000000..f390ed0 Binary files /dev/null and b/assets/app-icon/generated/app-icon-default-preview.png differ diff --git a/assets/app-icon/generated/app-icon.icns b/assets/app-icon/generated/app-icon.icns new file mode 100644 index 0000000..8938f86 Binary files /dev/null and b/assets/app-icon/generated/app-icon.icns differ diff --git a/assets/app-icon/source/dock-icon-composer-input.png b/assets/app-icon/source/dock-icon-composer-input.png new file mode 100644 index 0000000..350a792 Binary files /dev/null and b/assets/app-icon/source/dock-icon-composer-input.png differ diff --git a/assets/app-icon/source/dock-icon-original.jpg b/assets/app-icon/source/dock-icon-original.jpg new file mode 100644 index 0000000..d0f05a6 Binary files /dev/null and b/assets/app-icon/source/dock-icon-original.jpg differ diff --git a/assets/app-icon/source/dock-icon-original.png b/assets/app-icon/source/dock-icon-original.png new file mode 100644 index 0000000..350a792 Binary files /dev/null and b/assets/app-icon/source/dock-icon-original.png differ diff --git a/assets/tray-icon/generated/tray-icon-template.png b/assets/tray-icon/generated/tray-icon-template.png new file mode 100644 index 0000000..9190b86 Binary files /dev/null and b/assets/tray-icon/generated/tray-icon-template.png differ diff --git a/assets/tray-icon/source/tray-icon-expanded.png b/assets/tray-icon/source/tray-icon-expanded.png new file mode 100644 index 0000000..b23a72d Binary files /dev/null and b/assets/tray-icon/source/tray-icon-expanded.png differ diff --git a/assets/tray-icon/source/tray-icon-original.jpg b/assets/tray-icon/source/tray-icon-original.jpg new file mode 100644 index 0000000..b98063b Binary files /dev/null and b/assets/tray-icon/source/tray-icon-original.jpg differ diff --git a/assets/tray-icon/source/tray-icon-original.png b/assets/tray-icon/source/tray-icon-original.png new file mode 100644 index 0000000..e44d331 Binary files /dev/null and b/assets/tray-icon/source/tray-icon-original.png differ diff --git a/docs/decisions/index.md b/docs/decisions/index.md index 3bd3dcc..984d4bf 100644 --- a/docs/decisions/index.md +++ b/docs/decisions/index.md @@ -25,4 +25,5 @@ Question this index answers: "why is it shaped this way?" ## Current decisions -- No accepted decision records are currently checked in under this lane. +- [`native-ui-boundary.md`](./native-ui-boundary.md) records the Rust Core plus + platform-native UI boundary for the macOS-first host. diff --git a/docs/decisions/native-ui-boundary.md b/docs/decisions/native-ui-boundary.md new file mode 100644 index 0000000..cebad6a --- /dev/null +++ b/docs/decisions/native-ui-boundary.md @@ -0,0 +1,24 @@ +# Native UI Boundary + +Status: accepted + +Date: 2026-05-05 + +Question: How should Voxit split product logic from platform UI after replacing the +legacy Rust UI shell with a macOS-first native interface? + +Decision: Voxit uses Rust Core plus platform-native UI. Shared runtime contracts, +provider/auth/audio orchestration, transcript assembly, and platform-neutral UI model +types stay in Rust crates. macOS UI lives under `native/macos-host/` as a SwiftPM +package that links a small Rust C ABI from `packages/voxit-host-ffi/`. + +Consequences: + +- The app shell is macOS-only and implemented with Swift/SwiftUI. +- The legacy Rust UI crate and legacy Rust app bundling path are not app authority. +- Swift renders state copied from Rust-owned model snapshots instead of redefining core + state machines. +- The host FFI starts narrow: ABI version, session lifetime, and snapshot copy-out. + Auth, audio, transcription, and paste APIs should cross the boundary only after their + Rust service APIs are intentionally promoted. +- The first auth route exposed to native UI is ChatGPT OAuth via device code. diff --git a/docs/reference/repository-layout.md b/docs/reference/repository-layout.md index cd517ad..7aa49ae 100644 --- a/docs/reference/repository-layout.md +++ b/docs/reference/repository-layout.md @@ -9,29 +9,35 @@ definitions, or documentation topics currently live. Not this document: The normative runtime contract, the first-run operator sequence, or the design rationale behind specific product choices. -Covers: The repository surface map, ownership boundaries, and the role of `apps/`, -`packages/`, `docs/`, `scripts/`, and repository root policy files. +Covers: The repository surface map, ownership boundaries, and the role of +`native/macos-host/`, `packages/`, `docs/`, `scripts/`, and repository root policy +files. ## Top-level surfaces -- `apps/voxit/` holds the application crate and packaging-facing entrypoint for the - macOS app. +- `native/macos-host/` holds the SwiftPM native macOS host. It owns platform UI + composition, the menu bar extra, and links Rust through the host FFI static library. - `packages/voxit-core/` holds the shared runtime logic, auth, OpenAI integration, and - dictation pipeline code. + dictation pipeline code. Platform-neutral UI model types also live here so hosts do + not invent divergent state names. - `packages/voxit-audio/` holds audio-capture specific functionality. +- `packages/voxit-host-ffi/` holds the thin C ABI consumed by `native/macos-host/`. - `packages/voxit-macos/` holds macOS-specific integration surfaces. - `docs/spec/` holds normative runtime and behavior contracts. - `docs/runbook/` holds operator procedures such as onboarding and validation flows. - `docs/reference/` holds current repository and implementation surface maps. - `docs/decisions/` holds durable rationale and tradeoffs behind current design choices. - `Makefile.toml` holds repo-native task names for lint, test, format, and checks. -- `scripts/` holds repository helper scripts such as local macOS packaging helpers. +- `scripts/` holds repository helper scripts such as Swift native-host staging and + local launch helpers. - `.github/workflows/` holds CI and release automation. ## Boundary notes - Runtime authority stays in the application and package crates plus the governing specs under `docs/spec/`. +- Native UI code may depend on `packages/voxit-host-ffi/`, but it must not duplicate + provider/auth/audio runtime policy already owned by Rust core crates. - `docs/runbook/`, `docs/reference/`, and `docs/decisions/` must not override runtime or configuration authority. - `Makefile.toml` is the source of truth for named repository tasks. diff --git a/docs/runbook/first-run.md b/docs/runbook/first-run.md index 4382e42..7b2919f 100644 --- a/docs/runbook/first-run.md +++ b/docs/runbook/first-run.md @@ -28,15 +28,14 @@ Verification: ## 1. Launch Voxit - Start the app from `Voxit.app` or a local debug build. -- If you are building from source, use the workspace manifests under `apps/` and - `packages/` rather than an old single-crate entrypoint. +- If you are building from source, run `./scripts/build_and_run.sh run` from the + repository root to build, stage, and launch the Swift native host. ## 2. Sign in -- Open the auth controls in the panel. -- Use the default browser OAuth flow first. -- Complete the callback flow and return to the app. -- If browser OAuth is unavailable, use the device-code fallback path. +- Open the auth controls in the Voxit window. +- Use the default ChatGPT device-code OAuth flow. +- Complete authorization in the browser using the visible code, then return to the app. ## 3. Grant required macOS permissions @@ -49,13 +48,16 @@ Verification: ## 4. Confirm runtime configuration +- Open **Settings...** from the menu bar menu or press `Cmd+,` to confirm shell + preferences and permission shortcuts are available. - Check the config file at: ```text $HOME/Library/Application Support/voxit/config.toml ``` -- Confirm the default hotkey and audio device settings look reasonable for the machine. +- Confirm the default runtime hotkey and audio device settings look reasonable for the + machine. - If you need an explicit microphone, refresh the device list and select it before the first real dictation run. @@ -69,7 +71,7 @@ $HOME/Library/Application Support/voxit/config.toml ## 6. Failure handling -- If sign-in stalls, reopen the auth surface and retry with the visible-window path. +- If sign-in stalls, reopen the auth surface and retry the device-code flow. - If a permission does not update, grant it in macOS System Settings and then re-check from Voxit. - If paste fails, verify Accessibility and Input Monitoring first before debugging the diff --git a/docs/spec/runtime.md b/docs/spec/runtime.md index 368b6ef..638f4ca 100644 --- a/docs/spec/runtime.md +++ b/docs/spec/runtime.md @@ -21,8 +21,11 @@ Defines: ## 1) Runtime Scope and Boundaries -- Build entrypoint uses `eframe::run_native` and renders an always-on-top panel - controlled by system tray visibility and a global hotkey. +- Build entrypoint is the SwiftPM native macOS host under `native/macos-host/`. +- Voxit uses Rust Core plus a platform host: shared runtime and platform-neutral model + contracts stay in Rust crates, while Swift owns the macOS UI. +- The staged app bundle is a menu bar utility (`LSUIElement = true`) with a SwiftUI + `MenuBarExtra` and an on-demand Voxit window. - The app supports English-first behavior and configuration defaults (`language = "en"`). - No speech is injected into target apps while Pass1 is running; text is only pasted after Pass2 or Pass3 completion. @@ -53,12 +56,12 @@ State transitions: ## 3) Authentication Contract -- Default login is browser OAuth flow to ChatGPT and must open a browser callback page - first. -- Device-code path is available and should be used as fallback or manual fallback. +- Default login is ChatGPT OAuth via device-code authorization. +- Browser callback OAuth is not part of the active V1 login surface. - Token acquisition flow: - - open browser login via OAuth - - exchange authorization token and persist auth locally + - show the device code and verification URL + - poll until ChatGPT authorization completes or fails + - exchange the authorized device session and persist auth locally - fallback path uses `OPENAI_API_KEY` only when no OAuth token exists - Storage: - preferred: keyring @@ -155,14 +158,17 @@ State transitions: - supported mode switch: toggle or hold - currently recognized physical combo: `Ctrl+Shift+Space` - configuration exposes `hotkey.chord` for future use -- Tray behavior: - - left click toggles panel visibility - - no menu-driven start, stop, rewrite, or quit actions are implemented in this - version +- Menu bar behavior: + - `MenuBarExtra` exposes `Open Voxit` (`Cmd+O`), `Settings...` (`Cmd+,`), + `Refresh Status` (`Cmd+R`), and `Quit Voxit` (`Cmd+Q`). + - `Start Dictation` displays the configured dictation shortcut presentation, but + remains disabled until the Swift menu action is wired to the Rust runtime command. + - `Settings...` opens a dedicated AppKit-hosted Settings window. + - The Settings window handles `Cmd+W` to close and `Cmd+Q` to terminate. ## 9) UI and Onboarding Contract -- Panel contains: +- UI contains: - auth status and sign-in actions - runtime controls (start/stop, rewrite toggle, hotkey mode) - live stream sections (committed plus draft) @@ -177,6 +183,9 @@ State transitions: re-check in Voxit before continuing. - "Paste raw now" is always available when finalization or rewrite is active and should bypass Pass3. +- The Swift native host must render platform-neutral Rust model snapshots from + `packages/voxit-core/` through `packages/voxit-host-ffi/` instead of defining a + separate UI state machine. ## 10) Configuration Contract @@ -205,13 +214,19 @@ On load: - non-zero `audio.input_device_id` requests that device; if unavailable at startup, Voxit falls back to default input +Current Swift Settings window: + +- persists shell preferences in macOS `UserDefaults` +- does not yet write those preferences through the Rust `config.toml` path + ## 11) CI and Release - `language.yml` is macOS-only for lint, format, and test checks. - Release packaging matrix is restricted to `aarch64-apple-darwin` and comments out Linux and Windows jobs. -- Packaging uses `cargo bundle --manifest-path apps/voxit/Cargo.toml` (or `cargo bundle - -p voxit`) and zips `target//bundle/osx/Voxit.app` as `voxit-.zip`. +- Packaging uses `scripts/build_and_run.sh stage` to build `packages/voxit-host-ffi`, + build the SwiftPM host, stage `target/voxit-native-host/Voxit.app`, and zip it as + `voxit-.zip`. ## 12) Observability and Logs @@ -222,7 +237,9 @@ On load: ## 13) Known Gaps -- Tray menu controls are not implemented; only click-to-toggle panel exists. +- Swift Settings write-through to `config.toml` is not implemented yet. +- Menu-driven start/stop dictation is visible but not wired to the Rust runtime command + yet. - Configured hotkey chord string is not yet mapped; current hardcoded gesture is `Ctrl+Shift+Space` only. - CPAL fallback capture is not implemented despite a configuration option; only the diff --git a/native/macos-host/Package.swift b/native/macos-host/Package.swift new file mode 100644 index 0000000..f7cfe34 --- /dev/null +++ b/native/macos-host/Package.swift @@ -0,0 +1,59 @@ +// swift-tools-version: 6.0 + +import Foundation +import PackageDescription + +let packageRoot = URL(fileURLWithPath: #filePath).deletingLastPathComponent() +let repoRoot = packageRoot.deletingLastPathComponent().deletingLastPathComponent() +let defaultRustLibDir = repoRoot.appendingPathComponent("target/debug").path +let rustLibDir = ProcessInfo.processInfo.environment["VOXIT_HOST_FFI_LIB_DIR"] ?? defaultRustLibDir + +let package = Package( + name: "VoxitNativeHost", + platforms: [ + .macOS(.v14), + ], + products: [ + .library(name: "VoxitHostBridge", targets: ["VoxitHostBridge"]), + .library(name: "VoxitNativeHostKit", targets: ["VoxitNativeHostKit"]), + .executable(name: "VoxitHostBridgeProbe", targets: ["VoxitHostBridgeProbe"]), + .executable(name: "VoxitNativeHost", targets: ["VoxitNativeHost"]), + ], + targets: [ + .systemLibrary( + name: "CVoxitHostFFI", + path: "Sources/CVoxitHostFFI" + ), + .target( + name: "VoxitHostBridge", + dependencies: ["CVoxitHostFFI"], + linkerSettings: [ + .linkedFramework("AppKit"), + .linkedFramework("AudioToolbox"), + .linkedFramework("CoreAudio"), + .linkedFramework("Security"), + .unsafeFlags([ + "-L", + rustLibDir, + "-lvoxit_host_ffi", + ]), + ] + ), + .target( + name: "VoxitNativeHostKit", + dependencies: ["VoxitHostBridge"], + linkerSettings: [ + .linkedFramework("AppKit"), + .linkedFramework("SwiftUI"), + ] + ), + .executableTarget( + name: "VoxitHostBridgeProbe", + dependencies: ["VoxitHostBridge"] + ), + .executableTarget( + name: "VoxitNativeHost", + dependencies: ["VoxitNativeHostKit"] + ), + ] +) diff --git a/native/macos-host/Sources/CVoxitHostFFI/include/voxit_host_ffi_shim.h b/native/macos-host/Sources/CVoxitHostFFI/include/voxit_host_ffi_shim.h new file mode 100644 index 0000000..3a31e95 --- /dev/null +++ b/native/macos-host/Sources/CVoxitHostFFI/include/voxit_host_ffi_shim.h @@ -0,0 +1,10 @@ +#ifndef VOXIT_HOST_FFI_SHIM_H +#define VOXIT_HOST_FFI_SHIM_H + +#include "../../../../../packages/voxit-host-ffi/include/voxit_host_ffi.h" + +static inline uint32_t voxit_status_code(enum VoxitStatus status) { + return (uint32_t)status; +} + +#endif diff --git a/native/macos-host/Sources/CVoxitHostFFI/module.modulemap b/native/macos-host/Sources/CVoxitHostFFI/module.modulemap new file mode 100644 index 0000000..614bd4a --- /dev/null +++ b/native/macos-host/Sources/CVoxitHostFFI/module.modulemap @@ -0,0 +1,4 @@ +module CVoxitHostFFI [system] { + header "include/voxit_host_ffi_shim.h" + export * +} diff --git a/native/macos-host/Sources/VoxitHostBridge/HostFFI.swift b/native/macos-host/Sources/VoxitHostBridge/HostFFI.swift new file mode 100644 index 0000000..101afbe --- /dev/null +++ b/native/macos-host/Sources/VoxitHostBridge/HostFFI.swift @@ -0,0 +1,212 @@ +import CVoxitHostFFI +import Foundation + +public enum HostPlatform: Equatable, Sendable { + case macOS + case unsupported +} + +public enum AuthMethod: Equatable, Sendable { + case chatGPTDeviceCode +} + +public enum AuthState: Equatable, Sendable { + case checking + case signedOut + case signedIn + case busy +} + +public enum DictationState: Equatable, Sendable { + case idle + case listening + case finalizing + case rewriting + case done +} + +public enum HotkeyMode: Equatable, Sendable { + case toggle + case hold +} + +public struct HostSnapshot: Equatable, Sendable { + public var platform: HostPlatform + public var authMethod: AuthMethod + public var authState: AuthState + public var dictationState: DictationState + public var hotkeyMode: HotkeyMode + public var panelWidth: Int + public var panelHeight: Int + public var rewriteEnabled: Bool + + public init( + platform: HostPlatform, + authMethod: AuthMethod, + authState: AuthState, + dictationState: DictationState, + hotkeyMode: HotkeyMode, + panelWidth: Int, + panelHeight: Int, + rewriteEnabled: Bool + ) { + self.platform = platform + self.authMethod = authMethod + self.authState = authState + self.dictationState = dictationState + self.hotkeyMode = hotkeyMode + self.panelWidth = panelWidth + self.panelHeight = panelHeight + self.rewriteEnabled = rewriteEnabled + } +} + +public enum HostBridgeError: Error, Equatable, CustomStringConvertible { + case abiVersionMismatch(expected: UInt32, actual: UInt32) + case sessionCreationFailed + case ffiStatus(context: String, code: UInt32) + case invalidPlatform(UInt32) + case invalidAuthMethod(UInt32) + case invalidAuthState(UInt32) + case invalidDictationState(UInt32) + case invalidHotkeyMode(UInt32) + + public var description: String { + switch self { + case .abiVersionMismatch(let expected, let actual): + return "FFI ABI mismatch: expected \(expected), got \(actual)" + case .sessionCreationFailed: + return "Failed to create Voxit host session" + case .ffiStatus(let context, let code): + return "FFI status \(code) while \(context)" + case .invalidPlatform(let rawValue): + return "Unknown platform \(rawValue)" + case .invalidAuthMethod(let rawValue): + return "Unknown auth method \(rawValue)" + case .invalidAuthState(let rawValue): + return "Unknown auth state \(rawValue)" + case .invalidDictationState(let rawValue): + return "Unknown dictation state \(rawValue)" + case .invalidHotkeyMode(let rawValue): + return "Unknown hotkey mode \(rawValue)" + } + } +} + +public final class VoxitHostSession { + private let handle: OpaquePointer + + public init() throws { + let actualAbi = voxit_host_ffi_abi_version() + if actualAbi != VOXIT_HOST_FFI_ABI_VERSION { + throw HostBridgeError.abiVersionMismatch( + expected: VOXIT_HOST_FFI_ABI_VERSION, + actual: actualAbi + ) + } + + let config = VoxitHostConfig(platform: VOXIT_PLATFORM_MACOS) + guard let handle = voxit_host_session_create(config) else { + throw HostBridgeError.sessionCreationFailed + } + + self.handle = handle + } + + deinit { + voxit_host_session_destroy(handle) + } + + public func currentSnapshot() throws -> HostSnapshot { + var outSnapshot = VoxitHostSnapshot() + try requireOk( + voxit_host_session_copy_snapshot(handle, &outSnapshot), + context: "copying host snapshot" + ) + + return try decode(snapshot: outSnapshot) + } + + private func requireOk(_ status: VoxitStatus, context: String) throws { + let code = voxit_status_code(status) + if code != 0 { + throw HostBridgeError.ffiStatus(context: context, code: code) + } + } + + private func decode(snapshot: VoxitHostSnapshot) throws -> HostSnapshot { + HostSnapshot( + platform: try decode(platform: snapshot.platform), + authMethod: try decode(authMethod: snapshot.auth_method), + authState: try decode(authState: snapshot.auth_state), + dictationState: try decode(dictationState: snapshot.dictation_state), + hotkeyMode: try decode(hotkeyMode: snapshot.hotkey_mode), + panelWidth: Int(snapshot.panel_width_px), + panelHeight: Int(snapshot.panel_height_px), + rewriteEnabled: snapshot.rewrite_enabled != 0 + ) + } + + private func decode(platform: VoxitPlatformTag) throws -> HostPlatform { + switch platform.rawValue { + case VOXIT_PLATFORM_MACOS.rawValue: + return .macOS + case VOXIT_PLATFORM_UNSUPPORTED.rawValue: + return .unsupported + default: + throw HostBridgeError.invalidPlatform(platform.rawValue) + } + } + + private func decode(authMethod: VoxitAuthMethod) throws -> AuthMethod { + switch authMethod.rawValue { + case VOXIT_AUTH_METHOD_CHATGPT_DEVICE_CODE.rawValue: + return .chatGPTDeviceCode + default: + throw HostBridgeError.invalidAuthMethod(authMethod.rawValue) + } + } + + private func decode(authState: VoxitAuthState) throws -> AuthState { + switch authState.rawValue { + case VOXIT_AUTH_STATE_CHECKING.rawValue: + return .checking + case VOXIT_AUTH_STATE_SIGNED_OUT.rawValue: + return .signedOut + case VOXIT_AUTH_STATE_SIGNED_IN.rawValue: + return .signedIn + case VOXIT_AUTH_STATE_BUSY.rawValue: + return .busy + default: + throw HostBridgeError.invalidAuthState(authState.rawValue) + } + } + + private func decode(dictationState: VoxitDictationState) throws -> DictationState { + switch dictationState.rawValue { + case VOXIT_DICTATION_STATE_IDLE.rawValue: + return .idle + case VOXIT_DICTATION_STATE_LISTENING.rawValue: + return .listening + case VOXIT_DICTATION_STATE_FINALIZING.rawValue: + return .finalizing + case VOXIT_DICTATION_STATE_REWRITING.rawValue: + return .rewriting + case VOXIT_DICTATION_STATE_DONE.rawValue: + return .done + default: + throw HostBridgeError.invalidDictationState(dictationState.rawValue) + } + } + + private func decode(hotkeyMode: VoxitHotkeyMode) throws -> HotkeyMode { + switch hotkeyMode.rawValue { + case VOXIT_HOTKEY_MODE_TOGGLE.rawValue: + return .toggle + case VOXIT_HOTKEY_MODE_HOLD.rawValue: + return .hold + default: + throw HostBridgeError.invalidHotkeyMode(hotkeyMode.rawValue) + } + } +} diff --git a/native/macos-host/Sources/VoxitHostBridgeProbe/main.swift b/native/macos-host/Sources/VoxitHostBridgeProbe/main.swift new file mode 100644 index 0000000..f527bcb --- /dev/null +++ b/native/macos-host/Sources/VoxitHostBridgeProbe/main.swift @@ -0,0 +1,23 @@ +import Foundation +import VoxitHostBridge + +@main +enum VoxitHostBridgeProbe { + static func main() throws { + let session = try VoxitHostSession() + let snapshot = try session.currentSnapshot() + + guard snapshot.platform == .macOS else { + fatalError("unexpected platform: \(snapshot)") + } + guard snapshot.authMethod == .chatGPTDeviceCode else { + fatalError("unexpected auth method: \(snapshot)") + } + guard snapshot.dictationState == .idle else { + fatalError("unexpected dictation state: \(snapshot)") + } + guard snapshot.panelWidth > 0, snapshot.panelHeight > 0 else { + fatalError("unexpected panel dimensions: \(snapshot)") + } + } +} diff --git a/native/macos-host/Sources/VoxitNativeHost/VoxitNativeHostMain.swift b/native/macos-host/Sources/VoxitNativeHost/VoxitNativeHostMain.swift new file mode 100644 index 0000000..76e5167 --- /dev/null +++ b/native/macos-host/Sources/VoxitNativeHost/VoxitNativeHostMain.swift @@ -0,0 +1,3 @@ +import VoxitNativeHostKit + +VoxitNativeHostApp.main() diff --git a/native/macos-host/Sources/VoxitNativeHostKit/App/VoxitNativeHostApp.swift b/native/macos-host/Sources/VoxitNativeHostKit/App/VoxitNativeHostApp.swift new file mode 100644 index 0000000..4d87356 --- /dev/null +++ b/native/macos-host/Sources/VoxitNativeHostKit/App/VoxitNativeHostApp.swift @@ -0,0 +1,99 @@ +import AppKit +import SwiftUI + +public struct VoxitNativeHostApp: App { + @Environment(\.openWindow) private var openWindow + @StateObject private var store = HostStore() + @StateObject private var settingsStore = VoxitSettingsStore() + @State private var settingsWindowController: VoxitSettingsWindowController? + + public init() {} + + public var body: some Scene { + Window("Voxit", id: "main") { + ContentView(store: store) + .frame(minWidth: 720, minHeight: 460) + .task { + VoxitArtwork.applyApplicationIcon() + await store.reload() + } + } + .commands { + CommandGroup(replacing: .appSettings) { + Button("Settings...") { + presentSettings() + } + .keyboardShortcut(",", modifiers: [.command]) + } + + CommandGroup(after: .appInfo) { + Button("Start Dictation") {} + .keyboardShortcut( + settingsStore.settings.dictationHotkeyPresentation.swiftUIKeyEquivalent, + modifiers: settingsStore.settings.dictationHotkeyPresentation.swiftUIModifiers + ) + .disabled(true) + + Divider() + + Button("Refresh Status") { + Task { + await store.reload() + } + } + .keyboardShortcut("r", modifiers: [.command]) + } + } + + MenuBarExtra { + Button("Open Voxit") { + openWindow(id: "main") + NSApp.activate(ignoringOtherApps: true) + } + .keyboardShortcut("o", modifiers: [.command]) + + Button("Start Dictation") {} + .keyboardShortcut( + settingsStore.settings.dictationHotkeyPresentation.swiftUIKeyEquivalent, + modifiers: settingsStore.settings.dictationHotkeyPresentation.swiftUIModifiers + ) + .disabled(true) + + Divider() + + Button("Settings...") { + presentSettings() + } + .keyboardShortcut(",", modifiers: [.command]) + + Button("Refresh Status") { + Task { + await store.reload() + } + } + .keyboardShortcut("r", modifiers: [.command]) + + Divider() + + Button("Quit Voxit") { + NSApp.terminate(nil) + } + .keyboardShortcut("q", modifiers: [.command]) + } label: { + Image(nsImage: VoxitArtwork.statusBarImage()) + .renderingMode(.template) + .foregroundStyle(.primary) + } + } + + @MainActor + private func presentSettings() { + if settingsWindowController == nil { + settingsWindowController = VoxitSettingsWindowController(settingsStore: settingsStore) { + NSApp.setActivationPolicy(.accessory) + } + } + + settingsWindowController?.present() + } +} diff --git a/native/macos-host/Sources/VoxitNativeHostKit/Models/NavigationItem.swift b/native/macos-host/Sources/VoxitNativeHostKit/Models/NavigationItem.swift new file mode 100644 index 0000000..92e1f20 --- /dev/null +++ b/native/macos-host/Sources/VoxitNativeHostKit/Models/NavigationItem.swift @@ -0,0 +1,33 @@ +import Foundation + +public enum NavigationItem: String, CaseIterable, Identifiable, Sendable { + case dictation + case auth + case audio + + public var id: String { + rawValue + } + + var title: String { + switch self { + case .dictation: + return "Dictation" + case .auth: + return "ChatGPT" + case .audio: + return "Audio" + } + } + + var systemImage: String { + switch self { + case .dictation: + return "waveform" + case .auth: + return "person.crop.circle.badge.checkmark" + case .audio: + return "mic" + } + } +} diff --git a/native/macos-host/Sources/VoxitNativeHostKit/Stores/HostStore.swift b/native/macos-host/Sources/VoxitNativeHostKit/Stores/HostStore.swift new file mode 100644 index 0000000..e457201 --- /dev/null +++ b/native/macos-host/Sources/VoxitNativeHostKit/Stores/HostStore.swift @@ -0,0 +1,33 @@ +import Foundation +import VoxitHostBridge + +@MainActor +public final class HostStore: ObservableObject { + @Published public private(set) var snapshot: HostSnapshot? + @Published public private(set) var errorMessage: String? + + private var session: VoxitHostSession? + + public init() {} + + public func reload() async { + do { + let session = try currentSession() + snapshot = try session.currentSnapshot() + errorMessage = nil + } catch { + errorMessage = String(describing: error) + } + } + + private func currentSession() throws -> VoxitHostSession { + if let session { + return session + } + + let session = try VoxitHostSession() + self.session = session + + return session + } +} diff --git a/native/macos-host/Sources/VoxitNativeHostKit/Stores/VoxitSettingsStore.swift b/native/macos-host/Sources/VoxitNativeHostKit/Stores/VoxitSettingsStore.swift new file mode 100644 index 0000000..cefd978 --- /dev/null +++ b/native/macos-host/Sources/VoxitNativeHostKit/Stores/VoxitSettingsStore.swift @@ -0,0 +1,236 @@ +import AppKit +import Foundation + +@MainActor +final class VoxitSettingsStore: ObservableObject { + static let didChangeNotification = Notification.Name("VoxitSettingsDidChange") + + @Published private(set) var settings: VoxitSettings + + private enum DefaultsKey { + static let dictationHotkey = "dictationHotkey" + static let hotkeyMode = "hotkeyMode" + static let startHidden = "startHidden" + static let pasteAfterTranscription = "pasteAfterTranscription" + static let rewriteAfterTranscription = "rewriteAfterTranscription" + static let authRoute = "authRoute" + static let audioInput = "audioInput" + } + + private let defaults: UserDefaults + + init(defaults: UserDefaults = .standard) { + self.defaults = defaults + let baseSettings = VoxitSettings.defaults + let settings = VoxitSettings( + dictationHotkey: defaults.string(forKey: DefaultsKey.dictationHotkey) + ?? baseSettings.dictationHotkey, + hotkeyMode: VoxitHotkeyModePreference( + rawValue: defaults.string(forKey: DefaultsKey.hotkeyMode) ?? "") + ?? baseSettings.hotkeyMode, + startHidden: defaults.object(forKey: DefaultsKey.startHidden) as? Bool + ?? baseSettings.startHidden, + pasteAfterTranscription: defaults.object(forKey: DefaultsKey.pasteAfterTranscription) + as? Bool + ?? baseSettings.pasteAfterTranscription, + rewriteAfterTranscription: defaults.object(forKey: DefaultsKey.rewriteAfterTranscription) + as? Bool + ?? baseSettings.rewriteAfterTranscription, + authRoute: VoxitAuthRoutePreference( + rawValue: defaults.string(forKey: DefaultsKey.authRoute) ?? "") + ?? baseSettings.authRoute, + audioInput: VoxitAudioInputPreference( + rawValue: defaults.string(forKey: DefaultsKey.audioInput) ?? "") + ?? baseSettings.audioInput + ) + self.settings = settings.sanitized() + Self.persist(self.settings, into: defaults) + } + + func update(_ mutate: (inout VoxitSettings) -> Void) { + var next = settings + mutate(&next) + let sanitized = next.sanitized() + settings = sanitized + Self.persist(sanitized, into: defaults) + NotificationCenter.default.post(name: Self.didChangeNotification, object: self) + } + + private static func persist(_ settings: VoxitSettings, into defaults: UserDefaults) { + defaults.set(settings.dictationHotkey, forKey: DefaultsKey.dictationHotkey) + defaults.set(settings.hotkeyMode.rawValue, forKey: DefaultsKey.hotkeyMode) + defaults.set(settings.startHidden, forKey: DefaultsKey.startHidden) + defaults.set(settings.pasteAfterTranscription, forKey: DefaultsKey.pasteAfterTranscription) + defaults.set(settings.rewriteAfterTranscription, forKey: DefaultsKey.rewriteAfterTranscription) + defaults.set(settings.authRoute.rawValue, forKey: DefaultsKey.authRoute) + defaults.set(settings.audioInput.rawValue, forKey: DefaultsKey.audioInput) + } +} + +struct VoxitSettings: Equatable { + var dictationHotkey: String + var hotkeyMode: VoxitHotkeyModePreference + var startHidden: Bool + var pasteAfterTranscription: Bool + var rewriteAfterTranscription: Bool + var authRoute: VoxitAuthRoutePreference + var audioInput: VoxitAudioInputPreference + + static var defaults: Self { + Self( + dictationHotkey: "Control-Shift-Space", + hotkeyMode: .toggle, + startHidden: true, + pasteAfterTranscription: true, + rewriteAfterTranscription: true, + authRoute: .chatGPTDeviceCode, + audioInput: .systemDefault + ) + } + + var dictationHotkeyPresentation: VoxitHotkeyPresentation { + Self.dictationHotkeyPresentation(for: dictationHotkey) + } + + func sanitized() -> Self { + var copy = self + copy.dictationHotkey = + Self.dictationHotkeyPresentation(for: copy.dictationHotkey) + .displayTitle + return copy + } + + static func dictationHotkeyPresentation(for raw: String) -> VoxitHotkeyPresentation { + parseHotkeyPresentation(raw) + ?? parseHotkeyPresentation(defaults.dictationHotkey) + ?? VoxitHotkeyPresentation( + displayTitle: "Control-Shift-Space", + keyEquivalent: " ", + modifierMask: [.control, .shift] + ) + } + + private static func parseHotkeyPresentation(_ raw: String) -> VoxitHotkeyPresentation? { + let tokens = hotkeyTokens(from: raw) + guard !tokens.isEmpty else { + return nil + } + + var modifiers = NSEvent.ModifierFlags() + var keyEquivalent: String? + for token in tokens { + switch token.lowercased() { + case "alt", "option": + modifiers.insert(.option) + case "ctrl", "control": + modifiers.insert(.control) + case "shift": + modifiers.insert(.shift) + case "cmd", "command", "super", "meta", "win": + modifiers.insert(.command) + default: + keyEquivalent = normalizedMenuKeyEquivalent(for: token) + } + } + + guard let keyEquivalent else { + return nil + } + + var titleParts: [String] = [] + if modifiers.contains(.control) { + titleParts.append("Control") + } + if modifiers.contains(.option) { + titleParts.append("Option") + } + if modifiers.contains(.shift) { + titleParts.append("Shift") + } + if modifiers.contains(.command) { + titleParts.append("Command") + } + titleParts.append(displayTitle(for: keyEquivalent)) + + return VoxitHotkeyPresentation( + displayTitle: titleParts.joined(separator: "-"), + keyEquivalent: keyEquivalent, + modifierMask: modifiers + ) + } + + private static func normalizedMenuKeyEquivalent(for token: String) -> String? { + let normalized = token.lowercased() + let key = normalized.hasPrefix("key") ? String(normalized.dropFirst(3)) : normalized + switch key { + case "space": + return " " + default: + guard key.count == 1, key.unicodeScalars.allSatisfy(\.isASCII) else { + return nil + } + return key + } + } + + private static func displayTitle(for keyEquivalent: String) -> String { + keyEquivalent == " " ? "Space" : keyEquivalent.uppercased() + } + + private static func hotkeyTokens(from raw: String) -> [String] { + raw + .split { character in + character == "+" || character == "-" + } + .map { $0.trimmingCharacters(in: .whitespacesAndNewlines) } + .filter { !$0.isEmpty } + } +} + +struct VoxitHotkeyPresentation: Equatable { + let displayTitle: String + let keyEquivalent: String + let modifierMask: NSEvent.ModifierFlags +} + +enum VoxitHotkeyModePreference: String, CaseIterable, Identifiable { + case toggle + case hold + + var id: Self { self } + + var title: String { + switch self { + case .toggle: + return "Toggle" + case .hold: + return "Hold" + } + } +} + +enum VoxitAuthRoutePreference: String, CaseIterable, Identifiable { + case chatGPTDeviceCode + + var id: Self { self } + + var title: String { + switch self { + case .chatGPTDeviceCode: + return "ChatGPT Device Code" + } + } +} + +enum VoxitAudioInputPreference: String, CaseIterable, Identifiable { + case systemDefault + + var id: Self { self } + + var title: String { + switch self { + case .systemDefault: + return "System Default" + } + } +} diff --git a/native/macos-host/Sources/VoxitNativeHostKit/Support/Labels.swift b/native/macos-host/Sources/VoxitNativeHostKit/Support/Labels.swift new file mode 100644 index 0000000..c9fcdf1 --- /dev/null +++ b/native/macos-host/Sources/VoxitNativeHostKit/Support/Labels.swift @@ -0,0 +1,53 @@ +import VoxitHostBridge + +extension AuthMethod { + var label: String { + switch self { + case .chatGPTDeviceCode: + return "Device Code" + } + } +} + +extension AuthState { + var label: String { + switch self { + case .checking: + return "Checking" + case .signedOut: + return "Signed Out" + case .signedIn: + return "Signed In" + case .busy: + return "Busy" + } + } +} + +extension DictationState { + var label: String { + switch self { + case .idle: + return "Idle" + case .listening: + return "Listening" + case .finalizing: + return "Finalizing" + case .rewriting: + return "Rewriting" + case .done: + return "Done" + } + } +} + +extension HotkeyMode { + var label: String { + switch self { + case .toggle: + return "Toggle" + case .hold: + return "Hold" + } + } +} diff --git a/native/macos-host/Sources/VoxitNativeHostKit/Support/MenuShortcuts.swift b/native/macos-host/Sources/VoxitNativeHostKit/Support/MenuShortcuts.swift new file mode 100644 index 0000000..9fae879 --- /dev/null +++ b/native/macos-host/Sources/VoxitNativeHostKit/Support/MenuShortcuts.swift @@ -0,0 +1,32 @@ +import AppKit +import SwiftUI + +extension VoxitHotkeyPresentation { + var swiftUIKeyEquivalent: KeyEquivalent { + if keyEquivalent == " " { + return .space + } + + guard let character = keyEquivalent.first else { + return .space + } + return KeyEquivalent(character) + } + + var swiftUIModifiers: EventModifiers { + var modifiers = EventModifiers() + if modifierMask.contains(.control) { + modifiers.insert(.control) + } + if modifierMask.contains(.option) { + modifiers.insert(.option) + } + if modifierMask.contains(.shift) { + modifiers.insert(.shift) + } + if modifierMask.contains(.command) { + modifiers.insert(.command) + } + return modifiers + } +} diff --git a/native/macos-host/Sources/VoxitNativeHostKit/Support/VoxitArtwork.swift b/native/macos-host/Sources/VoxitNativeHostKit/Support/VoxitArtwork.swift new file mode 100644 index 0000000..6c20e82 --- /dev/null +++ b/native/macos-host/Sources/VoxitNativeHostKit/Support/VoxitArtwork.swift @@ -0,0 +1,50 @@ +import AppKit +import Foundation + +enum VoxitArtwork { + static func statusBarImage() -> NSImage { + let image = + image( + named: "StatusBarIcon.png", + fallbackPath: "assets/tray-icon/generated/tray-icon-template.png") + ?? NSImage(systemSymbolName: "waveform", accessibilityDescription: "Voxit") + ?? NSImage(size: NSSize(width: 18, height: 18)) + image.isTemplate = true + image.size = NSSize(width: 18, height: 18) + return image + } + + @MainActor + static func applyApplicationIcon() { + guard + let image = image( + named: "AppIcon.icns", + fallbackPath: "assets/app-icon/generated/app-icon.icns" + ) + else { + return + } + NSApp.applicationIconImage = image + } + + private static func image(named resourceName: String, fallbackPath: String) -> NSImage? { + let directResourceURL = Bundle.main.resourceURL?.appendingPathComponent(resourceName) + let fallbackResourceURL = repositoryRootURL()?.appendingPathComponent(fallbackPath) + + return [directResourceURL, fallbackResourceURL] + .compactMap { $0 } + .first(where: { FileManager.default.fileExists(atPath: $0.path) }) + .flatMap { NSImage(contentsOf: $0) } + } + + private static func repositoryRootURL() -> URL? { + var url = URL(fileURLWithPath: #filePath) + for _ in 0..<8 { + url.deleteLastPathComponent() + if FileManager.default.fileExists(atPath: url.appendingPathComponent("Cargo.toml").path) { + return url + } + } + return nil + } +} diff --git a/native/macos-host/Sources/VoxitNativeHostKit/Support/VoxitSettingsWindowController.swift b/native/macos-host/Sources/VoxitNativeHostKit/Support/VoxitSettingsWindowController.swift new file mode 100644 index 0000000..6f28610 --- /dev/null +++ b/native/macos-host/Sources/VoxitNativeHostKit/Support/VoxitSettingsWindowController.swift @@ -0,0 +1,115 @@ +import AppKit +import SwiftUI + +private final class VoxitSettingsWindow: NSWindow { + override var canBecomeKey: Bool { + true + } + + override var canBecomeMain: Bool { + true + } + + override func performKeyEquivalent(with event: NSEvent) -> Bool { + if handleCommandShortcut(event) { + return true + } + return super.performKeyEquivalent(with: event) + } + + override func keyDown(with event: NSEvent) { + if handleCommandShortcut(event) { + return + } + super.keyDown(with: event) + } + + private func handleCommandShortcut(_ event: NSEvent) -> Bool { + let commandModifiers = event.modifierFlags.intersection([ + .command, .option, .control, .shift, + ]) + guard commandModifiers == .command, + let character = event.charactersIgnoringModifiers?.lowercased() + else { + return false + } + + switch character { + case "w": + performClose(nil) + return true + case "q": + NSApp.terminate(nil) + return true + default: + return false + } + } +} + +@MainActor +final class VoxitSettingsWindowController: NSWindowController, NSWindowDelegate { + private let viewModel: VoxitSettingsViewModel + private let onClose: () -> Void + + init(settingsStore: VoxitSettingsStore, onClose: @escaping () -> Void = {}) { + self.viewModel = VoxitSettingsViewModel(settingsStore: settingsStore) + self.onClose = onClose + + let contentRect = NSRect( + x: 0, + y: 0, + width: VoxitSettingsWindowMetrics.width, + height: VoxitSettingsWindowMetrics.idealHeight + ) + let window = VoxitSettingsWindow( + contentRect: contentRect, + styleMask: [.titled, .closable, .miniaturizable, .fullSizeContentView], + backing: .buffered, + defer: false + ) + window.title = "Settings" + window.titleVisibility = .hidden + window.titlebarAppearsTransparent = true + window.backgroundColor = .clear + window.isOpaque = false + if #available(macOS 11.0, *) { + window.titlebarSeparatorStyle = .none + } + window.isReleasedWhenClosed = false + window.contentMinSize = NSSize( + width: VoxitSettingsWindowMetrics.width, + height: VoxitSettingsWindowMetrics.minHeight + ) + window.collectionBehavior.insert(.moveToActiveSpace) + + super.init(window: window) + + window.delegate = self + let hostingController = NSHostingController(rootView: VoxitSettingsView(model: viewModel)) + hostingController.view.wantsLayer = true + hostingController.view.layer?.backgroundColor = NSColor.clear.cgColor + window.contentViewController = hostingController + window.center() + viewModel.refresh() + } + + @available(*, unavailable) + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + func present() { + NSApp.setActivationPolicy(.regular) + viewModel.refresh() + showWindow(nil) + NSRunningApplication.current.activate(options: [.activateAllWindows]) + window?.makeKeyAndOrderFront(nil) + window?.invalidateShadow() + NSApp.activate(ignoringOtherApps: true) + } + + func windowWillClose(_: Notification) { + onClose() + } +} diff --git a/native/macos-host/Sources/VoxitNativeHostKit/Views/ContentView.swift b/native/macos-host/Sources/VoxitNativeHostKit/Views/ContentView.swift new file mode 100644 index 0000000..9ebcc71 --- /dev/null +++ b/native/macos-host/Sources/VoxitNativeHostKit/Views/ContentView.swift @@ -0,0 +1,19 @@ +import SwiftUI +import VoxitHostBridge + +public struct ContentView: View { + @ObservedObject var store: HostStore + @SceneStorage("selection") private var selection: NavigationItem = .dictation + + public init(store: HostStore) { + self.store = store + } + + public var body: some View { + NavigationSplitView { + SidebarView(selection: $selection, snapshot: store.snapshot) + } detail: { + DetailView(selection: selection, snapshot: store.snapshot, errorMessage: store.errorMessage) + } + } +} diff --git a/native/macos-host/Sources/VoxitNativeHostKit/Views/DetailView.swift b/native/macos-host/Sources/VoxitNativeHostKit/Views/DetailView.swift new file mode 100644 index 0000000..201f65d --- /dev/null +++ b/native/macos-host/Sources/VoxitNativeHostKit/Views/DetailView.swift @@ -0,0 +1,146 @@ +import SwiftUI +import VoxitHostBridge + +struct DetailView: View { + var selection: NavigationItem + var snapshot: HostSnapshot? + var errorMessage: String? + + var body: some View { + ScrollView { + VStack(alignment: .leading, spacing: 18) { + header + + if let errorMessage { + StatusCard( + title: "Host Bridge", value: errorMessage, systemImage: "exclamationmark.triangle") + } + + switch selection { + case .dictation: + DictationDetail(snapshot: snapshot) + case .auth: + AuthDetail(snapshot: snapshot) + case .audio: + AudioDetail() + } + } + .padding(24) + .frame(maxWidth: 760, alignment: .leading) + } + .navigationTitle(selection.title) + } + + private var header: some View { + VStack(alignment: .leading, spacing: 5) { + Text(selection.title) + .font(.largeTitle.weight(.semibold)) + Text(subtitle) + .foregroundStyle(.secondary) + } + } + + private var subtitle: String { + switch selection { + case .dictation: + return "Record, finalize, rewrite, and paste." + case .auth: + return "ChatGPT device-code authorization." + case .audio: + return "Microphone input and permission state." + } + } +} + +private struct DictationDetail: View { + var snapshot: HostSnapshot? + + var body: some View { + LabeledContentGrid { + StatusCard( + title: "State", + value: snapshot?.dictationState.label ?? "Loading", + systemImage: "waveform" + ) + StatusCard( + title: "Rewrite", + value: snapshot?.rewriteEnabled == true ? "Enabled" : "Disabled", + systemImage: "wand.and.stars" + ) + } + + HStack(spacing: 10) { + Button("Start Recording", systemImage: "record.circle") {} + .buttonStyle(.borderedProminent) + .disabled(true) + Button("Paste Raw", systemImage: "text.badge.checkmark") {} + .disabled(true) + } + } +} + +private struct AuthDetail: View { + var snapshot: HostSnapshot? + + var body: some View { + LabeledContentGrid { + StatusCard( + title: "Status", + value: snapshot?.authState.label ?? "Loading", + systemImage: "person.crop.circle.badge.checkmark" + ) + StatusCard( + title: "Method", + value: snapshot?.authMethod.label ?? "Device Code", + systemImage: "rectangle.and.pencil.and.ellipsis" + ) + } + + Button("Sign In", systemImage: "arrow.right.circle") {} + .buttonStyle(.borderedProminent) + .disabled(true) + } +} + +private struct AudioDetail: View { + var body: some View { + LabeledContentGrid { + StatusCard(title: "Input", value: "System Default", systemImage: "mic") + StatusCard(title: "Permission", value: "Unknown", systemImage: "checkmark.shield") + } + } +} + +private struct LabeledContentGrid: View { + @ViewBuilder var content: Content + + var body: some View { + LazyVGrid(columns: [GridItem(.adaptive(minimum: 220), spacing: 12)], spacing: 12) { + content + } + } +} + +private struct StatusCard: View { + var title: String + var value: String + var systemImage: String + + var body: some View { + VStack(alignment: .leading, spacing: 10) { + Image(systemName: systemImage) + .foregroundStyle(.secondary) + VStack(alignment: .leading, spacing: 2) { + Text(title) + .font(.caption) + .foregroundStyle(.secondary) + Text(value) + .font(.title3.weight(.medium)) + .lineLimit(2) + } + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(14) + .background(.regularMaterial, in: RoundedRectangle(cornerRadius: 8)) + } +} diff --git a/native/macos-host/Sources/VoxitNativeHostKit/Views/SidebarView.swift b/native/macos-host/Sources/VoxitNativeHostKit/Views/SidebarView.swift new file mode 100644 index 0000000..d6467f8 --- /dev/null +++ b/native/macos-host/Sources/VoxitNativeHostKit/Views/SidebarView.swift @@ -0,0 +1,41 @@ +import SwiftUI +import VoxitHostBridge + +struct SidebarView: View { + @Binding var selection: NavigationItem + var snapshot: HostSnapshot? + + var body: some View { + List(selection: $selection) { + ForEach(NavigationItem.allCases) { item in + Label(item.title, systemImage: item.systemImage) + .tag(item) + } + } + .listStyle(.sidebar) + .navigationTitle("Voxit") + .safeAreaInset(edge: .bottom) { + if let snapshot { + SidebarStatus(snapshot: snapshot) + } + } + } +} + +private struct SidebarStatus: View { + var snapshot: HostSnapshot + + var body: some View { + VStack(alignment: .leading, spacing: 4) { + Text(snapshot.dictationState.label) + .font(.caption) + .foregroundStyle(.primary) + Text(snapshot.authState.label) + .font(.caption2) + .foregroundStyle(.secondary) + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(.horizontal, 12) + .padding(.vertical, 10) + } +} diff --git a/native/macos-host/Sources/VoxitNativeHostKit/Views/VoxitSettingsView.swift b/native/macos-host/Sources/VoxitNativeHostKit/Views/VoxitSettingsView.swift new file mode 100644 index 0000000..11b8bed --- /dev/null +++ b/native/macos-host/Sources/VoxitNativeHostKit/Views/VoxitSettingsView.swift @@ -0,0 +1,433 @@ +import AppKit +import SwiftUI + +enum VoxitSettingsWindowMetrics { + static let width: CGFloat = 620 + static let minHeight: CGFloat = 336 + static let idealHeight: CGFloat = 396 + static let cornerRadius: CGFloat = 18 +} + +@MainActor +final class VoxitSettingsViewModel: ObservableObject { + @Published private(set) var settings: VoxitSettings + + private let settingsStore: VoxitSettingsStore + + init(settingsStore: VoxitSettingsStore) { + self.settingsStore = settingsStore + self.settings = settingsStore.settings + } + + func refresh() { + settings = settingsStore.settings + } + + func update(_ mutate: (inout VoxitSettings) -> Void) { + settingsStore.update(mutate) + settings = settingsStore.settings + } + + func restoreDefaults() { + update { $0 = VoxitSettings.defaults } + } + + func openMicrophoneSettings() { + openPrivacySettings(query: "Privacy_Microphone") + } + + func openAccessibilitySettings() { + openPrivacySettings(query: "Privacy_Accessibility") + } + + private func openPrivacySettings(query: String) { + let modernURLString = + "x-apple.systempreferences:com.apple.settings.PrivacySecurity.extension?\(query)" + if let modernURL = URL(string: modernURLString), NSWorkspace.shared.open(modernURL) { + return + } + + let fallbackURLString = "x-apple.systempreferences:com.apple.preference.security?\(query)" + if let fallbackURL = URL(string: fallbackURLString) { + NSWorkspace.shared.open(fallbackURL) + } + } +} + +struct VoxitSettingsView: View { + @ObservedObject var model: VoxitSettingsViewModel + @State private var selectedSection: VoxitSettingsSection = .general + + var body: some View { + HStack(alignment: .top, spacing: 12) { + SettingsRail(selectedSection: $selectedSection) + .frame(width: 150) + .padding(.top, 24) + + SettingsDashboard( + model: model, + section: selectedSection, + restoreDefaults: model.restoreDefaults + ) + .frame(maxWidth: .infinity, alignment: .topLeading) + } + .padding(.top, 12) + .padding(.horizontal, 14) + .padding(.bottom, 12) + .controlSize(.small) + .frame( + minWidth: VoxitSettingsWindowMetrics.width, + idealWidth: VoxitSettingsWindowMetrics.width, + minHeight: VoxitSettingsWindowMetrics.minHeight, + idealHeight: VoxitSettingsWindowMetrics.idealHeight + ) + .background(.regularMaterial) + } +} + +private enum VoxitSettingsSection: String, CaseIterable, Identifiable { + case general + case dictation + case audio + case permissions + case about + + var id: Self { self } + + var title: String { + switch self { + case .general: + return "General" + case .dictation: + return "Dictation" + case .audio: + return "Audio" + case .permissions: + return "Permissions" + case .about: + return "About" + } + } + + var subtitle: String { + switch self { + case .general: + return "Startup" + case .dictation: + return "Shortcut" + case .audio: + return "Input" + case .permissions: + return "Access" + case .about: + return "Project" + } + } + + var symbolName: String { + switch self { + case .general: + return "switch.2" + case .dictation: + return "waveform" + case .audio: + return "mic" + case .permissions: + return "lock.shield" + case .about: + return "info.circle" + } + } + + var allowsRestoreDefaults: Bool { + switch self { + case .general, .dictation, .audio: + return true + case .permissions, .about: + return false + } + } +} + +private struct SettingsRail: View { + @Binding var selectedSection: VoxitSettingsSection + + var body: some View { + VStack(alignment: .leading, spacing: 14) { + HStack(spacing: 8) { + Image(nsImage: NSApp.applicationIconImage) + .resizable() + .scaledToFit() + .frame(width: 28, height: 28) + .clipShape(RoundedRectangle(cornerRadius: 7, style: .continuous)) + Text("Voxit") + .font(.system(size: 17, weight: .semibold, design: .rounded)) + } + .padding(.horizontal, 2) + + VStack(spacing: 5) { + ForEach(VoxitSettingsSection.allCases) { section in + SettingsRailButton( + section: section, + isSelected: selectedSection == section + ) { + selectedSection = section + } + } + } + } + } +} + +private struct SettingsRailButton: View { + let section: VoxitSettingsSection + let isSelected: Bool + let action: () -> Void + @State private var isHovered = false + + var body: some View { + Button(action: action) { + HStack(spacing: 8) { + Image(systemName: section.symbolName) + .symbolRenderingMode(.hierarchical) + .font(.system(size: 12.5, weight: .semibold)) + .foregroundStyle(isSelected ? Color.accentColor : Color.secondary) + .frame(width: 23, height: 23) + + VStack(alignment: .leading, spacing: 2) { + Text(section.title) + .font(.system(size: 12.5, weight: .semibold)) + .lineLimit(1) + Text(section.subtitle) + .font(.system(size: 10, weight: .medium)) + .foregroundStyle(.secondary) + .lineLimit(1) + } + Spacer(minLength: 0) + } + .padding(.horizontal, 8) + .padding(.vertical, 5) + .frame(maxWidth: .infinity) + .contentShape(RoundedRectangle(cornerRadius: 9, style: .continuous)) + .background { + if isSelected { + RoundedRectangle(cornerRadius: 9, style: .continuous) + .fill(.primary.opacity(0.06)) + HStack { + Capsule() + .fill(Color.accentColor) + .frame(width: 2, height: 16) + Spacer() + } + .padding(.leading, 1) + } else if isHovered { + RoundedRectangle(cornerRadius: 9, style: .continuous) + .fill(.primary.opacity(0.035)) + } + } + } + .buttonStyle(.plain) + .onHover { isHovered = $0 } + } +} + +private struct SettingsDashboard: View { + @ObservedObject var model: VoxitSettingsViewModel + let section: VoxitSettingsSection + let restoreDefaults: () -> Void + + var body: some View { + VStack(alignment: .leading, spacing: 12) { + HStack(alignment: .firstTextBaseline) { + VStack(alignment: .leading, spacing: 2) { + Text(section.title) + .font(.system(size: 20, weight: .semibold)) + Text(section.subtitle) + .font(.caption) + .foregroundStyle(.secondary) + } + Spacer() + if section.allowsRestoreDefaults { + Button("Defaults", action: restoreDefaults) + } + } + .padding(.top, 10) + + Group { + switch section { + case .general: + GeneralSettingsPane(model: model) + case .dictation: + DictationSettingsPane(model: model) + case .audio: + AudioSettingsPane(model: model) + case .permissions: + PermissionsSettingsPane(model: model) + case .about: + AboutSettingsPane() + } + } + .frame(maxWidth: .infinity, alignment: .topLeading) + } + .padding(.top, 10) + } +} + +private struct GeneralSettingsPane: View { + @ObservedObject var model: VoxitSettingsViewModel + + var body: some View { + SettingsPanel { + Toggle( + "Start as menu bar app", + isOn: settingBinding(\.startHidden) + ) + Toggle( + "Paste after transcription", + isOn: settingBinding(\.pasteAfterTranscription) + ) + Toggle( + "Rewrite after transcription", + isOn: settingBinding(\.rewriteAfterTranscription) + ) + } + } + + private func settingBinding(_ keyPath: WritableKeyPath) -> Binding { + Binding( + get: { model.settings[keyPath: keyPath] }, + set: { value in + model.update { $0[keyPath: keyPath] = value } + } + ) + } +} + +private struct DictationSettingsPane: View { + @ObservedObject var model: VoxitSettingsViewModel + + var body: some View { + SettingsPanel { + Picker("Shortcut", selection: hotkeyBinding) { + Text("Control-Shift-Space").tag("Control-Shift-Space") + Text("Option-Space").tag("Option-Space") + Text("Command-Shift-Space").tag("Command-Shift-Space") + Text("Control-Option-V").tag("Control-Option-V") + } + .pickerStyle(.menu) + + Picker("Mode", selection: hotkeyModeBinding) { + ForEach(VoxitHotkeyModePreference.allCases) { mode in + Text(mode.title).tag(mode) + } + } + .pickerStyle(.segmented) + + Picker("Auth", selection: authRouteBinding) { + ForEach(VoxitAuthRoutePreference.allCases) { route in + Text(route.title).tag(route) + } + } + .pickerStyle(.menu) + } + } + + private var hotkeyBinding: Binding { + Binding( + get: { model.settings.dictationHotkey }, + set: { value in + model.update { $0.dictationHotkey = value } + } + ) + } + + private var hotkeyModeBinding: Binding { + Binding( + get: { model.settings.hotkeyMode }, + set: { value in + model.update { $0.hotkeyMode = value } + } + ) + } + + private var authRouteBinding: Binding { + Binding( + get: { model.settings.authRoute }, + set: { value in + model.update { $0.authRoute = value } + } + ) + } +} + +private struct AudioSettingsPane: View { + @ObservedObject var model: VoxitSettingsViewModel + + var body: some View { + SettingsPanel { + Picker("Input", selection: audioInputBinding) { + ForEach(VoxitAudioInputPreference.allCases) { input in + Text(input.title).tag(input) + } + } + .pickerStyle(.menu) + + LabeledContent("Target rate", value: "24 kHz") + LabeledContent("Channels", value: "Mono") + } + } + + private var audioInputBinding: Binding { + Binding( + get: { model.settings.audioInput }, + set: { value in + model.update { $0.audioInput = value } + } + ) + } +} + +private struct PermissionsSettingsPane: View { + @ObservedObject var model: VoxitSettingsViewModel + + var body: some View { + SettingsPanel { + HStack { + LabeledContent("Microphone", value: "Required") + Button("Open") { + model.openMicrophoneSettings() + } + } + + HStack { + LabeledContent("Accessibility", value: "Optional") + Button("Open") { + model.openAccessibilitySettings() + } + } + } + } +} + +private struct AboutSettingsPane: View { + var body: some View { + SettingsPanel { + LabeledContent("App", value: "Voxit") + LabeledContent("Host", value: "Swift + Rust Core FFI") + LabeledContent("Auth", value: "ChatGPT Device Code") + } + } +} + +private struct SettingsPanel: View { + @ViewBuilder var content: Content + + var body: some View { + VStack(alignment: .leading, spacing: 12) { + content + } + .frame(maxWidth: .infinity, alignment: .leading) + .padding(14) + .background(.thinMaterial, in: RoundedRectangle(cornerRadius: 8, style: .continuous)) + } +} diff --git a/packages/voxit-core/src/lib.rs b/packages/voxit-core/src/lib.rs index 19cbfc7..0fe134a 100644 --- a/packages/voxit-core/src/lib.rs +++ b/packages/voxit-core/src/lib.rs @@ -6,6 +6,7 @@ pub mod inference; pub mod openai; pub mod realtime; pub mod transcript; +pub mod ui_model; mod audio_payload; mod providers; @@ -24,4 +25,8 @@ pub use self::{ REALTIME_ENDPOINT, RealtimeError, RealtimeEvent, RealtimeSession, RealtimeSessionConfig, }, transcript::{TranscriptAssembler, TranscriptEvent, TranscriptSnapshot}, + ui_model::{ + AuthMethod, AuthSurfaceState, DictationSurfaceState, HotkeySurfaceMode, NativeHostSnapshot, + PlatformHost, + }, }; diff --git a/packages/voxit-core/src/ui_model.rs b/packages/voxit-core/src/ui_model.rs new file mode 100644 index 0000000..8f79d45 --- /dev/null +++ b/packages/voxit-core/src/ui_model.rs @@ -0,0 +1,139 @@ +//! Platform-neutral UI model shared by native hosts. +//! +//! The first native-host slice keeps UI state deliberately small. Runtime services +//! still live in the existing Rust crates, while platform hosts render this model and +//! call back into focused core services as those APIs are promoted behind the host +//! boundary. + +use crate::config::Config; + +/// Platform family that owns the visible application shell. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum PlatformHost { + /// Native macOS host. + MacOS, + /// Unsupported or test-only host. + Unsupported, +} + +/// Authentication method exposed by the first native host. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum AuthMethod { + /// ChatGPT OAuth device-code authorization. + ChatGptDeviceCode, +} + +/// Authentication state shown by native UI. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum AuthSurfaceState { + /// The host has not completed an auth status read yet. + Checking, + /// No usable ChatGPT session is present. + SignedOut, + /// A usable ChatGPT session is present. + SignedIn, + /// An auth flow is currently in progress. + Busy, +} + +/// Dictation state shown by native UI. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum DictationSurfaceState { + /// No active recording is in progress. + Idle, + /// Microphone capture and pass-1 streaming are active. + Listening, + /// Pass-2 finalization is running. + Finalizing, + /// Pass-3 rewrite is running. + Rewriting, + /// The latest dictation cycle has completed. + Done, +} + +/// Native hotkey behavior shown by UI. +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum HotkeySurfaceMode { + /// Press once to start and press again to stop. + Toggle, + /// Hold to record and release to stop. + Hold, +} +impl HotkeySurfaceMode { + fn from_config_value(value: &str) -> Self { + match value { + "hold" => Self::Hold, + _ => Self::Toggle, + } + } +} + +/// Minimal platform-neutral snapshot rendered by native hosts. +#[derive(Clone, Debug, Eq, PartialEq)] +pub struct NativeHostSnapshot { + /// Visible platform shell. + pub platform: PlatformHost, + /// Auth method for the first native UI. + pub auth_method: AuthMethod, + /// Current auth status. + pub auth_state: AuthSurfaceState, + /// Current dictation status. + pub dictation_state: DictationSurfaceState, + /// Current hotkey mode. + pub hotkey_mode: HotkeySurfaceMode, + /// Suggested panel width. + pub panel_width_px: u32, + /// Suggested panel height. + pub panel_height_px: u32, + /// True when pass-3 rewrite is enabled by config. + pub rewrite_enabled: bool, +} +impl NativeHostSnapshot { + /// Builds the first native-host snapshot from persistent config defaults. + pub fn initial(platform: PlatformHost, config: &Config) -> Self { + Self { + platform, + auth_method: AuthMethod::ChatGptDeviceCode, + auth_state: AuthSurfaceState::Checking, + dictation_state: DictationSurfaceState::Idle, + hotkey_mode: HotkeySurfaceMode::from_config_value(&config.hotkey.mode), + panel_width_px: config.ui.panel_width_px, + panel_height_px: config.ui.panel_height_px, + rewrite_enabled: config.rewrite.enabled, + } + } +} + +#[cfg(test)] +mod tests { + use crate::{ + config::Config, + ui_model::{HotkeySurfaceMode, NativeHostSnapshot, PlatformHost}, + }; + + #[test] + fn initial_snapshot_uses_configured_dimensions_and_rewrite_flag() { + let mut config = Config::default(); + + config.ui.panel_width_px = 512; + config.ui.panel_height_px = 320; + config.rewrite.enabled = false; + + let snapshot = NativeHostSnapshot::initial(PlatformHost::MacOS, &config); + + assert_eq!(snapshot.panel_width_px, 512); + assert_eq!(snapshot.panel_height_px, 320); + assert!(!snapshot.rewrite_enabled); + } + + #[test] + fn initial_snapshot_maps_hotkey_mode() { + let mut config = Config::default(); + + config.hotkey.mode = "hold".to_string(); + + let snapshot = NativeHostSnapshot::initial(PlatformHost::MacOS, &config); + + assert_eq!(snapshot.hotkey_mode, HotkeySurfaceMode::Hold); + } +} diff --git a/packages/voxit-host-ffi/Cargo.toml b/packages/voxit-host-ffi/Cargo.toml new file mode 100644 index 0000000..3f2fdc5 --- /dev/null +++ b/packages/voxit-host-ffi/Cargo.toml @@ -0,0 +1,17 @@ +[package] +authors.workspace = true +description = "Thin C ABI bridge for the Voxit native host." +edition.workspace = true +homepage.workspace = true +license.workspace = true +name = "voxit-host-ffi" +readme.workspace = true +repository.workspace = true +version.workspace = true + +[lib] +crate-type = ["rlib", "staticlib"] +path = "src/lib.rs" + +[dependencies] +voxit-core = { path = "../voxit-core" } diff --git a/packages/voxit-host-ffi/include/voxit_host_ffi.h b/packages/voxit-host-ffi/include/voxit_host_ffi.h new file mode 100644 index 0000000..364b632 --- /dev/null +++ b/packages/voxit-host-ffi/include/voxit_host_ffi.h @@ -0,0 +1,77 @@ +#ifndef VOXIT_HOST_FFI_H +#define VOXIT_HOST_FFI_H + +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define VOXIT_HOST_FFI_ABI_VERSION 1u + +typedef struct VoxitHostSessionHandle VoxitHostSessionHandle; + +typedef enum VoxitStatus { + VOXIT_STATUS_OK = 0, + VOXIT_STATUS_NULL_HANDLE = 1, + VOXIT_STATUS_NULL_OUTPUT = 2, + VOXIT_STATUS_INVALID_INPUT = 3, +} VoxitStatus; + +typedef enum VoxitPlatformTag { + VOXIT_PLATFORM_MACOS = 0, + VOXIT_PLATFORM_UNSUPPORTED = 1, +} VoxitPlatformTag; + +typedef enum VoxitAuthMethod { + VOXIT_AUTH_METHOD_CHATGPT_DEVICE_CODE = 0, +} VoxitAuthMethod; + +typedef enum VoxitAuthState { + VOXIT_AUTH_STATE_CHECKING = 0, + VOXIT_AUTH_STATE_SIGNED_OUT = 1, + VOXIT_AUTH_STATE_SIGNED_IN = 2, + VOXIT_AUTH_STATE_BUSY = 3, +} VoxitAuthState; + +typedef enum VoxitDictationState { + VOXIT_DICTATION_STATE_IDLE = 0, + VOXIT_DICTATION_STATE_LISTENING = 1, + VOXIT_DICTATION_STATE_FINALIZING = 2, + VOXIT_DICTATION_STATE_REWRITING = 3, + VOXIT_DICTATION_STATE_DONE = 4, +} VoxitDictationState; + +typedef enum VoxitHotkeyMode { + VOXIT_HOTKEY_MODE_TOGGLE = 0, + VOXIT_HOTKEY_MODE_HOLD = 1, +} VoxitHotkeyMode; + +typedef struct VoxitHostConfig { + enum VoxitPlatformTag platform; +} VoxitHostConfig; + +typedef struct VoxitHostSnapshot { + enum VoxitPlatformTag platform; + enum VoxitAuthMethod auth_method; + enum VoxitAuthState auth_state; + enum VoxitDictationState dictation_state; + enum VoxitHotkeyMode hotkey_mode; + uint32_t panel_width_px; + uint32_t panel_height_px; + uint8_t rewrite_enabled; +} VoxitHostSnapshot; + +uint32_t voxit_host_ffi_abi_version(void); +VoxitHostSessionHandle *voxit_host_session_create(struct VoxitHostConfig config); +void voxit_host_session_destroy(VoxitHostSessionHandle *handle); +enum VoxitStatus voxit_host_session_copy_snapshot( + VoxitHostSessionHandle *handle, + struct VoxitHostSnapshot *out +); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/packages/voxit-host-ffi/src/lib.rs b/packages/voxit-host-ffi/src/lib.rs new file mode 100644 index 0000000..fb1f17a --- /dev/null +++ b/packages/voxit-host-ffi/src/lib.rs @@ -0,0 +1,269 @@ +//! Thin C ABI bridge for native platform hosts. +//! +//! The ABI intentionally starts with only a session handle and a copy-out UI snapshot. +//! This gives the Swift host a stable Rust-owned model without moving audio, auth, or +//! inference orchestration across FFI before those boundaries are ready. + +use std::ptr::NonNull; + +use voxit_core::{ + Config, NativeHostSnapshot, PlatformHost, + ui_model::{AuthMethod, AuthSurfaceState, DictationSurfaceState, HotkeySurfaceMode}, +}; + +/// ABI version exported by the thin C host bridge. +pub const VOXIT_HOST_FFI_ABI_VERSION: u32 = 1; + +/// Opaque session handle owned by the native host through the C ABI. +pub struct VoxitHostSessionHandle { + snapshot: NativeHostSnapshot, +} + +/// Result code returned by FFI entry points. +#[repr(C)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum VoxitStatus { + /// The operation succeeded. + Ok = 0, + /// The provided session handle was null. + NullHandle = 1, + /// The provided output pointer was null. + NullOutput = 2, + /// The provided input payload was invalid. + InvalidInput = 3, +} + +/// FFI-safe platform tag. +#[repr(C)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum VoxitPlatformTag { + /// Native macOS host. + MacOS = 0, + /// Unsupported or test-only host. + Unsupported = 1, +} + +/// FFI-safe auth method. +#[repr(C)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum VoxitAuthMethod { + /// ChatGPT OAuth device-code authorization. + ChatGptDeviceCode = 0, +} + +/// FFI-safe auth state. +#[repr(C)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum VoxitAuthState { + /// The host has not completed an auth status read yet. + Checking = 0, + /// No usable ChatGPT session is present. + SignedOut = 1, + /// A usable ChatGPT session is present. + SignedIn = 2, + /// An auth flow is currently in progress. + Busy = 3, +} + +/// FFI-safe dictation state. +#[repr(C)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum VoxitDictationState { + /// No active recording is in progress. + Idle = 0, + /// Microphone capture and pass-1 streaming are active. + Listening = 1, + /// Pass-2 finalization is running. + Finalizing = 2, + /// Pass-3 rewrite is running. + Rewriting = 3, + /// The latest dictation cycle has completed. + Done = 4, +} + +/// FFI-safe hotkey mode. +#[repr(C)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub enum VoxitHotkeyMode { + /// Press once to start and press again to stop. + Toggle = 0, + /// Hold to record and release to stop. + Hold = 1, +} + +/// FFI-safe session configuration. +#[repr(C)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct VoxitHostConfig { + /// Platform family that owns the host. + pub platform: VoxitPlatformTag, +} + +/// FFI-safe native-host snapshot. +#[repr(C)] +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct VoxitHostSnapshot { + /// Visible platform shell. + pub platform: VoxitPlatformTag, + /// Auth method for the first native UI. + pub auth_method: VoxitAuthMethod, + /// Current auth status. + pub auth_state: VoxitAuthState, + /// Current dictation status. + pub dictation_state: VoxitDictationState, + /// Current hotkey mode. + pub hotkey_mode: VoxitHotkeyMode, + /// Suggested panel width. + pub panel_width_px: u32, + /// Suggested panel height. + pub panel_height_px: u32, + /// Non-zero when pass-3 rewrite is enabled. + pub rewrite_enabled: u8, +} +impl Default for VoxitHostSnapshot { + fn default() -> Self { + Self { + platform: VoxitPlatformTag::Unsupported, + auth_method: VoxitAuthMethod::ChatGptDeviceCode, + auth_state: VoxitAuthState::Checking, + dictation_state: VoxitDictationState::Idle, + hotkey_mode: VoxitHotkeyMode::Toggle, + panel_width_px: 0, + panel_height_px: 0, + rewrite_enabled: 0, + } + } +} + +/// Returns the ABI version exported by this bridge. +#[unsafe(no_mangle)] +pub extern "C" fn voxit_host_ffi_abi_version() -> u32 { + VOXIT_HOST_FFI_ABI_VERSION +} + +/// Creates a Rust-owned native-host session. +#[unsafe(no_mangle)] +pub extern "C" fn voxit_host_session_create( + config: VoxitHostConfig, +) -> *mut VoxitHostSessionHandle { + let platform = match config.platform { + VoxitPlatformTag::MacOS => PlatformHost::MacOS, + VoxitPlatformTag::Unsupported => PlatformHost::Unsupported, + }; + let snapshot = NativeHostSnapshot::initial(platform, &Config::default()); + + Box::into_raw(Box::new(VoxitHostSessionHandle { snapshot })) +} + +/// Destroys a Rust-owned native-host session. +/// +/// # Safety +/// +/// `handle` must be either null or a pointer previously returned by +/// [`voxit_host_session_create`] that has not already been destroyed. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn voxit_host_session_destroy(handle: *mut VoxitHostSessionHandle) { + if let Some(handle) = NonNull::new(handle) { + unsafe { drop(Box::from_raw(handle.as_ptr())) }; + } +} + +/// Copies the current Rust-owned host snapshot into caller-owned memory. +/// +/// # Safety +/// +/// `handle` must be a valid pointer returned by [`voxit_host_session_create`]. `out` +/// must point to writable memory for one [`VoxitHostSnapshot`]. +#[unsafe(no_mangle)] +pub unsafe extern "C" fn voxit_host_session_copy_snapshot( + handle: *mut VoxitHostSessionHandle, + out: *mut VoxitHostSnapshot, +) -> VoxitStatus { + let Some(handle) = NonNull::new(handle) else { + return VoxitStatus::NullHandle; + }; + let Some(out) = NonNull::new(out) else { + return VoxitStatus::NullOutput; + }; + let snapshot = unsafe { &handle.as_ref().snapshot }; + + unsafe { out.as_ptr().write(encode_snapshot(snapshot)) }; + + VoxitStatus::Ok +} + +fn encode_snapshot(snapshot: &NativeHostSnapshot) -> VoxitHostSnapshot { + VoxitHostSnapshot { + platform: encode_platform(snapshot.platform), + auth_method: encode_auth_method(snapshot.auth_method), + auth_state: encode_auth_state(snapshot.auth_state), + dictation_state: encode_dictation_state(snapshot.dictation_state), + hotkey_mode: encode_hotkey_mode(snapshot.hotkey_mode), + panel_width_px: snapshot.panel_width_px, + panel_height_px: snapshot.panel_height_px, + rewrite_enabled: u8::from(snapshot.rewrite_enabled), + } +} + +fn encode_platform(platform: PlatformHost) -> VoxitPlatformTag { + match platform { + PlatformHost::MacOS => VoxitPlatformTag::MacOS, + PlatformHost::Unsupported => VoxitPlatformTag::Unsupported, + } +} + +fn encode_auth_method(method: AuthMethod) -> VoxitAuthMethod { + match method { + AuthMethod::ChatGptDeviceCode => VoxitAuthMethod::ChatGptDeviceCode, + } +} + +fn encode_auth_state(state: AuthSurfaceState) -> VoxitAuthState { + match state { + AuthSurfaceState::Checking => VoxitAuthState::Checking, + AuthSurfaceState::SignedOut => VoxitAuthState::SignedOut, + AuthSurfaceState::SignedIn => VoxitAuthState::SignedIn, + AuthSurfaceState::Busy => VoxitAuthState::Busy, + } +} + +fn encode_dictation_state(state: DictationSurfaceState) -> VoxitDictationState { + match state { + DictationSurfaceState::Idle => VoxitDictationState::Idle, + DictationSurfaceState::Listening => VoxitDictationState::Listening, + DictationSurfaceState::Finalizing => VoxitDictationState::Finalizing, + DictationSurfaceState::Rewriting => VoxitDictationState::Rewriting, + DictationSurfaceState::Done => VoxitDictationState::Done, + } +} + +fn encode_hotkey_mode(mode: HotkeySurfaceMode) -> VoxitHotkeyMode { + match mode { + HotkeySurfaceMode::Toggle => VoxitHotkeyMode::Toggle, + HotkeySurfaceMode::Hold => VoxitHotkeyMode::Hold, + } +} + +#[cfg(test)] +mod tests { + use crate::{ + VoxitAuthMethod, VoxitDictationState, VoxitHostConfig, VoxitHostSnapshot, VoxitPlatformTag, + VoxitStatus, + }; + + #[test] + fn session_snapshot_uses_device_code_auth_method() { + let handle = + crate::voxit_host_session_create(VoxitHostConfig { platform: VoxitPlatformTag::MacOS }); + let mut snapshot = VoxitHostSnapshot::default(); + let status = unsafe { crate::voxit_host_session_copy_snapshot(handle, &mut snapshot) }; + + assert_eq!(status, VoxitStatus::Ok); + assert_eq!(snapshot.platform, VoxitPlatformTag::MacOS); + assert_eq!(snapshot.auth_method, VoxitAuthMethod::ChatGptDeviceCode); + assert_eq!(snapshot.dictation_state, VoxitDictationState::Idle); + assert_eq!(snapshot.rewrite_enabled, 1); + + unsafe { crate::voxit_host_session_destroy(handle) }; + } +} diff --git a/packages/voxit-host-ffi/tests/header_smoke.c b/packages/voxit-host-ffi/tests/header_smoke.c new file mode 100644 index 0000000..73f040f --- /dev/null +++ b/packages/voxit-host-ffi/tests/header_smoke.c @@ -0,0 +1,32 @@ +#include "voxit_host_ffi.h" + +int main(void) { + VoxitHostConfig config = { + .platform = VOXIT_PLATFORM_MACOS, + }; + VoxitHostSnapshot snapshot = {0}; + VoxitHostSessionHandle *handle = voxit_host_session_create(config); + + if (handle == 0) { + return 1; + } + if (voxit_host_ffi_abi_version() != VOXIT_HOST_FFI_ABI_VERSION) { + voxit_host_session_destroy(handle); + return 2; + } + if (voxit_host_session_copy_snapshot(handle, &snapshot) != VOXIT_STATUS_OK) { + voxit_host_session_destroy(handle); + return 3; + } + if (snapshot.platform != VOXIT_PLATFORM_MACOS) { + voxit_host_session_destroy(handle); + return 4; + } + if (snapshot.auth_method != VOXIT_AUTH_METHOD_CHATGPT_DEVICE_CODE) { + voxit_host_session_destroy(handle); + return 5; + } + + voxit_host_session_destroy(handle); + return 0; +} diff --git a/scripts/build_and_run.sh b/scripts/build_and_run.sh new file mode 100755 index 0000000..d62aee3 --- /dev/null +++ b/scripts/build_and_run.sh @@ -0,0 +1,229 @@ +#!/usr/bin/env bash +set -euo pipefail + +MODE="${1:-run}" +APP_NAME="Voxit" +EXECUTABLE_NAME="VoxitNativeHost" +BUNDLE_ID="ink.hack.voxit" +MIN_SYSTEM_VERSION="14.0" +DEFAULT_SIGN_IDENTITY="x@acg.box" + +ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +PACKAGE_DIR="$ROOT_DIR/native/macos-host" +COMMON_ROOT="$(cd "$(git -C "$ROOT_DIR" rev-parse --git-common-dir)/.." && pwd)" +STAGE_DIR="${VOXIT_NATIVE_HOST_STAGE_DIR:-$COMMON_ROOT/target/voxit-native-host}" +APP_BUNDLE="$STAGE_DIR/$APP_NAME.app" +APP_CONTENTS="$APP_BUNDLE/Contents" +APP_MACOS="$APP_CONTENTS/MacOS" +APP_RESOURCES="$APP_CONTENTS/Resources" +APP_BINARY="$APP_MACOS/$EXECUTABLE_NAME" +INFO_PLIST="$APP_CONTENTS/Info.plist" +APP_ICON_SOURCE="$ROOT_DIR/assets/app-icon/generated/app-icon.icns" +APP_ICON_NAME="AppIcon.icns" +STATUS_ICON_SOURCE="$ROOT_DIR/assets/tray-icon/generated/tray-icon-template.png" +STATUS_ICON_NAME="StatusBarIcon.png" + +RUST_PROFILE="${VOXIT_NATIVE_HOST_RUST_PROFILE:-debug}" +SWIFT_CONFIGURATION="${VOXIT_NATIVE_HOST_SWIFT_CONFIGURATION:-debug}" +RESOLVED_SIGN_IDENTITY="" + +RUST_BUILD_ARGS=(-p voxit-host-ffi) +if [[ "${VOXIT_NATIVE_HOST_LOCKED:-0}" == "1" ]]; then + RUST_BUILD_ARGS+=(--locked) +fi +if [[ "$RUST_PROFILE" == "debug" ]]; then + RUST_LIB_DIR="$ROOT_DIR/target/debug" +else + RUST_BUILD_ARGS+=(--profile "$RUST_PROFILE") + RUST_LIB_DIR="$ROOT_DIR/target/$RUST_PROFILE" +fi + +SWIFT_BUILD_FLAGS=() +if [[ "$SWIFT_CONFIGURATION" == "release" ]]; then + SWIFT_BUILD_FLAGS=(-c release) +fi + +APP_VERSION="$(sed -n '/^\[workspace.package\]/,/^\[/s/^version *= *"\(.*\)"/\1/p' "$ROOT_DIR/Cargo.toml" | head -n 1)" +APP_VERSION="${APP_VERSION:-0.1.0}" + +write_info_plist() { + local icon_plist_entry="" + if [[ -f "$APP_RESOURCES/$APP_ICON_NAME" ]]; then + icon_plist_entry="$(cat <CFBundleIconFile + ${APP_ICON_NAME%.icns} +PLIST +)" + fi + + mkdir -p "$APP_CONTENTS" + cat >"$INFO_PLIST" < + + + + CFBundleExecutable + $EXECUTABLE_NAME + CFBundleIdentifier + $BUNDLE_ID + CFBundleName + $APP_NAME + CFBundleDisplayName + $APP_NAME + CFBundlePackageType + APPL + CFBundleShortVersionString + $APP_VERSION + CFBundleVersion + $APP_VERSION + LSApplicationCategoryType + public.app-category.productivity + LSMinimumSystemVersion + $MIN_SYSTEM_VERSION + LSUIElement + + NSHighResolutionCapable + + NSMicrophoneUsageDescription + Voxit needs microphone access to transcribe your speech. + NSPrincipalClass + NSApplication +$icon_plist_entry + + +PLIST +} + +resolve_signing_identity() { + local requested_identity identity_list identity + + requested_identity="${VOXIT_NATIVE_HOST_SIGN_IDENTITY:-$DEFAULT_SIGN_IDENTITY}" + if [[ "$requested_identity" == "-" ]]; then + RESOLVED_SIGN_IDENTITY="-" + return 0 + fi + + identity_list="$(security find-identity -v -p codesigning 2>/dev/null || true)" + if [[ -n "$requested_identity" ]]; then + while IFS= read -r line; do + identity="${line#*\"}" + identity="${identity%%\"*}" + if [[ -n "$identity" && "$identity" == *"$requested_identity"* ]]; then + RESOLVED_SIGN_IDENTITY="$identity" + return 0 + fi + done <<<"$identity_list" + fi + + while IFS= read -r line; do + identity="${line#*\"}" + identity="${identity%%\"*}" + if [[ -n "$identity" && "$identity" == Apple\ Development:* ]]; then + RESOLVED_SIGN_IDENTITY="$identity" + return 0 + fi + done <<<"$identity_list" + + return 1 +} + +sign_app_bundle() { + local build_root="$1" + local entitlements_path requested_identity + requested_identity="${VOXIT_NATIVE_HOST_SIGN_IDENTITY:-$DEFAULT_SIGN_IDENTITY}" + entitlements_path="$build_root/$EXECUTABLE_NAME-entitlement.plist" + + if resolve_signing_identity; then + if [[ -f "$entitlements_path" ]]; then + codesign \ + --force \ + --deep \ + --options runtime \ + --sign "$RESOLVED_SIGN_IDENTITY" \ + --entitlements "$entitlements_path" \ + "$APP_BUNDLE" + else + codesign \ + --force \ + --deep \ + --options runtime \ + --sign "$RESOLVED_SIGN_IDENTITY" \ + "$APP_BUNDLE" + fi + return + fi + + echo "error: no valid macOS codesigning identity matching \"$requested_identity\" was found." >&2 + echo "error: import the real signing certificate or set VOXIT_NATIVE_HOST_SIGN_IDENTITY to a valid identity." >&2 + echo "error: Voxit native host staging requires a stable codesigning identity." >&2 + exit 1 +} + +stage_app_bundle() { + local build_root build_binary + + MACOSX_DEPLOYMENT_TARGET="$MIN_SYSTEM_VERSION" cargo build "${RUST_BUILD_ARGS[@]}" + + build_root="$( + VOXIT_HOST_FFI_LIB_DIR="$RUST_LIB_DIR" \ + swift build --package-path "$PACKAGE_DIR" "${SWIFT_BUILD_FLAGS[@]}" --show-bin-path + )" + build_binary="$build_root/$EXECUTABLE_NAME" + + rm -f "$build_binary" + VOXIT_HOST_FFI_LIB_DIR="$RUST_LIB_DIR" \ + swift build --package-path "$PACKAGE_DIR" "${SWIFT_BUILD_FLAGS[@]}" --product "$EXECUTABLE_NAME" + + if [[ ! -x "$build_binary" ]]; then + echo "error: Swift build did not produce $build_binary" >&2 + exit 1 + fi + + rm -rf "$APP_BUNDLE" + mkdir -p "$APP_MACOS" "$APP_RESOURCES" + cp "$build_binary" "$APP_BINARY" + chmod +x "$APP_BINARY" + if [[ -f "$APP_ICON_SOURCE" ]]; then + cp "$APP_ICON_SOURCE" "$APP_RESOURCES/$APP_ICON_NAME" + fi + if [[ -f "$STATUS_ICON_SOURCE" ]]; then + cp "$STATUS_ICON_SOURCE" "$APP_RESOURCES/$STATUS_ICON_NAME" + fi + write_info_plist + plutil -lint "$INFO_PLIST" >/dev/null + sign_app_bundle "$build_root" +} + +terminate_running_host() { + pkill -x "$EXECUTABLE_NAME" >/dev/null 2>&1 || true +} + +open_app() { + /usr/bin/open "$APP_BUNDLE" +} + +case "$MODE" in + stage|--stage) + stage_app_bundle + ;; + run) + terminate_running_host + stage_app_bundle + open_app + ;; + verify|--verify) + terminate_running_host + stage_app_bundle + open_app + sleep 1 + pgrep -x "$EXECUTABLE_NAME" >/dev/null + ;; + --debug|debug) + stage_app_bundle + lldb -- "$APP_BINARY" + ;; + *) + echo "usage: $0 [run|stage|--verify|--debug]" >&2 + exit 2 + ;; +esac diff --git a/scripts/bundle-and-open-macos.sh b/scripts/bundle-and-open-macos.sh deleted file mode 100755 index 44ef9dc..0000000 --- a/scripts/bundle-and-open-macos.sh +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env bash -set -euo pipefail - -repo_root="$(git rev-parse --show-toplevel)" -cd "$repo_root" - -cargo bundle --release -p voxit - -app_path="target/release/bundle/osx/Voxit.app" - -if [ "${VOXIT_ALLOW_MULTI_INSTANCE:-0}" != "1" ]; then - existing_pids="$(pgrep -x voxit || true)" - if [ -n "$existing_pids" ]; then - echo "Voxit is already running (pids: $(echo "$existing_pids" | tr '\n' ' '))." - echo "Quit it first to avoid launching multiple menu bar instances (this script uses: open -n)." - echo "To override, rerun with: VOXIT_ALLOW_MULTI_INSTANCE=1" - exit 2 - fi -fi - -# If the bundle has Gatekeeper attributes, Launch Services may block launch via `open` even for local builds. -# Only strip attributes when they are actually present. -if xattr -p com.apple.quarantine "$app_path" >/dev/null 2>&1; then - echo "Detected Gatekeeper quarantine on $app_path; removing for local dev launch..." - xattr -dr com.apple.quarantine "$app_path" -fi - -# `com.apple.provenance` can trigger remote Gatekeeper assessment, which rejects ad-hoc signed local bundles. -if xattr -p com.apple.provenance "$app_path" >/dev/null 2>&1; then - echo "Detected com.apple.provenance on $app_path; removing for local dev launch..." - xattr -dr com.apple.provenance "$app_path" - if xattr -p com.apple.provenance "$app_path" >/dev/null 2>&1; then - echo "Note: com.apple.provenance is still present after removal attempt." - fi -fi - -# `cargo bundle` produces a minimal `.app` with an ad-hoc-signed Mach-O binary. -# Without a bundle signature, macOS Launch Services may refuse to launch it with: -# "code has no resources but signature indicates they must be present". -# Re-signing the bundle ad-hoc keeps local dev runs launchable via `open`. -codesign_identity="${VOXIT_CODESIGN_IDENTITY:--}" -echo "Signing Voxit.app with identity: $codesign_identity" -codesign --force --deep --sign "$codesign_identity" "$app_path" - -# Some macOS builds attach `com.apple.provenance` during signing; strip it again to avoid -# triggering remote Gatekeeper assessment that kills the process shortly after launch. -if xattr -p com.apple.provenance "$app_path" >/dev/null 2>&1; then - echo "Detected com.apple.provenance after codesign; removing for local dev launch..." - xattr -dr com.apple.provenance "$app_path" - if xattr -p com.apple.provenance "$app_path" >/dev/null 2>&1; then - echo "Note: com.apple.provenance is still present after removal attempt." - fi -fi - -open -n "$app_path" - -new_pid="" -for _ in {1..8}; do - sleep 0.25 - pid_now="$(pgrep -x voxit || true)" - if [ -n "$pid_now" ]; then - new_pid="$pid_now" - break - fi -done - -if [ -z "$new_pid" ]; then - echo "Voxit process did not appear after open." -else - # Verify it stays running for a short window (common failure mode: killed after remote assessment). - alive=1 - for _ in {1..24}; do - sleep 0.25 - if ! kill -0 "$new_pid" >/dev/null 2>&1; then - alive=0 - break - fi - done - - if [ "$alive" -eq 1 ]; then - echo "Voxit launched (pid=$new_pid)." - echo "Note: Voxit is a menu bar app (LSUIElement=1). If no window appears, click the tray icon or press the hotkey." - echo "Config: ~/Library/Application Support/hack.ink.voxit/config.toml (ui.start_hidden / hotkey.chord)" - exit 0 - fi -fi - -echo "Voxit did not stay running after launch via open (likely Gatekeeper/assessment kill)." -echo "Signature check:" -codesign -vvv --deep --strict "$app_path" 2>&1 | head -n 20 || true -echo "spctl assessment (expected reject for local ad-hoc builds):" -spctl --assess --type execute --verbose=4 "$app_path" 2>&1 | head -n 20 || true -echo "Falling back to direct exec (bypasses LaunchServices):" -nohup "$app_path/Contents/MacOS/voxit" >/dev/null 2>&1 & -sleep 0.5 -pid_fallback="$(pgrep -x voxit || true)" -if [ -n "$pid_fallback" ]; then - echo "Voxit launched via direct exec (pid=$pid_fallback)." -else - echo "Direct exec failed too. Recent crash reports:" - ls -lt "$HOME/Library/Logs/DiagnosticReports" 2>/dev/null | rg -i "voxit|Voxit" | head -n 5 || true - exit 1 -fi