diff --git a/.cargo/config.toml b/.cargo/config.toml new file mode 100644 index 0000000..36a2bb1 --- /dev/null +++ b/.cargo/config.toml @@ -0,0 +1,29 @@ + +[alias] +release = "packager --release -f all -v" +build-win = "build --bin discord-modloader" +build-unix = "build --lib" + +[target.x86_64-unknown-linux-gnu] +rustflags = [ + "-Zthreads=0", + "-Zshare-generics=y", + "-Ctarget-feature=-crt-static", +] + +[profile.release] +codegen-units = 1 +lto = "fat" +panic = "abort" +opt-level = "z" + +[profile.dev] +panic = "abort" + +[profile.fast-release] +inherits = "release" +codegen-backend = "cranelift" +codegen-units = 1 +lto = "fat" +panic = "abort" +opt-level = "z" diff --git a/.gitignore b/.gitignore index 0b5e021..4bf7f36 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,7 @@ /target -app.asar +/dist + +# Hide everything in /configs except for /configs/icons and /configs/templates +/configs/* +!/configs/icons +!/configs/templates diff --git a/Cargo.lock b/Cargo.lock index ed3ca30..ded1ed2 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1,6 +1,116 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 + +[[package]] +name = "ab_glyph" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec3672c180e71eeaaac3a541fbbc5f5ad4def8b747c595ad30d674e43049f7b0" +dependencies = [ + "ab_glyph_rasterizer", + "owned_ttf_parser", +] + +[[package]] +name = "ab_glyph_rasterizer" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c71b1793ee61086797f5c80b6efa2b8ffa6d5dd703f118545808a7f2e27f7046" + +[[package]] +name = "accesskit" +version = "0.16.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99b76d84ee70e30a4a7e39ab9018e2b17a6a09e31084176cc7c0b2dec036ba45" +dependencies = [ + "enumn", + "serde", +] + +[[package]] +name = "accesskit_atspi_common" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5393c75d4666f580f4cac0a968bc97c36076bb536a129f28210dac54ee127ed" +dependencies = [ + "accesskit", + "accesskit_consumer", + "atspi-common", + "serde", + "thiserror 1.0.69", + "zvariant", +] + +[[package]] +name = "accesskit_consumer" +version = "0.24.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a12dc159d52233c43d9fe5415969433cbdd52c3d6e0df51bda7d447427b9986" +dependencies = [ + "accesskit", + "immutable-chunkmap", +] + +[[package]] +name = "accesskit_macos" +version = "0.17.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfc6c1ecd82053d127961ad80a8beaa6004fb851a3a5b96506d7a6bd462403f6" +dependencies = [ + "accesskit", + "accesskit_consumer", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "once_cell", +] + +[[package]] +name = "accesskit_unix" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "be7f5cf6165be10a54b2655fa2e0e12b2509f38ed6fc43e11c31fdb7ee6230bb" +dependencies = [ + "accesskit", + "accesskit_atspi_common", + "async-channel", + "async-executor", + "async-task", + "atspi", + "futures-lite", + "futures-util", + "serde", + "zbus", +] + +[[package]] +name = "accesskit_windows" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "974e96c347384d9133427167fb8a58c340cb0496988dacceebdc1ed27071023b" +dependencies = [ + "accesskit", + "accesskit_consumer", + "paste", + "static_assertions", + "windows 0.58.0", + "windows-core 0.58.0", +] + +[[package]] +name = "accesskit_winit" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aea3522719f1c44564d03e9469a8e2f3a98b3a8a880bd66d0789c6b9c4a669dd" +dependencies = [ + "accesskit", + "accesskit_macos", + "accesskit_unix", + "accesskit_windows", + "raw-window-handle", + "winit", +] [[package]] name = "addr2line" @@ -17,15 +127,89 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +[[package]] +name = "adler2" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "512761e0bb2578dd7380c6baaa0f4ce03e84f95e960231d1dec8bf4d7d6e2627" + +[[package]] +name = "aes" +version = "0.8.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" +dependencies = [ + "cfg-if 1.0.0", + "cipher", + "cpufeatures", +] + +[[package]] +name = "ahash" +version = "0.7.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "891477e0c6a8957309ee5c45a6368af3ae14bb510732d2684ffa19af310920f9" +dependencies = [ + "getrandom", + "once_cell", + "version_check", +] + +[[package]] +name = "ahash" +version = "0.8.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" +dependencies = [ + "cfg-if 1.0.0", + "getrandom", + "once_cell", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" -version = "1.1.2" +version = "1.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" +checksum = "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916" dependencies = [ "memchr", ] +[[package]] +name = "aligned-vec" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa90d7ce82d4be67b64039a3d588d38dbcc6736577de4a847025ce5b0c468d1" + +[[package]] +name = "android-activity" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef6978589202a00cd7e118380c448a08b6ed394c3a8df3a430d0898e3a42d046" +dependencies = [ + "android-properties", + "bitflags 2.8.0", + "cc", + "cesu8", + "jni", + "jni-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-tzdata" version = "0.1.1" @@ -43,63 +227,109 @@ dependencies = [ [[package]] name = "anstream" -version = "0.6.4" +version = "0.6.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab91ebe16eb252986481c5b62f6098f3b698a45e34b5b98200cf20dd2484a44" +checksum = "8acc5369981196006228e28809f761875c0327210a891e941f4c683b3a99529b" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", + "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" -version = "1.0.4" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7079075b41f533b8c61d2a4d073c4676e1f8b249ff94a393b0595db304e0dd87" +checksum = "55cc3b69f167a1ef2e161439aa98aed94e6028e5f9a59be9a6ffb47aef1651f9" [[package]] name = "anstyle-parse" -version = "0.2.2" +version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "317b9a89c1868f5ea6ff1d9539a69f45dffc21ce321ac1fd1160dfa48c8e2140" +checksum = "3b2d16507662817a6a20a9ea92df6652ee4f94f914589377d69f3b21bc5798a9" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" -version = "1.0.0" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" +checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" dependencies = [ - "windows-sys", + "windows-sys 0.59.0", ] [[package]] name = "anstyle-wincon" -version = "3.0.1" +version = "3.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0699d10d2f4d628a98ee7b57b289abbc98ff3bad977cb3152709d4bf2330628" +checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e" dependencies = [ "anstyle", - "windows-sys", + "once_cell", + "windows-sys 0.59.0", +] + +[[package]] +name = "anyhow" +version = "1.0.95" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34ac096ce696dc2fcabef30516bb13c0a68a11d30131d3df6f04711467681b04" + +[[package]] +name = "arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dde20b3d026af13f561bdd0f15edf01fc734f0dafcedbaf42bba506a9517f223" +dependencies = [ + "derive_arbitrary", +] + +[[package]] +name = "arc-swap" +version = "1.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69f7f8c3906b62b754cd5326047894316021dcfe5a194c8ea52bdd94934a3457" + +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", ] +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + [[package]] name = "arrayvec" -version = "0.7.4" +version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96d30a06541fbafbc7f82ed10c06164cfbd2c401138f6addd8404629c4b16711" +checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" + +[[package]] +name = "as-raw-xcb-connection" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175571dd1d178ced59193a6fc02dde1b972eb0bc56c892cde9beeceac5bf0f6b" [[package]] name = "asar" -version = "0.2.0" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7386663b76ee42bd2a0c0314948e129fa29092cf7135e0566c520e69c5b4320" +checksum = "1b9051a11bd40b01b4f8b926956b9f22ff5ef08fcbc7eb34693e2a73982ff85e" dependencies = [ "byteorder", "clap", @@ -110,1062 +340,6449 @@ dependencies = [ "serde_json", "serde_with", "sha2", - "thiserror", + "thiserror 1.0.69", "walkdir", "wax", ] [[package]] -name = "autocfg" -version = "1.1.0" +name = "async-broadcast" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] [[package]] -name = "backtrace" -version = "0.3.69" +name = "async-channel" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" +checksum = "89b47800b0be77592da0afd425cc03468052844aff33b84e33cc696f64e77b6a" dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", ] [[package]] -name = "base64" -version = "0.13.1" +name = "async-executor" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e1b586273c5702936fe7b7d6896644d8be71e6314cfe09d3167c95f712589e8" +checksum = "30ca9a001c1e8ba5149f91a74362376cc6bc5b919d92d988668657bd570bdcec" +dependencies = [ + "async-task", + "concurrent-queue", + "fastrand", + "futures-lite", + "slab", +] [[package]] -name = "block-buffer" -version = "0.10.4" +name = "async-fs" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" +checksum = "ebcd09b382f40fcd159c2d695175b2ae620ffa5f3bd6f664131efff4e8b9e04a" dependencies = [ - "generic-array", + "async-lock", + "blocking", + "futures-lite", ] [[package]] -name = "brownstone" -version = "3.0.0" +name = "async-io" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5839ee4f953e811bfdcf223f509cb2c6a3e1447959b0bff459405575bc17f22" +checksum = "43a2b323ccce0a1d90b449fd71f2a06ca7faa7c54c2751f06c9bd851fc061059" dependencies = [ - "arrayvec", + "async-lock", + "cfg-if 1.0.0", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix", + "slab", + "tracing", + "windows-sys 0.59.0", ] [[package]] -name = "bstr" -version = "0.2.17" +name = "async-lock" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba3569f383e8f1598449f1a423e72e99569137b47740b1da11ef19af3d5c3223" +checksum = "ff6e472cdea888a4bd64f342f09b3f50e1886d32afe8df3d663c01140b811b18" dependencies = [ - "lazy_static", - "memchr", - "regex-automata 0.1.10", + "event-listener", + "event-listener-strategy", + "pin-project-lite", ] [[package]] -name = "bumpalo" -version = "3.14.0" +name = "async-process" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f30e7476521f6f8af1a1c4c0b8cc94f0bee37d91763d0ca2665f299b6cd8aec" +checksum = "63255f1dc2381611000436537bbedfe83183faa303a5a0edaf191edef06526bb" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if 1.0.0", + "event-listener", + "futures-lite", + "rustix", + "tracing", +] [[package]] -name = "byteorder" -version = "1.5.0" +name = "async-recursion" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" +checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] [[package]] -name = "cc" -version = "1.0.83" +name = "async-signal" +version = "0.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0" +checksum = "637e00349800c0bdf8bfc21ebbc0b6524abea702b0da4168ac00d070d0c0b9f3" dependencies = [ - "libc", + "async-io", + "async-lock", + "atomic-waker", + "cfg-if 1.0.0", + "futures-core", + "futures-io", + "rustix", + "signal-hook-registry", + "slab", + "windows-sys 0.59.0", ] [[package]] -name = "cfg-if" -version = "1.0.0" +name = "async-task" +version = "4.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] -name = "chrono" -version = "0.4.31" +name = "async-trait" +version = "0.1.85" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f2c685bad3eb3d45a01354cedb7d5faa66194d1d58ba6e267a8de788f79db38" +checksum = "3f934833b4b7233644e5848f235df3f57ed8c80f1528a26c3dfa13d2147fa056" dependencies = [ - "android-tzdata", - "iana-time-zone", - "num-traits", - "serde", - "windows-targets", + "proc-macro2", + "quote", + "syn 2.0.96", ] [[package]] -name = "clap" -version = "4.4.7" +name = "atomic-waker" +version = "1.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac495e00dcec98c83465d5ad66c5c4fabd652fd6686e7c6269b117e729a6f17b" -dependencies = [ - "clap_builder", - "clap_derive", -] +checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] -name = "clap_builder" -version = "4.4.7" +name = "atspi" +version = "0.22.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c77ed9a32a62e6ca27175d00d29d05ca32e396ea1eb5fb01d8256b669cec7663" +checksum = "be534b16650e35237bb1ed189ba2aab86ce65e88cc84c66f4935ba38575cecbf" dependencies = [ - "anstream", - "anstyle", - "clap_lex", - "strsim", + "atspi-common", + "atspi-connection", + "atspi-proxies", ] [[package]] -name = "clap_derive" -version = "4.4.7" +name = "atspi-common" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cf9804afaaf59a91e75b022a30fb7229a7901f60c755489cc61c9b423b836442" +checksum = "1909ed2dc01d0a17505d89311d192518507e8a056a48148e3598fef5e7bb6ba7" dependencies = [ - "heck", - "proc-macro2", - "quote", - "syn", + "enumflags2", + "serde", + "static_assertions", + "zbus", + "zbus-lockstep", + "zbus-lockstep-macros", + "zbus_names", + "zvariant", ] [[package]] -name = "clap_lex" +name = "atspi-connection" version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "702fc72eb24e5a1e48ce58027a675bc24edd52096d5397d4aea7c6dd9eca0bd1" - -[[package]] -name = "color-eyre" -version = "0.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a667583cca8c4f8436db8de46ea8233c42a7d9ae424a82d338f2e4675229204" +checksum = "430c5960624a4baaa511c9c0fcc2218e3b58f5dbcc47e6190cafee344b873333" dependencies = [ - "backtrace", - "color-spantrace", - "eyre", - "indenter", - "once_cell", - "owo-colors", - "tracing-error", + "atspi-common", + "atspi-proxies", + "futures-lite", + "zbus", ] [[package]] -name = "color-spantrace" -version = "0.2.0" +name = "atspi-proxies" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ba75b3d9449ecdccb27ecbc479fdc0b87fa2dd43d2f8298f9bf0e59aacc8dce" +checksum = "a5e6c5de3e524cf967569722446bcd458d5032348554d9a17d7d72b041ab7496" dependencies = [ - "once_cell", - "owo-colors", - "tracing-core", - "tracing-error", + "atspi-common", + "serde", + "zbus", + "zvariant", ] [[package]] -name = "colorchoice" -version = "1.0.0" +name = "autocfg" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" +checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26" [[package]] -name = "const_format" -version = "0.2.32" +name = "av1-grain" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +checksum = "6678909d8c5d46a42abcf571271e15fdbc0a225e3646cf23762cd415046c78bf" dependencies = [ - "const_format_proc_macros", + "anyhow", + "arrayvec", + "log", + "nom", + "num-rational", + "v_frame", ] [[package]] -name = "const_format_proc_macros" -version = "0.2.32" +name = "avif-serialize" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +checksum = "e335041290c43101ca215eed6f43ec437eb5a42125573f600fc3fa42b9bddd62" dependencies = [ - "proc-macro2", - "quote", - "unicode-xid", + "arrayvec", ] [[package]] -name = "core-foundation-sys" -version = "0.8.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e496a50fda8aacccc86d7529e2c1e0892dbd0f898a6b5645b5561b89c3210efa" - -[[package]] -name = "cpufeatures" -version = "0.2.11" +name = "backtrace" +version = "0.3.71" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce420fe07aecd3e67c5f910618fe65e94158f6dcc0adf44e00d69ce2bdfe0fd0" +checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" dependencies = [ + "addr2line", + "cc", + "cfg-if 1.0.0", "libc", + "miniz_oxide 0.7.4", + "object", + "rustc-demangle", ] [[package]] -name = "crypto-common" -version = "0.1.6" +name = "base64" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" -dependencies = [ - "generic-array", - "typenum", -] +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] -name = "darling" -version = "0.20.3" +name = "base64" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0209d94da627ab5605dcccf08bb18afa5009cfbef48d8a8b7d7bdbc79be25c5e" -dependencies = [ - "darling_core", - "darling_macro", -] +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] -name = "darling_core" -version = "0.20.3" +name = "bindgen" +version = "0.70.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "177e3443818124b357d8e76f53be906d60937f0d3a90773a664fa63fa253e621" +checksum = "f49d8fed880d473ea71efb9bf597651e77201bdd4893efe54c9e5d65ae04ce6f" dependencies = [ - "fnv", - "ident_case", + "bitflags 2.8.0", + "cexpr", + "clang-sys", + "itertools 0.13.0", + "log", + "prettyplease", "proc-macro2", "quote", - "strsim", - "syn", + "regex", + "rustc-hash 1.1.0", + "shlex", + "syn 2.0.96", ] [[package]] -name = "darling_macro" -version = "0.20.3" +name = "bit_field" +version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "836a9bbc7ad63342d6d6e7b815ccab164bc77a2d95d84bc3117a8c0d5c98e2d5" -dependencies = [ - "darling_core", - "quote", - "syn", -] +checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61" [[package]] -name = "deranged" -version = "0.3.9" +name = "bitflags" +version = "1.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f32d04922c60427da6f9fef14d042d9edddef64cb9d4ce0d64d0685fbeb1fd3" -dependencies = [ - "powerfmt", - "serde", -] +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] -name = "detours-sys" -version = "0.1.2" +name = "bitflags" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a089d64a8b2e0fa98aa661f1323f49c7c382920504f47fcd2d157bd75cd1587" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" dependencies = [ - "cc", + "serde", ] [[package]] -name = "digest" -version = "0.10.7" +name = "bitstream-io" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" -dependencies = [ - "block-buffer", - "crypto-common", -] +checksum = "6099cdc01846bc367c4e7dd630dc5966dccf36b652fae7a74e17b640411a91b2" [[package]] -name = "either" -version = "1.9.0" +name = "block" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a26ae43d7bcc3b814de94796a5e736d4029efb0ee900c12e2d54c993ad1a1e07" +checksum = "0d8c1fef690941d3e7788d328517591fecc684c084084702d6ff1641e993699a" [[package]] -name = "eyre" -version = "0.6.8" +name = "block-buffer" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c2b6b5a29c02cdc822728b7d7b8ae1bab3e3b05d44522770ddd49722eeac7eb" +checksum = "3078c7629b62d3f0439517fa394996acacc5cbc91c5a20d8c658e77abd503a71" dependencies = [ - "indenter", - "once_cell", + "generic-array", ] [[package]] -name = "fnv" -version = "1.0.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" - -[[package]] -name = "generic-array" -version = "0.14.7" +name = "block2" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a" +checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" dependencies = [ - "typenum", - "version_check", + "objc2", ] [[package]] -name = "gimli" -version = "0.28.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0" - -[[package]] -name = "hashbrown" -version = "0.12.3" +name = "blocking" +version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +checksum = "703f41c54fc768e63e091340b424302bb1c29ef4aa0c7f10fe849dfb114d29ea" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] [[package]] -name = "heck" -version = "0.4.1" +name = "built" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8" +checksum = "c360505aed52b7ec96a3636c3f039d99103c37d1d9b4f7a8c743d3ea9ffcd03b" [[package]] -name = "hex" -version = "0.4.3" +name = "bumpalo" +version = "3.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +checksum = "79296716171880943b8470b5f8d03aa55eb2e645a4874bdbb28adb49162e012c" [[package]] -name = "iana-time-zone" -version = "0.1.58" +name = "bytemuck" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8326b86b6cff230b97d0d312a6c40a60726df3332e721f72a1b035f451663b20" -dependencies = [ - "android_system_properties", - "core-foundation-sys", - "iana-time-zone-haiku", - "js-sys", - "wasm-bindgen", - "windows-core", -] +checksum = "ef657dfab802224e671f5818e9a4935f9b1957ed18e58292690cc39e7a4092a3" [[package]] -name = "iana-time-zone-haiku" -version = "0.1.2" +name = "byteorder" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" -dependencies = [ - "cc", -] +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] -name = "ident_case" -version = "1.0.1" +name = "byteorder-lite" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" +checksum = "8f1fe948ff07f4bd06c30984e69f5b4899c516a3ef74f34df92a2df2ab535495" [[package]] -name = "indent_write" -version = "2.2.0" +name = "bytes" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0cfe9645a18782869361d9c8732246be7b410ad4e919d3609ebabdac00ba12c3" +checksum = "325918d6fe32f23b19878fe4b34794ae41fc19ddbe53b10571a4874d44ffd39b" [[package]] -name = "indenter" -version = "0.3.3" +name = "bzip2" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" +checksum = "bdb116a6ef3f6c3698828873ad02c3014b3c85cadb88496095628e3ef1e347f8" +dependencies = [ + "bzip2-sys", + "libc", +] [[package]] -name = "indexmap" -version = "1.9.3" +name = "bzip2-sys" +version = "0.1.11+1.0.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +checksum = "736a955f3fa7875102d57c82b8cac37ec45224a07fd32d58f9f7a186b6cd4cdc" dependencies = [ - "autocfg", - "hashbrown", - "serde", + "cc", + "libc", + "pkg-config", ] [[package]] -name = "is_executable" -version = "1.0.1" +name = "calloop" +version = "0.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fa9acdc6d67b75e626ad644734e8bc6df893d9cd2a834129065d3dd6158ea9c8" +checksum = "b99da2f8558ca23c71f4fd15dc57c906239752dd27ff3c00a1d56b685b7cbfec" dependencies = [ - "winapi", + "bitflags 2.8.0", + "log", + "polling", + "rustix", + "slab", + "thiserror 1.0.69", ] [[package]] -name = "itertools" -version = "0.10.5" +name = "calloop-wayland-source" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b0fd2260e829bddf4cb6ea802289de2f86d6a7a690192fbe91b3f46e0f2c8473" +checksum = "95a66a987056935f7efce4ab5668920b5d0dac4a7c99991a67395f13702ddd20" dependencies = [ - "either", + "calloop", + "rustix", + "wayland-backend", + "wayland-client", ] [[package]] -name = "itoa" -version = "1.0.9" +name = "cc" +version = "1.2.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af150ab688ff2122fcef229be89cb50dd66af9e01a4ff320cc137eecc9bacc38" +checksum = "13208fcbb66eaeffe09b99fffbe1af420f00a7b35aa99ad683dfc1aa76145229" +dependencies = [ + "jobserver", + "libc", + "shlex", +] [[package]] -name = "joinery" -version = "2.1.0" +name = "cesu8" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72167d68f5fce3b8655487b8038691a3c9984ee769590f93f2a631f4ad64e4f5" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" [[package]] -name = "js-sys" -version = "0.3.65" +name = "cexpr" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "54c0c35952f67de54bb584e9fd912b3023117cbafc0a77d8f3dee1fb5f572fe8" +checksum = "6fac387a98bb7c37292057cffc56d62ecb629900026402633ae9160df93a8766" dependencies = [ - "wasm-bindgen", + "nom", ] [[package]] -name = "lazy_static" -version = "1.4.0" +name = "cfg-expr" +version = "0.15.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "d067ad48b8650848b989a59a86c6c36a995d02d2bf778d45c3c5d57bc2718f02" +dependencies = [ + "smallvec", + "target-lexicon", +] [[package]] -name = "libc" -version = "0.2.150" +name = "cfg-if" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "89d92a4743f9a61002fae18374ed11e7973f530cb3a3255fb354818118b2203c" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" [[package]] -name = "log" -version = "0.4.20" +name = "cfg-if" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5e6163cb8c49088c2c36f57875e58ccd8c87c7427f7fbd50ea6710b2f3f2e8f" +checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" [[package]] -name = "memchr" -version = "2.6.4" +name = "cfg_aliases" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] -name = "minimal-lexical" -version = "0.2.1" +name = "cgl" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" +checksum = "0ced0551234e87afee12411d535648dd89d2e7f34c78b753395567aff3d447ff" +dependencies = [ + "libc", +] [[package]] -name = "miniz_oxide" -version = "0.7.1" +name = "chrono" +version = "0.4.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" +checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" dependencies = [ - "adler", + "android-tzdata", + "iana-time-zone", + "js-sys", + "num-traits", + "serde", + "wasm-bindgen", + "windows-targets 0.52.6", ] [[package]] -name = "modhook" -version = "1.0.3" +name = "cipher" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "asar", - "clap", - "detours-sys", - "widestring", - "winapi", + "crypto-common", + "inout", ] [[package]] -name = "nom" -version = "7.1.3" +name = "clang-sys" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +checksum = "0b023947811758c97c59bf9d1c188fd619ad4718dcaa767947df1cadb14f39f4" dependencies = [ - "memchr", - "minimal-lexical", + "glob", + "libc", + "libloading", ] [[package]] -name = "nom-supreme" -version = "0.8.0" +name = "clap" +version = "4.5.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bd3ae6c901f1959588759ff51c95d24b491ecb9ff91aa9c2ef4acc5b1dcab27" +checksum = "769b0145982b4b48713e01ec42d61614425f27b7058bda7180a3a41f30104796" dependencies = [ - "brownstone", - "indent_write", - "joinery", - "memchr", - "nom", + "clap_builder", + "clap_derive", ] [[package]] -name = "num-traits" -version = "0.2.17" +name = "clap_builder" +version = "4.5.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39e3200413f237f41ab11ad6d161bc7239c84dcb631773ccd7de3dfe4b5c267c" +checksum = "1b26884eb4b57140e4d2d93652abfa49498b938b3c9179f9fc487b0acc3edad7" dependencies = [ - "autocfg", + "anstream", + "anstyle", + "clap_lex", + "strsim", ] [[package]] -name = "object" -version = "0.32.1" +name = "clap_derive" +version = "4.5.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" +checksum = "54b755194d6389280185988721fffba69495eed5ee9feeee9a599b53db80318c" dependencies = [ - "memchr", + "heck", + "proc-macro2", + "quote", + "syn 2.0.96", ] [[package]] -name = "once_cell" -version = "1.18.0" +name = "clap_lex" +version = "0.7.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" +checksum = "f46ad14479a25103f283c0f10005961cf086d8dc42205bb44c46ac563475dca6" [[package]] -name = "owo-colors" -version = "3.5.0" +name = "clipboard-win" +version = "3.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" +checksum = "9fdf5e01086b6be750428ba4a40619f847eb2e95756eee84b18e06e5f0b50342" +dependencies = [ + "lazy-bytes-cast", + "winapi 0.3.9", +] [[package]] -name = "pin-project-lite" -version = "0.2.13" +name = "color-eyre" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" +checksum = "55146f5e46f237f7423d74111267d4597b59b0dad0ffaf7303bce9945d843ad5" +dependencies = [ + "backtrace", + "color-spantrace", + "eyre", + "indenter", + "once_cell", + "owo-colors", + "tracing-error", +] [[package]] -name = "pori" -version = "0.0.0" +name = "color-spantrace" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a63d338dec139f56dacc692ca63ad35a6be6a797442479b55acd611d79e906" +checksum = "cd6be1b2a7e382e2b98b43b2adcca6bb0e465af0bdd38123873ae61eb17a72c2" dependencies = [ - "nom", + "once_cell", + "owo-colors", + "tracing-core", + "tracing-error", ] [[package]] -name = "powerfmt" -version = "0.2.0" +name = "color_quant" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" [[package]] -name = "proc-macro2" -version = "1.0.69" +name = "colorchoice" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "134c189feb4956b20f6f547d2cf727d4c0fe06722b20a0eec87ed445a97f92da" -dependencies = [ - "unicode-ident", -] +checksum = "5b63caa9aa9397e2d9480a9b13673856c78d8ac123288526c37d7839f2a86990" [[package]] -name = "quote" -version = "1.0.33" +name = "combine" +version = "4.6.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5267fca4496028628a95160fc423a33e8b2e6af8a5302579e322e4b520293cae" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" dependencies = [ - "proc-macro2", + "bytes", + "memchr", ] [[package]] -name = "regex" -version = "1.10.2" +name = "concurrent-queue" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" dependencies = [ - "aho-corasick", - "memchr", - "regex-automata 0.4.3", - "regex-syntax", + "crossbeam-utils", ] [[package]] -name = "regex-automata" -version = "0.1.10" +name = "const_format" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c230d73fb8d8c1b9c0b3135c5142a8acee3a0558fb8db5cf1cb65f8d7862132" +checksum = "126f97965c8ad46d6d9163268ff28432e8f6a1196a55578867832e3049df63dd" +dependencies = [ + "const_format_proc_macros", +] [[package]] -name = "regex-automata" -version = "0.4.3" +name = "const_format_proc_macros" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" dependencies = [ - "aho-corasick", - "memchr", - "regex-syntax", + "proc-macro2", + "quote", + "unicode-xid", ] [[package]] -name = "regex-syntax" -version = "0.8.2" +name = "constant_time_eq" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" +checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6" [[package]] -name = "rustc-demangle" -version = "0.1.23" +name = "convert_case" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] [[package]] -name = "ryu" -version = "1.0.15" +name = "cookie" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad4cc8da4ef723ed60bced201181d83791ad433213d8c24efffda1eec85d741" +checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" +dependencies = [ + "percent-encoding", + "time", + "version_check", +] [[package]] -name = "same-file" -version = "1.0.6" +name = "cookie_store" +version = "0.21.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +checksum = "2eac901828f88a5241ee0600950ab981148a18f2f756900ffba1b125ca6a3ef9" dependencies = [ + "cookie", + "document-features", + "idna", + "indexmap 2.7.1", + "log", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + +[[package]] +name = "copypasta" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "deb85422867ca93da58b7f95fb5c0c10f6183ed6e1ef8841568968a896d3a858" +dependencies = [ + "clipboard-win", + "objc", + "objc-foundation", + "objc_id", + "smithay-clipboard", + "x11-clipboard", +] + +[[package]] +name = "core-foundation" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e195e091a93c46f7102ec7818a2aa394e1e1771c3ab4825963fa03e45afb8f" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b55271e5c8c478ad3f38ad24ef34923091e0548492a266d19b3c0b4d82574c63" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + +[[package]] +name = "core-graphics" +version = "0.23.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c07782be35f9e1140080c6b96f0d44b739e2278479f64e02fdab4e32dfd8b081" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "core-graphics-types", + "foreign-types 0.5.0", + "libc", +] + +[[package]] +name = "core-graphics-types" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45390e6114f68f718cc7a830514a96f903cccd70d02a8f6d9f643ac4ba45afaf" +dependencies = [ + "bitflags 1.3.2", + "core-foundation 0.9.4", + "libc", +] + +[[package]] +name = "cpufeatures" +version = "0.2.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59ed5838eebb26a2bb2e58f6d5b5316989ae9d08bab10e0e6d103e656d1b0280" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69e6e4d7b33a94f0991c26729976b10ebde1d34c3ee82408fb536164fa10d636" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" + +[[package]] +name = "crc32fast" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a97769d94ddab943e4510d138150169a2758b5ef3eb191a9ee688de3e23ef7b3" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "crossbeam-deque" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.9.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" +dependencies = [ + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.8.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" + +[[package]] +name = "crunchy" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43da5946c66ffcc7745f48db692ffbb10a83bfe0afd96235c5c2a4fb23994929" + +[[package]] +name = "crypto-common" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1bfb12502f3fc46cca1bb51ac28df9d618d813cdc3d2f25b9fe775a34af26bb3" +dependencies = [ + "generic-array", + "typenum", +] + +[[package]] +name = "cursor-icon" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "96a6ac251f4a2aca6b3f91340350eab87ae57c3f127ffeb585e92bd336717991" + +[[package]] +name = "darling" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f63b86c8a8826a49b8c21f08a2d07338eec8d900540f8630dc76284be802989" +dependencies = [ + "darling_core", + "darling_macro", +] + +[[package]] +name = "darling_core" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95133861a8032aaea082871032f5815eb9e98cef03fa916ab4500513994df9e5" +dependencies = [ + "fnv", + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.96", +] + +[[package]] +name = "darling_macro" +version = "0.20.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d336a2a514f6ccccaa3e09b02d41d35330c07ddf03a62165fcec10bb561c7806" +dependencies = [ + "darling_core", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "deflate64" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da692b8d1080ea3045efaab14434d40468c3d8657e42abddfffca87b428f4c1b" + +[[package]] +name = "deranged" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42b6fa04a440b495c8b04d0e71b707c585f83cb9cb28cf8cd0d976c315e31b4" +dependencies = [ + "powerfmt", + "serde", +] + +[[package]] +name = "derive_arbitrary" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "30542c1ad912e0e3d22a1935c290e12e8a29d704a420177a31faad4a601a0800" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "detours-sys" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a089d64a8b2e0fa98aa661f1323f49c7c382920504f47fcd2d157bd75cd1587" +dependencies = [ + "cc", +] + +[[package]] +name = "dialog" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "736bab36d647d14c985725a57a4110a1182c6852104536cd42f1c97e96d29bf0" +dependencies = [ + "dirs 2.0.2", + "rpassword", +] + +[[package]] +name = "digest" +version = "0.10.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" +dependencies = [ + "block-buffer", + "crypto-common", + "subtle", +] + +[[package]] +name = "dioxus" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e7d69c4bd197db4739654af0a37c426417b45d5f495df8d10fafeb5da846dca" +dependencies = [ + "dioxus-core", + "dioxus-core-macro", + "dioxus-hooks", + "dioxus-signals", +] + +[[package]] +name = "dioxus-cli-config" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d20764ae5fbe886e4602fdd6f2420ed03697eccba51605926f54693bd65879f3" + +[[package]] +name = "dioxus-clipboard" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe492404c00f658d2a5f2cefa60bb9f413c65528182d4027939596fbd49583b4" +dependencies = [ + "copypasta", + "dioxus-lib", +] + +[[package]] +name = "dioxus-config-macro" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75cbf582fbb1c32d34a1042ea675469065574109c95154468710a4d73ee98b49" +dependencies = [ + "proc-macro2", + "quote", +] + +[[package]] +name = "dioxus-core" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23f0eb105e433754659d8e2dd8ac4590aae92f4f2e13770b49700fdc08e87d2c" +dependencies = [ + "const_format", + "dioxus-core-types", + "futures-channel", + "futures-util", + "generational-box", + "longest-increasing-subsequence", + "rustc-hash 1.1.0", + "rustversion", + "slab", + "slotmap", + "tracing", + "warnings", +] + +[[package]] +name = "dioxus-core-macro" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a798c5538481e6bc831514a5dd10ee53e3df12fd13a88d64e787e0268443adcd" +dependencies = [ + "convert_case", + "dioxus-rsx", + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "dioxus-core-types" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91a82fccfa48574eb7aa183e297769540904694844598433a9eb55896ad9f93b" +dependencies = [ + "once_cell", +] + +[[package]] +name = "dioxus-document" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f195e3027dea214d34fd87c8189470cf24ef3f9ff10b13675cc9d80e0db07517" +dependencies = [ + "dioxus-core", + "dioxus-core-macro", + "dioxus-core-types", + "dioxus-html", + "futures-channel", + "futures-util", + "generational-box", + "lazy-js-bundle", + "serde", + "serde_json", + "tracing", +] + +[[package]] +name = "dioxus-history" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ae4e22616c698f35b60727313134955d885de2d32e83689258e586ebc9b7909" +dependencies = [ + "dioxus-core", + "tracing", +] + +[[package]] +name = "dioxus-hooks" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "948e2b3f20d9d4b2c300aaa60281b1755f3298684448920b27106da5841896d0" +dependencies = [ + "dioxus-core", + "dioxus-signals", + "futures-channel", + "futures-util", + "generational-box", + "rustversion", + "slab", + "tracing", + "warnings", +] + +[[package]] +name = "dioxus-html" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "664518f9d9b7d765755e79db271562b36c271e97709ff3bc0c71234700f5e8ae" +dependencies = [ + "async-trait", + "dioxus-core", + "dioxus-core-macro", + "dioxus-core-types", + "dioxus-hooks", + "dioxus-html-internal-macro", + "enumset", + "euclid", + "futures-channel", + "generational-box", + "keyboard-types", + "lazy-js-bundle", + "rustversion", + "tracing", +] + +[[package]] +name = "dioxus-html-internal-macro" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43ba87b53688a2c9f619ecdf4b3b955bc1f08bd0570a80a0d626c405f6d14a76" +dependencies = [ + "convert_case", + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "dioxus-lib" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5405b71aa9b8b0c3e0d22728f12f34217ca5277792bd315878cc6ecab7301b72" +dependencies = [ + "dioxus-config-macro", + "dioxus-core", + "dioxus-core-macro", + "dioxus-document", + "dioxus-history", + "dioxus-hooks", + "dioxus-html", + "dioxus-rsx", + "dioxus-signals", + "warnings", +] + +[[package]] +name = "dioxus-router" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc284034f3fffb198a189eaaa8be854520770160d79bdc85b8ad64a8e8170433" +dependencies = [ + "dioxus-cli-config", + "dioxus-history", + "dioxus-lib", + "dioxus-router-macro", + "rustversion", + "tracing", + "url", + "urlencoding", +] + +[[package]] +name = "dioxus-router-macro" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb48c4f1e8fd66716191c9bd492b532de3dd0b59a278c0a35dd0755b7a05e2fc" +dependencies = [ + "proc-macro2", + "quote", + "slab", + "syn 2.0.96", +] + +[[package]] +name = "dioxus-rsx" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb588e05800b5a7eb90b2f40fca5bbd7626e823fb5e1ba21e011de649b45aa1" +dependencies = [ + "proc-macro2", + "proc-macro2-diagnostics", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "dioxus-signals" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e8f8811f32274228aff06e01c891d0828f5461d33b4fa9465dc69b995fc12c1" +dependencies = [ + "dioxus-core", + "futures-channel", + "futures-util", + "generational-box", + "once_cell", + "parking_lot", + "rustc-hash 1.1.0", + "tracing", + "warnings", +] + +[[package]] +name = "dirs" +version = "2.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13aea89a5c93364a98e9b37b2fa237effbb694d5cfe01c5b70941f7eb087d5e3" +dependencies = [ + "cfg-if 0.1.10", + "dirs-sys 0.3.7", +] + +[[package]] +name = "dirs" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +dependencies = [ + "dirs-sys 0.4.1", +] + +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys 0.5.0", +] + +[[package]] +name = "dirs-sys" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" +dependencies = [ + "libc", + "redox_users 0.4.6", + "winapi 0.3.9", +] + +[[package]] +name = "dirs-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +dependencies = [ + "libc", + "option-ext", + "redox_users 0.4.6", + "windows-sys 0.48.0", +] + +[[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 0.5.0", + "windows-sys 0.59.0", +] + +[[package]] +name = "discord-modloader" +version = "0.2.0-beta.1" +dependencies = [ + "clap", + "electron-hook", + "electron-hook-build-scripts", + "gui", + "libc", + "modloader-core", +] + +[[package]] +name = "dispatch" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd0c93bb4b0c6d9b77f4435b0ae98c24d17f1c45b2ff844c6151a07256ca923b" + +[[package]] +name = "displaydoc" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", +] + +[[package]] +name = "document-features" +version = "0.2.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb6969eaabd2421f8a2775cfd2471a2b634372b4a25d41e3bd647b79912850a0" +dependencies = [ + "litrs", +] + +[[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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f25c0e292a7ca6d6498557ff1df68f32c99850012b6ea401cf8daf771f22ff53" + +[[package]] +name = "easer" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fba524f8b83c9c5bde02c2bb1627de9d1f81980489a6d54168cdfd08c258f917" +dependencies = [ + "num-traits", +] + +[[package]] +name = "either" +version = "1.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60b1af1c220855b6ceac025d3f6ecdd2b7c4894bfe9cd9bda4fbb4bc7c0d4cf0" + +[[package]] +name = "electron-hook" +version = "0.2.0-beta.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "003a7f4d22a5129b75a7d28cc46f696255cb89c29b8bf8ff99f6cef491f08c8a" +dependencies = [ + "asar", + "detours-sys", + "dirs 5.0.1", + "libc", + "retour", + "uuid", + "widestring", + "winapi 0.3.9", +] + +[[package]] +name = "electron-hook-build-scripts" +version = "0.2.0-beta.2" + +[[package]] +name = "encoding_rs" +version = "0.8.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75030f3c4f45dafd7586dd6780965a8c7e8e285a5ecb86713e63a79c5b2766f3" +dependencies = [ + "cfg-if 1.0.0", +] + +[[package]] +name = "endi" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" + +[[package]] +name = "enumflags2" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba2f4b465f5318854c6f8dd686ede6c0a9dc67d4b1ac241cf0eb51521a309147" +dependencies = [ + "enumflags2_derive", + "serde", +] + +[[package]] +name = "enumflags2_derive" +version = "0.7.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc4caf64a58d7a6d65ab00639b046ff54399a39f5f2554728895ace4b297cd79" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "enumn" +version = "0.1.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f9ed6b3789237c8a0c1c505af1c7eb2c560df6186f01b098c3a1064ea532f38" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "enumset" +version = "1.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d07a4b049558765cef5f0c1a273c3fc57084d768b44d2f98127aef4cceb17293" +dependencies = [ + "enumset_derive", +] + +[[package]] +name = "enumset_derive" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59c3b24c345d8c314966bdc1832f6c2635bfcce8e7cf363bd115987bba2ee242" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "equivalent" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" + +[[package]] +name = "errno" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" +dependencies = [ + "libc", + "windows-sys 0.59.0", +] + +[[package]] +name = "euclid" +version = "0.22.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ad9cdb4b747e485a12abb0e6566612956c7a1bafa3bdb8d682c5b6d403589e48" +dependencies = [ + "num-traits", +] + +[[package]] +name = "event-listener" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3492acde4c3fc54c845eaab3eed8bd00c7a7d881f78bfc801e43a93dec1331ae" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c3e4e0dd3673c1139bf041f3008816d9cf2946bbfac2945c09e523b8d7b05b2" +dependencies = [ + "event-listener", + "pin-project-lite", +] + +[[package]] +name = "exr" +version = "1.73.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83197f59927b46c04a183a619b7c29df34e63e63c7869320862268c0ef687e0" +dependencies = [ + "bit_field", + "half", + "lebe", + "miniz_oxide 0.8.3", + "rayon-core", + "smallvec", + "zune-inflate", +] + +[[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 = "fdeflate" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e6853b52649d4ac5c0bd02320cddc5ba956bdb407c4b75a2c6b75bf51500f8c" +dependencies = [ + "simd-adler32", +] + +[[package]] +name = "filetime" +version = "0.2.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35c0522e981e68cbfa8c3f978441a5f34b30b96e146b33cd3359176b50fe8586" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "libredox", + "windows-sys 0.59.0", +] + +[[package]] +name = "flate2" +version = "1.0.35" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c936bfdafb507ebbf50b8074c54fa31c5be9a1e7e5f467dd659697041407d07c" +dependencies = [ + "crc32fast", + "miniz_oxide 0.8.3", +] + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "foreign-types" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6f339eb8adc052cd2ca78910fda869aefa38d22d5cb648e6485e4d3fc06f3b1" +dependencies = [ + "foreign-types-shared 0.1.1", +] + +[[package]] +name = "foreign-types" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d737d9aa519fb7b749cbc3b962edcf310a8dd1f4b67c91c4f83975dbdd17d965" +dependencies = [ + "foreign-types-macros", + "foreign-types-shared 0.3.1", +] + +[[package]] +name = "foreign-types-macros" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a5c6c585bc94aaf2c7b51dd4c2ba22680844aba4c687be581871a6f518c5742" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "foreign-types-shared" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" + +[[package]] +name = "foreign-types-shared" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa9a19cbb55df58761df49b23516a86d432839add4af60fc256da840f66ed35b" + +[[package]] +name = "form_urlencoded" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13624c2627564efccf4934284bdd98cbaa14e79b0b5a141218e507b3a823456" +dependencies = [ + "percent-encoding", +] + +[[package]] +name = "freya" +version = "0.3.0-rc.0" +source = "git+https://github.com/marc2332/freya.git?rev=72022bdb3275cdd4b1c007196b8e6de28dbaae31#72022bdb3275cdd4b1c007196b8e6de28dbaae31" +dependencies = [ + "dioxus", + "dioxus-core", + "dioxus-core-macro", + "dioxus-hooks", + "dioxus-signals", + "freya-common", + "freya-components", + "freya-core", + "freya-elements", + "freya-engine", + "freya-hooks", + "freya-node-state", + "freya-renderer", + "tokio", + "torin", +] + +[[package]] +name = "freya-common" +version = "0.3.0-rc.0" +source = "git+https://github.com/marc2332/freya.git?rev=72022bdb3275cdd4b1c007196b8e6de28dbaae31#72022bdb3275cdd4b1c007196b8e6de28dbaae31" +dependencies = [ + "accesskit", + "dioxus-core", + "freya-engine", + "freya-native-core", + "itertools 0.13.0", + "rustc-hash 2.1.0", + "torin", + "uuid", + "winit", +] + +[[package]] +name = "freya-components" +version = "0.3.0-rc.0" +source = "git+https://github.com/marc2332/freya.git?rev=72022bdb3275cdd4b1c007196b8e6de28dbaae31#72022bdb3275cdd4b1c007196b8e6de28dbaae31" +dependencies = [ + "bytes", + "dioxus", + "dioxus-router", + "freya-common", + "freya-core", + "freya-elements", + "freya-engine", + "freya-hooks", + "freya-node-state", + "futures-util", + "open", + "reqwest", + "tokio", + "torin", + "tracing", + "winit", +] + +[[package]] +name = "freya-core" +version = "0.3.0-rc.0" +source = "git+https://github.com/marc2332/freya.git?rev=72022bdb3275cdd4b1c007196b8e6de28dbaae31#72022bdb3275cdd4b1c007196b8e6de28dbaae31" +dependencies = [ + "accesskit", + "accesskit_winit", + "dioxus-core", + "freya-common", + "freya-elements", + "freya-engine", + "freya-native-core", + "freya-node-state", + "itertools 0.13.0", + "rustc-hash 2.1.0", + "smallvec", + "tokio", + "torin", + "tracing", + "uuid", + "winit", +] + +[[package]] +name = "freya-elements" +version = "0.3.0-rc.0" +source = "git+https://github.com/marc2332/freya.git?rev=72022bdb3275cdd4b1c007196b8e6de28dbaae31#72022bdb3275cdd4b1c007196b8e6de28dbaae31" +dependencies = [ + "dioxus-core", + "dioxus-rsx", + "generational-box", + "keyboard-types", + "torin", + "winit", +] + +[[package]] +name = "freya-engine" +version = "0.3.0-rc.0" +source = "git+https://github.com/marc2332/freya.git?rev=72022bdb3275cdd4b1c007196b8e6de28dbaae31#72022bdb3275cdd4b1c007196b8e6de28dbaae31" +dependencies = [ + "bitflags 2.8.0", + "glutin", + "skia-safe", +] + +[[package]] +name = "freya-hooks" +version = "0.3.0-rc.0" +source = "git+https://github.com/marc2332/freya.git?rev=72022bdb3275cdd4b1c007196b8e6de28dbaae31#72022bdb3275cdd4b1c007196b8e6de28dbaae31" +dependencies = [ + "bitflags 2.8.0", + "bytes", + "dioxus-clipboard", + "dioxus-core", + "dioxus-hooks", + "dioxus-signals", + "easer", + "freya-common", + "freya-core", + "freya-elements", + "freya-engine", + "freya-node-state", + "paste", + "ropey", + "tokio", + "torin", + "tracing", + "uuid", + "winit", +] + +[[package]] +name = "freya-native-core" +version = "0.3.0-rc.0" +source = "git+https://github.com/marc2332/freya.git?rev=72022bdb3275cdd4b1c007196b8e6de28dbaae31#72022bdb3275cdd4b1c007196b8e6de28dbaae31" +dependencies = [ + "dioxus-core", + "parking_lot", + "rustc-hash 2.1.0", + "shipyard", + "smallvec", +] + +[[package]] +name = "freya-native-core-macro" +version = "0.3.0-rc.0" +source = "git+https://github.com/marc2332/freya.git?rev=72022bdb3275cdd4b1c007196b8e6de28dbaae31#72022bdb3275cdd4b1c007196b8e6de28dbaae31" +dependencies = [ + "quote", + "syn 2.0.96", +] + +[[package]] +name = "freya-node-state" +version = "0.3.0-rc.0" +source = "git+https://github.com/marc2332/freya.git?rev=72022bdb3275cdd4b1c007196b8e6de28dbaae31#72022bdb3275cdd4b1c007196b8e6de28dbaae31" +dependencies = [ + "accesskit", + "bytes", + "dioxus-core", + "freya-common", + "freya-engine", + "freya-native-core", + "freya-native-core-macro", + "nom", + "rustc-hash 2.1.0", + "serde_json", + "shipyard", + "tokio", + "torin", + "tracing", + "uuid", +] + +[[package]] +name = "freya-renderer" +version = "0.3.0-rc.0" +source = "git+https://github.com/marc2332/freya.git?rev=72022bdb3275cdd4b1c007196b8e6de28dbaae31#72022bdb3275cdd4b1c007196b8e6de28dbaae31" +dependencies = [ + "accesskit", + "accesskit_winit", + "dioxus-core", + "freya-common", + "freya-core", + "freya-elements", + "freya-engine", + "freya-hooks", + "freya-native-core", + "freya-node-state", + "futures-task", + "futures-util", + "gl", + "glutin", + "glutin-winit", + "image", + "itertools 0.13.0", + "pin-utils", + "raw-window-handle", + "tokio", + "torin", + "tracing", + "uuid", + "winit", +] + +[[package]] +name = "futures" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +dependencies = [ + "futures-channel", + "futures-core", + "futures-executor", + "futures-io", + "futures-sink", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-channel" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +dependencies = [ + "futures-core", + "futures-sink", +] + +[[package]] +name = "futures-core" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" + +[[package]] +name = "futures-executor" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +dependencies = [ + "futures-core", + "futures-task", + "futures-util", +] + +[[package]] +name = "futures-io" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" + +[[package]] +name = "futures-lite" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5edaec856126859abb19ed65f39e90fea3a9574b9707f13539acf4abf7eb532" +dependencies = [ + "fastrand", + "futures-core", + "futures-io", + "parking", + "pin-project-lite", +] + +[[package]] +name = "futures-macro" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "futures-sink" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" + +[[package]] +name = "futures-task" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" + +[[package]] +name = "futures-util" +version = "0.3.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +dependencies = [ + "futures-channel", + "futures-core", + "futures-io", + "futures-macro", + "futures-sink", + "futures-task", + "memchr", + "pin-project-lite", + "pin-utils", + "slab", +] + +[[package]] +name = "generational-box" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a673cf4fb0ea6a91aa86c08695756dfe875277a912cdbf33db9a9f62d47ed82b" +dependencies = [ + "parking_lot", + "tracing", +] + +[[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 = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +dependencies = [ + "libc", + "windows-targets 0.48.5", +] + +[[package]] +name = "getrandom" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c4567c8db10ae91089c99af84c68c38da3ec2f087c3f82960bcdbf3656b6f4d7" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "libc", + "wasi", + "wasm-bindgen", +] + +[[package]] +name = "gif" +version = "0.13.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb2d69b19215e18bb912fa30f7ce15846e301408695e44e0ef719f1da9e19f2" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gimli" +version = "0.28.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" + +[[package]] +name = "gl" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a94edab108827d67608095e269cf862e60d920f144a5026d3dbcfd8b877fb404" +dependencies = [ + "gl_generator", +] + +[[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 = "glob" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8d1add55171497b4705a648c6b583acafb01d58050a51727785f0b2c8e0a2b2" + +[[package]] +name = "glutin" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03642b8b0cce622392deb0ee3e88511f75df2daac806102597905c3ea1974848" +dependencies = [ + "bitflags 2.8.0", + "cfg_aliases", + "cgl", + "core-foundation 0.9.4", + "dispatch", + "glutin_egl_sys", + "glutin_glx_sys", + "glutin_wgl_sys", + "libloading", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "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 = "gui" +version = "0.2.0-beta.1" +dependencies = [ + "dirs 6.0.0", + "freya", + "modloader-core", + "open", + "strum", + "sysinfo", + "tokio", + "toml", + "uuid", +] + +[[package]] +name = "h2" +version = "0.4.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccae279728d634d083c00f6099cb58f01cc99c145b84b8be2f6c74618d79922e" +dependencies = [ + "atomic-waker", + "bytes", + "fnv", + "futures-core", + "futures-sink", + "http", + "indexmap 2.7.1", + "slab", + "tokio", + "tokio-util", + "tracing", +] + +[[package]] +name = "half" +version = "2.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dd08c532ae367adf81c312a4580bc67f1d0fe8bc9c460520283f4c0ff277888" +dependencies = [ + "cfg-if 1.0.0", + "crunchy", +] + +[[package]] +name = "hashbrown" +version = "0.12.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" +dependencies = [ + "ahash 0.7.8", +] + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" + +[[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.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fbf6a919d6cf397374f7dfeeea91d974c7c0a7221d0d0f4f20d859d329e53fcc" + +[[package]] +name = "hex" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" + +[[package]] +name = "hmac" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" +dependencies = [ + "digest", +] + +[[package]] +name = "http" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" +dependencies = [ + "bytes", + "fnv", + "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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" +dependencies = [ + "bytes", + "futures-util", + "http", + "http-body", + "pin-project-lite", +] + +[[package]] +name = "httparse" +version = "1.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7d71d3574edd2771538b901e6549113b4006ece66150fb69c0fb6d9a2adae946" + +[[package]] +name = "hyper" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "h2", + "http", + "http-body", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + +[[package]] +name = "hyper-rustls" +version = "0.27.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" +dependencies = [ + "futures-util", + "http", + "hyper", + "hyper-util", + "log", + "rustls", + "rustls-native-certs", + "rustls-pki-types", + "tokio", + "tokio-rustls", + "tower-service", +] + +[[package]] +name = "hyper-timeout" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b90d566bffbce6a75bd8b09a05aa8c2cb1fabb6cb348f8840c9e4c90a0d83b0" +dependencies = [ + "hyper", + "hyper-util", + "pin-project-lite", + "tokio", + "tower-service", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper", + "hyper-util", + "native-tls", + "tokio", + "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http", + "http-body", + "hyper", + "pin-project-lite", + "socket2", + "tokio", + "tower-service", + "tracing", +] + +[[package]] +name = "iana-time-zone" +version = "0.1.61" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "235e081f3925a06703c2d0117ea8b91f042756fd6e7a6e5d901e8ca1a996b220" +dependencies = [ + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "wasm-bindgen", + "windows-core 0.52.0", +] + +[[package]] +name = "iana-time-zone-haiku" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] + +[[package]] +name = "icu_collections" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526" +dependencies = [ + "displaydoc", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_locid" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637" +dependencies = [ + "displaydoc", + "litemap", + "tinystr", + "writeable", + "zerovec", +] + +[[package]] +name = "icu_locid_transform" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_locid_transform_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_locid_transform_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e" + +[[package]] +name = "icu_normalizer" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_normalizer_data", + "icu_properties", + "icu_provider", + "smallvec", + "utf16_iter", + "utf8_iter", + "write16", + "zerovec", +] + +[[package]] +name = "icu_normalizer_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516" + +[[package]] +name = "icu_properties" +version = "1.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5" +dependencies = [ + "displaydoc", + "icu_collections", + "icu_locid_transform", + "icu_properties_data", + "icu_provider", + "tinystr", + "zerovec", +] + +[[package]] +name = "icu_properties_data" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569" + +[[package]] +name = "icu_provider" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9" +dependencies = [ + "displaydoc", + "icu_locid", + "icu_provider_macros", + "stable_deref_trait", + "tinystr", + "writeable", + "yoke", + "zerofrom", + "zerovec", +] + +[[package]] +name = "icu_provider_macros" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[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.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "686f825264d630750a544639377bae737628043f20d38bbc029e8f29ea968a7e" +dependencies = [ + "idna_adapter", + "smallvec", + "utf8_iter", +] + +[[package]] +name = "idna_adapter" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71" +dependencies = [ + "icu_normalizer", + "icu_properties", +] + +[[package]] +name = "image" +version = "0.25.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd6f44aed642f18953a158afeb30206f4d50da59fbc66ecb53c66488de73563b" +dependencies = [ + "bytemuck", + "byteorder-lite", + "color_quant", + "exr", + "gif", + "image-webp", + "num-traits", + "png", + "qoi", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", +] + +[[package]] +name = "image-webp" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b77d01e822461baa8409e156015a1d91735549f0f2c17691bd2d996bef238f7f" +dependencies = [ + "byteorder-lite", + "quick-error", +] + +[[package]] +name = "imgref" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d0263a3d970d5c054ed9312c0057b4f3bde9c0b33836d3637361d4a9e6e7a408" + +[[package]] +name = "immutable-chunkmap" +version = "2.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12f97096f508d54f8f8ab8957862eee2ccd628847b6217af1a335e1c44dee578" +dependencies = [ + "arrayvec", +] + +[[package]] +name = "indenter" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce23b50ad8242c51a442f3ff322d56b02f08852c77e4c0b4d3fd684abc89c683" + +[[package]] +name = "indexmap" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" +dependencies = [ + "autocfg", + "hashbrown 0.12.3", + "serde", +] + +[[package]] +name = "indexmap" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8c9c992b02b5b4c94ea26e32fe5bccb7aa7d9f390ab5c1221ff895bc7ea8b652" +dependencies = [ + "equivalent", + "hashbrown 0.15.2", + "serde", +] + +[[package]] +name = "inout" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0c10553d664a4d0bcff9f4215d0aac67a639cc68ef660840afe309b807bc9f5" +dependencies = [ + "generic-array", +] + +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[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.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0f0a572e8ffe56e2ff4f769f32ffe919282c3916799f8b68688b6030063bea" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "is-docker" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "928bae27f42bc99b60d9ac7334e3a21d10ad8f1835a4e12ec3ec0464765ed1b3" +dependencies = [ + "once_cell", +] + +[[package]] +name = "is-wsl" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "173609498df190136aa7dea1a91db051746d339e18476eed5ca40521f02d7aa5" +dependencies = [ + "is-docker", + "once_cell", +] + +[[package]] +name = "is_executable" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4a1b5bad6f9072935961dfbf1cced2f3d129963d091b6f69f007fe04e758ae2" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "is_terminal_polyfill" +version = "1.70.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7943c866cc5cd64cbc25b2e01621d07fa8eb2a1a23160ee81ce38704e97b8ecf" + +[[package]] +name = "itertools" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1c173a5686ce8bfa551b3563d0c2170bf24ca44da99c7ca4bfdab5418c3fe57" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" +dependencies = [ + "either", +] + +[[package]] +name = "itertools" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +dependencies = [ + "either", +] + +[[package]] +name = "itoa" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d75a2a4b1b190afb6f5425f10f6a8f959d2ea0b9c2b1d79553551850539e4674" + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if 1.0.0", + "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.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d1dbcbbeb6a7fec7e059840aa538bd62aaccf972c7346c4d9d2059312853d0" +dependencies = [ + "libc", +] + +[[package]] +name = "jpeg-decoder" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" + +[[package]] +name = "js-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + +[[package]] +name = "jsonwebtoken" +version = "9.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9ae10193d25051e74945f1ea2d0b42e03cc3b890f7e4cc5faa44997d808193f" +dependencies = [ + "base64 0.21.7", + "js-sys", + "pem", + "ring", + "serde", + "serde_json", + "simple_asn1", +] + +[[package]] +name = "kernel32-sys" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7507624b29483431c0ba2d82aece8ca6cdba9382bff4ddd0f7490560c056098d" +dependencies = [ + "winapi 0.2.8", + "winapi-build", +] + +[[package]] +name = "keyboard-types" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" +dependencies = [ + "bitflags 2.8.0", + "serde", + "unicode-segmentation", +] + +[[package]] +name = "khronos_api" +version = "3.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2db585e1d738fc771bf08a151420d3ed193d9d895a36df7f6f8a9456b911ddc" + +[[package]] +name = "lazy-bytes-cast" +version = "5.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "10257499f089cd156ad82d0a9cd57d9501fa2c989068992a97eb3c27836f206b" + +[[package]] +name = "lazy-js-bundle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e49596223b9d9d4947a14a25c142a6e7d8ab3f27eb3ade269d238bb8b5c267e2" + +[[package]] +name = "lazy_static" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" + +[[package]] +name = "lebe" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8" + +[[package]] +name = "libc" +version = "0.2.169" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" + +[[package]] +name = "libfuzzer-sys" +version = "0.4.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b9569d2f74e257076d8c6bfa73fb505b46b851e51ddaecc825944aa3bed17fa" +dependencies = [ + "arbitrary", + "cc", +] + +[[package]] +name = "libloading" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc2f4eb4bc735547cfed7c0a4922cbd04a4655978c09b54f1f7b228750664c34" +dependencies = [ + "cfg-if 1.0.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "libredox" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" +dependencies = [ + "bitflags 2.8.0", + "libc", + "redox_syscall 0.5.8", +] + +[[package]] +name = "libudis86-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "139bbf9ddb1bfc90c1ac64dd2923d9c957cd433cee7315c018125d72ab08a6b0" +dependencies = [ + "cc", + "libc", +] + +[[package]] +name = "linux-raw-sys" +version = "0.4.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" + +[[package]] +name = "litemap" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ee93343901ab17bd981295f2cf0026d4ad018c7c31ba84549a4ddbb47a45104" + +[[package]] +name = "litrs" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4ce301924b7887e9d637144fdade93f9dfff9b60981d4ac161db09720d39aa5" + +[[package]] +name = "lock_api" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17" +dependencies = [ + "autocfg", + "scopeguard", +] + +[[package]] +name = "lockfree-object-pool" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9374ef4228402d4b7e403e5838cb880d9ee663314b0a900d5a6aabf0c213552e" + +[[package]] +name = "log" +version = "0.4.25" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" + +[[package]] +name = "longest-increasing-subsequence" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3bd0dd2cd90571056fdb71f6275fada10131182f84899f4b2a916e565d81d86" + +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + +[[package]] +name = "lzma-rs" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e" +dependencies = [ + "byteorder", + "crc", +] + +[[package]] +name = "mach2" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19b955cdeb2a02b9117f121ce63aa52d08ade45de53e48fe6a38b39c10f6f709" +dependencies = [ + "libc", +] + +[[package]] +name = "malloc_buf" +version = "0.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "62bb907fe88d54d8d9ce32a3cceab4218ed2f6b7d35617cafe9adf84e43919cb" +dependencies = [ + "libc", +] + +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if 1.0.0", + "rayon", +] + +[[package]] +name = "memchr" +version = "2.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" + +[[package]] +name = "memmap2" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd3f7eed9d3848f8b98834af67102b720745c4ec028fcd0aa0239277e7de374f" +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 = "mime" +version = "0.3.17" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" + +[[package]] +name = "minimal-lexical" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" + +[[package]] +name = "miniz_oxide" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8a240ddb74feaf34a79a7add65a741f3167852fba007066dcac1ca548d89c08" +dependencies = [ + "adler", +] + +[[package]] +name = "miniz_oxide" +version = "0.8.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8402cab7aefae129c6977bb0ff1b8fd9a04eb5b51efc50a70bea51cda0c7924" +dependencies = [ + "adler2", + "simd-adler32", +] + +[[package]] +name = "mio" +version = "1.0.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd" +dependencies = [ + "libc", + "wasi", + "windows-sys 0.52.0", +] + +[[package]] +name = "mmap-fixed-fixed" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0681853891801e4763dc252e843672faf32bcfee27a0aa3b19733902af450acc" +dependencies = [ + "libc", + "winapi 0.3.9", +] + +[[package]] +name = "modloader-core" +version = "0.2.0-beta.1" +dependencies = [ + "dialog", + "dirs 6.0.0", + "electron-hook", + "flate2", + "octocrab", + "serde", + "serde_json", + "strum", + "sysinfo", + "tar", + "toml", + "ureq", + "zip", +] + +[[package]] +name = "native-tls" +version = "0.2.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dab59f8e050d5df8e4dd87d9206fb6f65a483e20ac9fda365ade4fab353196c" +dependencies = [ + "libc", + "log", + "openssl", + "openssl-probe", + "openssl-sys", + "schannel", + "security-framework 2.11.1", + "security-framework-sys", + "tempfile", +] + +[[package]] +name = "ndk" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" +dependencies = [ + "bitflags 2.8.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 = "new_debug_unreachable" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "650eef8c711430f1a879fdd01d4745a7deea475becfb90269c06775983bbf086" + +[[package]] +name = "nix" +version = "0.29.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" +dependencies = [ + "bitflags 2.8.0", + "cfg-if 1.0.0", + "cfg_aliases", + "libc", + "memoffset", +] + +[[package]] +name = "nom" +version = "7.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" +dependencies = [ + "memchr", + "minimal-lexical", +] + +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + +[[package]] +name = "ntapi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +dependencies = [ + "winapi 0.3.9", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-conv" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d515d32fb182ee37cda2ccdcb92950d6a3c2893aa280e540671c2cd0f3b1d9" + +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "num_enum" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4e613fc340b2220f734a8595782c551f1250e969d87d3be1ae0579e8d4065179" +dependencies = [ + "num_enum_derive", +] + +[[package]] +name = "num_enum_derive" +version = "0.7.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[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-foundation" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1add1b659e36c9607c7aab864a76c7a4c2760cd0cd2e120f3fb8b952c7e22bf9" +dependencies = [ + "block", + "objc", + "objc_id", +] + +[[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-app-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" +dependencies = [ + "bitflags 2.8.0", + "block2", + "libc", + "objc2", + "objc2-core-data", + "objc2-core-image", + "objc2-foundation", + "objc2-quartz-core", +] + +[[package]] +name = "objc2-cloud-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +dependencies = [ + "bitflags 2.8.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc2-contacts" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-data" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" +dependencies = [ + "bitflags 2.8.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-core-image" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" +dependencies = [ + "block2", + "objc2", + "objc2-foundation", + "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", + "objc2", + "objc2-contacts", + "objc2-foundation", +] + +[[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.8.0", + "block2", + "dispatch", + "libc", + "objc2", +] + +[[package]] +name = "objc2-link-presentation" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +dependencies = [ + "block2", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + +[[package]] +name = "objc2-metal" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" +dependencies = [ + "bitflags 2.8.0", + "block2", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-quartz-core" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" +dependencies = [ + "bitflags 2.8.0", + "block2", + "objc2", + "objc2-foundation", + "objc2-metal", +] + +[[package]] +name = "objc2-symbols" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +dependencies = [ + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-ui-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +dependencies = [ + "bitflags 2.8.0", + "block2", + "objc2", + "objc2-cloud-kit", + "objc2-core-data", + "objc2-core-image", + "objc2-core-location", + "objc2-foundation", + "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", + "objc2", + "objc2-foundation", +] + +[[package]] +name = "objc2-user-notifications" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" +dependencies = [ + "bitflags 2.8.0", + "block2", + "objc2", + "objc2-core-location", + "objc2-foundation", +] + +[[package]] +name = "objc_id" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c92d4ddb4bd7b50d730c215ff871754d0da6b2178849f8a2a2ab69712d0c073b" +dependencies = [ + "objc", +] + +[[package]] +name = "object" +version = "0.32.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" +dependencies = [ + "memchr", +] + +[[package]] +name = "octocrab" +version = "0.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27527d68322f4c603319f7958973db8f9fa4be62c0e3fafe084f5562cf6353df" +dependencies = [ + "arc-swap", + "async-trait", + "base64 0.22.1", + "bytes", + "cfg-if 1.0.0", + "chrono", + "either", + "futures", + "futures-util", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-timeout", + "hyper-util", + "jsonwebtoken", + "once_cell", + "percent-encoding", + "pin-project", + "secrecy", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "snafu", + "tokio", + "tower", + "tower-http", + "tracing", + "url", + "web-time", +] + +[[package]] +name = "once_cell" +version = "1.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1261fe7e33c73b354eab43b1273a57c8f967d0391e80353e51f764ac02cf6775" + +[[package]] +name = "open" +version = "5.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2483562e62ea94312f3576a7aca397306df7990b8d89033e18766744377ef95" +dependencies = [ + "is-wsl", + "libc", + "pathdiff", +] + +[[package]] +name = "openssl" +version = "0.10.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f5e534d133a060a3c19daec1eb3e98ec6f4685978834f2dbadfe2ec215bab64e" +dependencies = [ + "bitflags 2.8.0", + "cfg-if 1.0.0", + "foreign-types 0.3.2", + "libc", + "once_cell", + "openssl-macros", + "openssl-sys", +] + +[[package]] +name = "openssl-macros" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "openssl-probe" +version = "0.1.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d05e27ee213611ffe7d6348b942e8f942b37114c00cc03cec254295a4a17852e" + +[[package]] +name = "openssl-sys" +version = "0.9.104" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "45abf306cbf99debc8195b66b7346498d7b10c210de50418b5ccd7ceba08c741" +dependencies = [ + "cc", + "libc", + "pkg-config", + "vcpkg", +] + +[[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.48" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba0b26cec2e24f08ed8bb31519a9333140a6599b867dac464bb150bdb796fd43" +dependencies = [ + "libredox", +] + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "22ec719bbf3b2a81c109a4e20b1f129b5566b7dce654bc3872f6a05abf82b2c4" +dependencies = [ + "ttf-parser", +] + +[[package]] +name = "owo-colors" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1b04fb49957986fdce4d6ee7a65027d55d4b6d2265e5848bbb507b58ccfdb6f" + +[[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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27" +dependencies = [ + "lock_api", + "parking_lot_core", +] + +[[package]] +name = "parking_lot_core" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8" +dependencies = [ + "cfg-if 1.0.0", + "libc", + "redox_syscall 0.5.8", + "smallvec", + "windows-targets 0.52.6", +] + +[[package]] +name = "paste" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" + +[[package]] +name = "pathdiff" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3" + +[[package]] +name = "pbkdf2" +version = "0.12.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2" +dependencies = [ + "digest", + "hmac", +] + +[[package]] +name = "pem" +version = "3.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e459365e590736a54c3fa561947c84837534b8e9af6fc5bf781307e82658fae" +dependencies = [ + "base64 0.22.1", + "serde", +] + +[[package]] +name = "percent-encoding" +version = "2.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" + +[[package]] +name = "pin-project" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e2ec53ad785f4d35dac0adea7f7dc6f1bb277ad84a680c7afefeae05d1f5916" +dependencies = [ + "pin-project-internal", +] + +[[package]] +name = "pin-project-internal" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d56a66c0c55993aa927429d0f8a0abfd74f084e4d9c192cffed01e418d83eefb" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[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.31" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "953ec861398dccce10c670dfeaf3ec4911ca479e9c02154b3a215178c5f566f2" + +[[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 0.8.3", +] + +[[package]] +name = "polling" +version = "3.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a604568c3202727d1507653cb121dbd627a58684eb09a820fd746bee38b4442f" +dependencies = [ + "cfg-if 1.0.0", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix", + "tracing", + "windows-sys 0.59.0", +] + +[[package]] +name = "pori" +version = "0.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4a63d338dec139f56dacc692ca63ad35a6be6a797442479b55acd611d79e906" +dependencies = [ + "nom", +] + +[[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.20" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" +dependencies = [ + "zerocopy", +] + +[[package]] +name = "prettyplease" +version = "0.2.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6924ced06e1f7dfe3fa48d57b9f74f55d8915f5036121bef647ef4b204895fac" +dependencies = [ + "proc-macro2", + "syn 2.0.96", +] + +[[package]] +name = "proc-macro-crate" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ecf48c7ca261d60b74ab1a7b20da18bede46776b2e55535cb958eb595c5fa7b" +dependencies = [ + "toml_edit", +] + +[[package]] +name = "proc-macro2" +version = "1.0.93" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60946a68e5f9d28b0dc1c21bb8a97ee7d018a8b322fa57838ba31cc878e22d99" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "proc-macro2-diagnostics" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af066a9c399a26e020ada66a034357a868728e72cd426f3adcd35f80d88d88c8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", + "version_check", +] + +[[package]] +name = "profiling" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "afbdc74edc00b6f6a218ca6a5364d6226a259d4b8ea1af4a0ea063f27e179f4d" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a65f2e60fbf1063868558d69c6beacf412dc755f9fc020f514b7955fc914fe30" +dependencies = [ + "quote", + "syn 2.0.96", +] + +[[package]] +name = "qoi" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" +dependencies = [ + "bytemuck", +] + +[[package]] +name = "quick-error" +version = "2.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" + +[[package]] +name = "quick-xml" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eff6510e86862b57b210fd8cbe8ed3f0d7d600b9c2863cd4549a2e033c66e956" +dependencies = [ + "memchr", + "serde", +] + +[[package]] +name = "quick-xml" +version = "0.36.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7649a7b4df05aed9ea7ec6f628c67c9953a43869b8bc50929569b2999d443fe" +dependencies = [ + "memchr", +] + +[[package]] +name = "quote" +version = "1.0.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0e4dccaaaf89514f546c693ddc140f729f958c247918a13380cccc6078391acc" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "rand" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +dependencies = [ + "libc", + "rand_chacha", + "rand_core", +] + +[[package]] +name = "rand_chacha" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" +dependencies = [ + "ppv-lite86", + "rand_core", +] + +[[package]] +name = "rand_core" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" +dependencies = [ + "getrandom", +] + +[[package]] +name = "rav1e" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cd87ce80a7665b1cce111f8a16c1f3929f6547ce91ade6addf4ec86a8dda5ce9" +dependencies = [ + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av1-grain", + "bitstream-io", + "built", + "cfg-if 1.0.0", + "interpolate_name", + "itertools 0.12.1", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "once_cell", + "paste", + "profiling", + "rand", + "rand_chacha", + "simd_helpers", + "system-deps", + "thiserror 1.0.69", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.11.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2413fd96bd0ea5cdeeb37eaf446a22e6ed7b981d792828721e74ded1980a45c6" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + +[[package]] +name = "raw-window-handle" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20675572f6f24e9e76ef639bc5552774ed45f1c30e2951e1e99c59888861c539" + +[[package]] +name = "rayon" +version = "1.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b418a60154510ca1a002a752ca9714984e21e4241e804d32555251faf8b78ffa" +dependencies = [ + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1465873a3dfdaa8ae7cb14b4383657caab0b3e8a0aa9ae8e04b044854c8dfce2" +dependencies = [ + "crossbeam-deque", + "crossbeam-utils", +] + +[[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.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03a862b389f93e68874fbf580b9de08dd02facb9a788ebadaf4a3fd33cf58834" +dependencies = [ + "bitflags 2.8.0", +] + +[[package]] +name = "redox_users" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba009ff324d1fc1b900bd1fdb31564febe58a8ccc8a6fdbb93b543d33b13ca43" +dependencies = [ + "getrandom", + "libredox", + "thiserror 1.0.69", +] + +[[package]] +name = "redox_users" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +dependencies = [ + "getrandom", + "libredox", + "thiserror 2.0.11", +] + +[[package]] +name = "regex" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b544ef1b4eac5dc2db33ea63606ae9ffcfac26c1416a2806ae0bf5f56b201191" +dependencies = [ + "aho-corasick", + "memchr", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "regex-automata" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "809e8dc61f6de73b46c85f4c96486310fe304c434cfa43669d7b40f711150908" +dependencies = [ + "aho-corasick", + "memchr", + "regex-syntax", +] + +[[package]] +name = "regex-syntax" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" + +[[package]] +name = "region" +version = "3.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e6b6ebd13bc009aef9cd476c1310d49ac354d36e240cf1bd753290f3dc7199a7" +dependencies = [ + "bitflags 1.3.2", + "libc", + "mach2", + "windows-sys 0.52.0", +] + +[[package]] +name = "reqwest" +version = "0.12.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" +dependencies = [ + "base64 0.22.1", + "bytes", + "encoding_rs", + "futures-core", + "futures-util", + "h2", + "http", + "http-body", + "http-body-util", + "hyper", + "hyper-rustls", + "hyper-tls", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper", + "system-configuration", + "tokio", + "tokio-native-tls", + "tower", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-registry", +] + +[[package]] +name = "retour" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9af44d40e2400b44d491bfaf8eae111b09f23ac4de6e92728e79d93e699c527" +dependencies = [ + "cfg-if 1.0.0", + "generic-array", + "libc", + "libudis86-sys", + "mmap-fixed-fixed", + "once_cell", + "region", + "slice-pool2", +] + +[[package]] +name = "rgb" +version = "0.8.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "57397d16646700483b67d2dd6511d79318f9d057fdbd21a4066aeac8b41d310a" + +[[package]] +name = "ring" +version = "0.17.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d" +dependencies = [ + "cc", + "cfg-if 1.0.0", + "getrandom", + "libc", + "spin", + "untrusted", + "windows-sys 0.52.0", +] + +[[package]] +name = "ropey" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93411e420bcd1a75ddd1dc3caf18c23155eda2c090631a85af21ba19e97093b5" +dependencies = [ + "smallvec", + "str_indices", +] + +[[package]] +name = "rpassword" +version = "2.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d37473170aedbe66ffa3ad3726939ba677d83c646ad4fd99e5b4bc38712f45ec" +dependencies = [ + "kernel32-sys", + "libc", + "winapi 0.2.8", +] + +[[package]] +name = "rustc-demangle" +version = "0.1.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" + +[[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.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7fb8039b3032c191086b10f11f319a6e99e1e82889c5cc6046f515c9db1d497" + +[[package]] +name = "rustix" +version = "0.38.44" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" +dependencies = [ + "bitflags 2.8.0", + "errno", + "libc", + "linux-raw-sys", + "windows-sys 0.59.0", +] + +[[package]] +name = "rustls" +version = "0.23.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f287924602bf649d949c63dc8ac8b235fa5387d394020705b80c4eb597ce5b8" +dependencies = [ + "log", + "once_cell", + "ring", + "rustls-pki-types", + "rustls-webpki", + "subtle", + "zeroize", +] + +[[package]] +name = "rustls-native-certs" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fcff2dd52b58a8d98a70243663a0d234c4e2b79235637849d15913394a247d3" +dependencies = [ + "openssl-probe", + "rustls-pki-types", + "schannel", + "security-framework 3.2.0", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "917ce264624a4b4db1c364dcc35bfca9ded014d0a958cd47ad3e960e988ea51c" + +[[package]] +name = "rustls-webpki" +version = "0.102.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64ca1bc8749bd4cf37b5ce386cc146580777b4e8572c7b97baf22c83f444bee9" +dependencies = [ + "ring", + "rustls-pki-types", + "untrusted", +] + +[[package]] +name = "rustversion" +version = "1.0.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c45b9784283f1b2e7fb61b42047c2fd678ef0960d4f6f1eba131594cc369d4" + +[[package]] +name = "ryu" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f3cb5ba0dc43242ce17de99c180e96db90b235b8a9fdc9543c96d2209116bd9f" + +[[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.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f29ebaa345f945cec9fbbc532eb307f0fdad8161f281b6369539c8d84876b3d" +dependencies = [ + "windows-sys 0.59.0", +] + +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + +[[package]] +name = "scopeguard" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" + +[[package]] +name = "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", + "tiny-skia", +] + +[[package]] +name = "secrecy" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e891af845473308773346dc847b2c23ee78fe442e0472ac50e22a18a93d3ae5a" +dependencies = [ + "zeroize", +] + +[[package]] +name = "security-framework" +version = "2.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" +dependencies = [ + "bitflags 2.8.0", + "core-foundation 0.9.4", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework" +version = "3.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271720403f46ca04f7ba6f55d438f8bd878d6b8ca0a1046e8228c4145bcbb316" +dependencies = [ + "bitflags 2.8.0", + "core-foundation 0.10.0", + "core-foundation-sys", + "libc", + "security-framework-sys", +] + +[[package]] +name = "security-framework-sys" +version = "2.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "49db231d56a190491cb4aeda9527f1ad45345af50b0851622a7adb8c03b01c32" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "serde" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.217" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "serde_json" +version = "1.0.137" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" +dependencies = [ + "itoa", + "memchr", + "ryu", + "serde", +] + +[[package]] +name = "serde_path_to_error" +version = "0.1.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af99884400da37c88f5e9146b7f1fd0fbcae8f6eec4e9da38b67d05486f814a6" +dependencies = [ + "itoa", + "serde", +] + +[[package]] +name = "serde_repr" +version = "0.1.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "serde_spanned" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "87607cb1398ed59d48732e575a4c28a7a8ebf2454b964fe3f224f2afc07909e1" +dependencies = [ + "serde", +] + +[[package]] +name = "serde_urlencoded" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" +dependencies = [ + "form_urlencoded", + "itoa", + "ryu", + "serde", +] + +[[package]] +name = "serde_with" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6b6f7f2fcb69f747921f79f3926bd1e203fce4fef62c268dd3abfb6d86029aa" +dependencies = [ + "base64 0.22.1", + "chrono", + "hex", + "indexmap 1.9.3", + "indexmap 2.7.1", + "serde", + "serde_derive", + "serde_json", + "serde_with_macros", + "time", +] + +[[package]] +name = "serde_with_macros" +version = "3.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d00caa5193a3c8362ac2b73be6b9e768aa5a4b2f721d8f4b339600c3cb51f8e" +dependencies = [ + "darling", + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if 1.0.0", + "cpufeatures", + "digest", +] + +[[package]] +name = "sha2" +version = "0.10.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +dependencies = [ + "cfg-if 1.0.0", + "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 = "shipyard" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ace89f5a47029be049c7f47d166cd2c7a30d74e127f4baa2d8885e5d3b486035" +dependencies = [ + "hashbrown 0.12.3", + "lock_api", + "rayon", + "shipyard_proc", +] + +[[package]] +name = "shipyard_proc" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3eb847f4b9582e468198b5cfb5731b65cc67fe5e535acc9cbf3c11703d15f08c" +dependencies = [ + "proc-macro2", + "quote", + "syn 1.0.109", +] + +[[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.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1" +dependencies = [ + "libc", +] + +[[package]] +name = "simd-adler32" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" + +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + +[[package]] +name = "simple_asn1" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "297f631f50729c8c99b84667867963997ec0b50f32b2a7dbcab828ef0541e8bb" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror 2.0.11", + "time", +] + +[[package]] +name = "skia-bindings" +version = "0.80.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "179ebe08aee2634b22ef4d2a1a9370aaa39dfe619a702fc0793d491a26c05cbb" +dependencies = [ + "bindgen", + "cc", + "flate2", + "heck", + "lazy_static", + "regex", + "serde_json", + "tar", + "toml", +] + +[[package]] +name = "skia-safe" +version = "0.80.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41f1a96bec5198699d49e9c6a46aea27033958521c971d9186ae015a0dbecb7b" +dependencies = [ + "base64 0.22.1", + "bitflags 2.8.0", + "lazy_static", + "percent-encoding", + "skia-bindings", + "skia-svg-macros", +] + +[[package]] +name = "skia-svg-macros" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "044dd2233c9717a74f75197f3e7f0a966db2127c0ffb5e05013b480a9b75b2c7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "slab" +version = "0.4.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" +dependencies = [ + "autocfg", +] + +[[package]] +name = "slice-pool2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a3d689654af89bdfeba29a914ab6ac0236d382eb3b764f7454dde052f2821f8" + +[[package]] +name = "slotmap" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbff4acf519f630b3a3ddcfaea6c06b42174d9a44bc70c620e9ed1649d58b82a" +dependencies = [ + "serde", + "version_check", +] + +[[package]] +name = "smallvec" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c5e1a9a646d36c3599cd173a41282daf47c44583ad367b8e6837255952e5c67" + +[[package]] +name = "smithay-client-toolkit" +version = "0.19.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3457dea1f0eb631b4034d61d4d8c32074caa6cd1ab2d59f2327bd8461e2c0016" +dependencies = [ + "bitflags 2.8.0", + "calloop", + "calloop-wayland-source", + "cursor-icon", + "libc", + "log", + "memmap2", + "rustix", + "thiserror 1.0.69", + "wayland-backend", + "wayland-client", + "wayland-csd-frame", + "wayland-cursor", + "wayland-protocols", + "wayland-protocols-wlr", + "wayland-scanner", + "xkeysym", +] + +[[package]] +name = "smithay-clipboard" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc8216eec463674a0e90f29e0ae41a4db573ec5b56b1c6c1c71615d249b6d846" +dependencies = [ + "libc", + "smithay-client-toolkit", + "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 = "snafu" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "223891c85e2a29c3fe8fb900c1fae5e69c2e42415e3177752e8718475efa5019" +dependencies = [ + "snafu-derive", +] + +[[package]] +name = "snafu-derive" +version = "0.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "03c3c6b7927ffe7ecaa769ee0e3994da3b8cafc8f444578982c83ecb161af917" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "socket2" +version = "0.5.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c970269d99b64e60ec3bd6ad27270092a5394c4e309314b18ae3fe575695fbe8" +dependencies = [ + "libc", + "windows-sys 0.52.0", +] + +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" + +[[package]] +name = "stable_deref_trait" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3" + +[[package]] +name = "static_assertions" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "str_indices" +version = "0.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d08889ec5408683408db66ad89e0e1f93dff55c73a4ccc71c427d5b277ee47e6" + +[[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 = "strum" +version = "0.26.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +dependencies = [ + "strum_macros", +] + +[[package]] +name = "strum_macros" +version = "0.26.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +dependencies = [ + "heck", + "proc-macro2", + "quote", + "rustversion", + "syn 2.0.96", +] + +[[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", + "quote", + "unicode-ident", +] + +[[package]] +name = "syn" +version = "2.0.96" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d5d0adab1ae378d7f53bdebc67a39f1f151407ef230f0ce2883572f5d8985c80" +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.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "sysinfo" +version = "0.33.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fc858248ea01b66f19d8e8a6d55f41deaf91e9d495246fd01368d99935c6c01" +dependencies = [ + "core-foundation-sys", + "libc", + "memchr", + "ntapi", + "rayon", + "windows 0.57.0", +] + +[[package]] +name = "system-configuration" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" +dependencies = [ + "bitflags 2.8.0", + "core-foundation 0.9.4", + "system-configuration-sys", +] + +[[package]] +name = "system-configuration-sys" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e1d1b10ced5ca923a1fcb8d03e96b8d3268065d724548c0211415ff6ac6bac4" +dependencies = [ + "core-foundation-sys", + "libc", +] + +[[package]] +name = "system-deps" +version = "6.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3e535eb8dded36d55ec13eddacd30dec501792ff23a0b1682c38601b8cf2349" +dependencies = [ + "cfg-expr", + "heck", + "pkg-config", + "toml", + "version-compare", +] + +[[package]] +name = "tar" +version = "0.4.43" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c65998313f8e17d0d553d28f91a0df93e4dbbbf770279c7bc21ca0f09ea1a1f6" +dependencies = [ + "filetime", + "libc", + "xattr", +] + +[[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.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8a559c81686f576e8cd0290cd2a24a2a9ad80c98b3478856500fcbd7acd704" +dependencies = [ + "cfg-if 1.0.0", + "fastrand", + "getrandom", + "once_cell", + "rustix", + "windows-sys 0.59.0", +] + +[[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.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc" +dependencies = [ + "thiserror-impl 2.0.11", +] + +[[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.96", +] + +[[package]] +name = "thiserror-impl" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "thread_local" +version = "1.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b9ef9bad013ada3808854ceac7b46812a6465ba368859a37e2100283d2d719c" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", +] + +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + +[[package]] +name = "time" +version = "0.3.37" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +dependencies = [ + "deranged", + "itoa", + "num-conv", + "powerfmt", + "serde", + "time-core", + "time-macros", +] + +[[package]] +name = "time-core" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" + +[[package]] +name = "time-macros" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +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 1.0.0", + "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.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f" +dependencies = [ + "displaydoc", + "zerovec", +] + +[[package]] +name = "tokio" +version = "1.43.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" +dependencies = [ + "backtrace", + "bytes", + "libc", + "mio", + "pin-project-lite", + "socket2", + "tokio-macros", + "windows-sys 0.52.0", +] + +[[package]] +name = "tokio-macros" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "tokio-native-tls" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2" +dependencies = [ + "native-tls", + "tokio", +] + +[[package]] +name = "tokio-rustls" +version = "0.26.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f6d0975eaace0cf0fcadee4e4aaa5da15b5c079146f2cffb67c113be122bf37" +dependencies = [ + "rustls", + "tokio", +] + +[[package]] +name = "tokio-util" +version = "0.7.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7fcaa8d55a2bdd6b83ace262b016eca0d79ee02818c5c1bcdf0305114081078" +dependencies = [ + "bytes", + "futures-core", + "futures-sink", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "toml" +version = "0.8.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit", +] + +[[package]] +name = "toml_datetime" +version = "0.6.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" +dependencies = [ + "serde", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" +dependencies = [ + "indexmap 2.7.1", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + +[[package]] +name = "torin" +version = "0.3.0-rc.0" +source = "git+https://github.com/marc2332/freya.git?rev=72022bdb3275cdd4b1c007196b8e6de28dbaae31#72022bdb3275cdd4b1c007196b8e6de28dbaae31" +dependencies = [ + "euclid", + "freya-native-core", + "rustc-hash 2.1.0", + "tracing", +] + +[[package]] +name = "tower" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" +dependencies = [ + "futures-core", + "futures-util", + "pin-project-lite", + "sync_wrapper", + "tokio", + "tokio-util", + "tower-layer", + "tower-service", + "tracing", +] + +[[package]] +name = "tower-http" +version = "0.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "403fa3b783d4b626a8ad51d766ab03cb6d2dbfc46b1c5d4448395e6628dc9697" +dependencies = [ + "bitflags 2.8.0", + "bytes", + "futures-util", + "http", + "http-body", + "iri-string", + "pin-project-lite", + "tower", + "tower-layer", + "tower-service", + "tracing", +] + +[[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.41" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +dependencies = [ + "log", + "pin-project-lite", + "tracing-attributes", + "tracing-core", +] + +[[package]] +name = "tracing-attributes" +version = "0.1.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "395ae124c09f9e6918a2310af6038fba074bcf474ac352496d5910dd59a2226d" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "tracing-core" +version = "0.1.33" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e672c95779cf947c5311f83787af4fa8fffd12fb27e4993211a84bdfd9610f9c" +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-subscriber" +version = "0.3.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8189decb5ac0fa7bc8b96b7cb9b2701d60d48805aca84a238004d665fcc4008" +dependencies = [ + "sharded-slab", + "thread_local", + "tracing-core", +] + +[[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 = "typenum" +version = "1.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" + +[[package]] +name = "uds_windows" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "89daebc3e6fd160ac4aa9fc8b3bf71e1f74fbf92367ae71fb83a037e8bf164b9" +dependencies = [ + "memoffset", + "tempfile", + "winapi 0.3.9", +] + +[[package]] +name = "unicode-ident" +version = "1.0.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11cd88e12b17c6494200a9c1b683a04fcac9573ed74cd1b62aeb2727c5592243" + +[[package]] +name = "unicode-segmentation" +version = "1.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" + +[[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 = "ureq" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b35e5241ee2cad3940e69c78b28b3600bec9c57146bce26b3eb888869b987d4" +dependencies = [ + "base64 0.22.1", + "cc", + "cookie_store", + "flate2", + "log", + "once_cell", + "percent-encoding", + "rustls", + "rustls-pemfile", + "rustls-pki-types", + "serde", + "serde_json", + "ureq-proto", + "utf-8", + "webpki-roots", +] + +[[package]] +name = "ureq-proto" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "42506c1d9fc65bcf2508cb8f9b5e7d21f88e71567fbb7595ca5e5bd5218ca7db" +dependencies = [ + "base64 0.22.1", + "http", + "httparse", + "log", +] + +[[package]] +name = "url" +version = "2.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32f8b686cadd1473f4bd0117a5d28d36b1ade384ea9b5069a1c40aefed7fda60" +dependencies = [ + "form_urlencoded", + "idna", + "percent-encoding", + "serde", +] + +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + +[[package]] +name = "utf-8" +version = "0.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09cc8ee72d2a9becf2f2febe0205bbed8fc6615b7cb429ad062dc7b7ddd036a9" + +[[package]] +name = "utf16_iter" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246" + +[[package]] +name = "utf8_iter" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" + +[[package]] +name = "utf8parse" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" + +[[package]] +name = "uuid" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" +dependencies = [ + "getrandom", +] + +[[package]] +name = "v_frame" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f32aaa24bacd11e488aa9ba66369c7cd514885742c9fe08cfe85884db3e92b" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + +[[package]] +name = "valuable" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba73ea9cf16a25df0c8caa16c51acb937d5712a8429db78a3ee29d5dcacd3a65" + +[[package]] +name = "vcpkg" +version = "0.2.15" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" + +[[package]] +name = "version-compare" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "852e951cb7832cb45cb1169900d19760cfa39b82bc0ea9c0e5a14ae88411c98b" + +[[package]] +name = "version_check" +version = "0.9.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", "winapi-util", ] [[package]] -name = "serde" -version = "1.0.192" +name = "want" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" +dependencies = [ + "try-lock", +] + +[[package]] +name = "warnings" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "64f68998838dab65727c9b30465595c6f7c953313559371ca8bf31759b3680ad" +dependencies = [ + "pin-project", + "tracing", + "warnings-macro", +] + +[[package]] +name = "warnings-macro" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59195a1db0e95b920366d949ba5e0d3fc0e70b67c09be15ce5abb790106b0571" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "wasi" +version = "0.11.0+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" + +[[package]] +name = "wasm-bindgen" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" +dependencies = [ + "cfg-if 1.0.0", + "once_cell", + "rustversion", + "wasm-bindgen-macro", +] + +[[package]] +name = "wasm-bindgen-backend" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" +dependencies = [ + "bumpalo", + "log", + "proc-macro2", + "quote", + "syn 2.0.96", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-futures" +version = "0.4.50" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "555d470ec0bc3bb57890405e5d4322cc9ea83cebb085523ced7be4144dac1e61" +dependencies = [ + "cfg-if 1.0.0", + "js-sys", + "once_cell", + "wasm-bindgen", + "web-sys", +] + +[[package]] +name = "wasm-bindgen-macro" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" +dependencies = [ + "quote", + "wasm-bindgen-macro-support", +] + +[[package]] +name = "wasm-bindgen-macro-support" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", + "wasm-bindgen-backend", + "wasm-bindgen-shared", +] + +[[package]] +name = "wasm-bindgen-shared" +version = "0.2.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "wax" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8d12a78aa0bab22d2f26ed1a96df7ab58e8a93506a3e20adb47c51a93b4e1357" +dependencies = [ + "const_format", + "itertools 0.11.0", + "nom", + "pori", + "regex", + "thiserror 1.0.69", + "walkdir", +] + +[[package]] +name = "wayland-backend" +version = "0.3.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "056535ced7a150d45159d3a8dc30f91a2e2d588ca0b23f70e56033622b8016f6" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b66249d3fc69f76fd74c82cc319300faa554e9d865dab1f7cd66cc20db10b280" +dependencies = [ + "bitflags 2.8.0", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-csd-frame" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "625c5029dbd43d25e6aa9615e88b829a5cad13b2819c4ae129fdbb7c31ab4c7e" +dependencies = [ + "bitflags 2.8.0", + "cursor-icon", + "wayland-backend", +] + +[[package]] +name = "wayland-cursor" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32b08bc3aafdb0035e7fe0fdf17ba0c09c268732707dca4ae098f60cb28c9e4c" +dependencies = [ + "rustix", + "wayland-client", + "xcursor", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cd0ade57c4e6e9a8952741325c30bf82f4246885dca8bf561898b86d0c1f58e" +dependencies = [ + "bitflags 2.8.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-plasma" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b31cab548ee68c7eb155517f2212049dc151f7cd7910c2b66abfd31c3ee12bd" +dependencies = [ + "bitflags 2.8.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols-wlr" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "782e12f6cd923c3c316130d56205ebab53f55d6666b7faddfad36cecaeeb4022" +dependencies = [ + "bitflags 2.8.0", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597f2001b2e5fc1121e3d5b9791d3e78f05ba6bfa4641053846248e3a13661c3" +dependencies = [ + "proc-macro2", + "quick-xml 0.36.2", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efa8ac0d8e8ed3e3b5c9fc92c7881406a268e11555abe36493efabe649a29e09" +dependencies = [ + "dlib", + "log", + "once_cell", + "pkg-config", +] + +[[package]] +name = "web-sys" +version = "0.3.77" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33b6dd2ef9186f1f2072e409e99cd22a975331a6b3591b12c764e0e55c60d5d2" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "serde", + "wasm-bindgen", +] + +[[package]] +name = "webpki-roots" +version = "0.26.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bca2a08484b285dcb282d0f67b26cadc0df8b19f8c12502c13d966bf9482f001" +checksum = "5d642ff16b7e79272ae451b7322067cdc17cadf68c23264be9d94a32319efe7e" dependencies = [ - "serde_derive", + "rustls-pki-types", ] [[package]] -name = "serde_derive" -version = "1.0.192" +name = "weezl" +version = "0.1.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53a85b86a771b1c87058196170769dd264f66c0782acf1ae6cc51bfd64b39082" + +[[package]] +name = "widestring" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7219d36b6eac893fa81e84ebe06485e7dcbb616177469b142df14f1f4deb1311" + +[[package]] +name = "winapi" +version = "0.2.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" + +[[package]] +name = "winapi" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-build" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" + +[[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.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf221c93e13a30d793f7645a0e7762c55d169dbb0a49671918a2319d289b10bb" +dependencies = [ + "windows-sys 0.59.0", +] + +[[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.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143" +dependencies = [ + "windows-core 0.57.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +dependencies = [ + "windows-core 0.58.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d" +dependencies = [ + "windows-implement 0.57.0", + "windows-interface 0.57.0", + "windows-result 0.1.2", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-core" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +dependencies = [ + "windows-implement 0.58.0", + "windows-interface 0.58.0", + "windows-result 0.2.0", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-implement" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "windows-implement" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "windows-interface" +version = "0.57.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "windows-interface" +version = "0.58.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result 0.2.0", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result 0.2.0", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + +[[package]] +name = "windows-sys" +version = "0.52.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" +dependencies = [ + "windows-targets 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-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + +[[package]] +name = "windows-targets" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + +[[package]] +name = "windows_i686_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + +[[package]] +name = "windows_i686_msvc" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d6c7207fbec9faa48073f3e3074cbe553af6ea512d7c21ba46e434e70ea9fbc1" -dependencies = [ - "proc-macro2", - "quote", - "syn", -] +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" [[package]] -name = "serde_json" -version = "1.0.108" +name = "windows_x86_64_msvc" +version = "0.42.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d1c7e3eac408d115102c4c24ad393e0821bb3a5df4d506a80f85f7a742a526b" -dependencies = [ - "itoa", - "ryu", - "serde", -] +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" [[package]] -name = "serde_with" -version = "2.3.3" +name = "windows_x86_64_msvc" +version = "0.48.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "07ff71d2c147a7b57362cead5e22f772cd52f6ab31cfcd9edcd7f6aeb2a0afbe" -dependencies = [ - "base64", - "chrono", - "hex", - "indexmap", - "serde", - "serde_json", - "serde_with_macros", - "time", -] +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] -name = "serde_with_macros" -version = "2.3.3" +name = "windows_x86_64_msvc" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "881b6f881b17d13214e5d494c939ebab463d01264ce1811e9d4ac3a882e7695f" -dependencies = [ - "darling", - "proc-macro2", - "quote", - "syn", -] +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" [[package]] -name = "sha2" -version = "0.10.8" +name = "winit" +version = "0.30.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "f5d74280aabb958072864bff6cfbcf9025cf8bfacdde5e32b5e12920ef703b0f" dependencies = [ - "cfg-if", - "cpufeatures", - "digest", + "ahash 0.8.11", + "android-activity", + "atomic-waker", + "bitflags 2.8.0", + "block2", + "bytemuck", + "calloop", + "cfg_aliases", + "concurrent-queue", + "core-foundation 0.9.4", + "core-graphics", + "cursor-icon", + "dpi", + "js-sys", + "libc", + "memmap2", + "ndk", + "objc2", + "objc2-app-kit", + "objc2-foundation", + "objc2-ui-kit", + "orbclient", + "percent-encoding", + "pin-project", + "raw-window-handle", + "redox_syscall 0.4.1", + "rustix", + "sctk-adwaita", + "smithay-client-toolkit", + "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 = "sharded-slab" -version = "0.1.7" +name = "winnow" +version = "0.6.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f40ca3c46823713e0d4209592e8d6e826aa57e928f09752619fc696c499637f6" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" dependencies = [ - "lazy_static", + "memchr", ] [[package]] -name = "smallvec" -version = "1.11.1" +name = "write16" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "942b4a808e05215192e39f4ab80813e599068285906cc91aa64f923db842bd5a" +checksum = "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936" [[package]] -name = "strsim" -version = "0.10.0" +name = "writeable" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" +checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] -name = "syn" -version = "2.0.39" +name = "x11-clipboard" +version = "0.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "23e78b90f2fcf45d3e842032ce32e3f2d1545ba6636271dcbf24fa306d87be7a" +checksum = "662d74b3d77e396b8e5beb00b9cad6a9eccf40b2ef68cc858784b14c41d535a3" dependencies = [ - "proc-macro2", - "quote", - "unicode-ident", + "libc", + "x11rb", ] [[package]] -name = "thiserror" -version = "1.0.50" +name = "x11-dl" +version = "2.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a7210f5c9a7156bb50aa36aed4c95afb51df0df00713949448cf9e97d382d2" +checksum = "38735924fedd5314a6e548792904ed8c6de6636285cb9fec04d5b1db85c1516f" dependencies = [ - "thiserror-impl", + "libc", + "once_cell", + "pkg-config", ] [[package]] -name = "thiserror-impl" -version = "1.0.50" +name = "x11rb" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "266b2e40bc00e5a6c09c3584011e08b06f123c00362c92b975ba9843aaaa14b8" +checksum = "5d91ffca73ee7f68ce055750bf9f6eca0780b8c85eff9bc046a3b0da41755e12" dependencies = [ - "proc-macro2", - "quote", - "syn", + "as-raw-xcb-connection", + "gethostname", + "libc", + "libloading", + "once_cell", + "rustix", + "x11rb-protocol", ] [[package]] -name = "thread_local" -version = "1.1.7" +name = "x11rb-protocol" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdd6f064ccff2d6567adcb3873ca630700f00b5ad3f060c25b5dcfd9a4ce152" -dependencies = [ - "cfg-if", - "once_cell", -] +checksum = "ec107c4503ea0b4a98ef47356329af139c0a4f7750e621cf2973cd3385ebcb3d" [[package]] -name = "time" -version = "0.3.30" +name = "xattr" +version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c4a34ab300f2dee6e562c10a046fc05e358b29f9bf92277f30c3c8d82275f6f5" +checksum = "e105d177a3871454f754b33bb0ee637ecaaac997446375fd3e5d43a2ed00c909" dependencies = [ - "deranged", - "itoa", - "powerfmt", - "serde", - "time-core", - "time-macros", + "libc", + "linux-raw-sys", + "rustix", ] [[package]] -name = "time-core" -version = "0.1.2" +name = "xcursor" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "0ef33da6b1660b4ddbfb3aef0ade110c8b8a781a3b6382fa5f2b5b040fd55f61" [[package]] -name = "time-macros" -version = "0.2.15" +name = "xdg-home" +version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ad70d68dba9e1f8aceda7aa6711965dfec1cac869f311a51bd08b3a2ccbce20" +checksum = "ec1cdab258fb55c0da61328dc52c8764709b249011b2cad0454c72f0bf10a1f6" dependencies = [ - "time-core", + "libc", + "windows-sys 0.59.0", ] [[package]] -name = "tracing" -version = "0.1.40" +name = "xkbcommon-dl" +version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" +checksum = "d039de8032a9a8856a6be89cea3e5d12fdd82306ab7c94d74e6deab2460651c5" dependencies = [ - "pin-project-lite", - "tracing-core", + "bitflags 2.8.0", + "dlib", + "log", + "once_cell", + "xkeysym", ] [[package]] -name = "tracing-core" -version = "0.1.32" +name = "xkeysym" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -dependencies = [ - "once_cell", - "valuable", -] +checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" [[package]] -name = "tracing-error" -version = "0.2.0" +name = "xml-rs" +version = "0.8.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d686ec1c0f384b1277f097b2f279a2ecc11afe8c133c1aabf036a27cb4cd206e" -dependencies = [ - "tracing", - "tracing-subscriber", -] +checksum = "c5b940ebc25896e71dd073bad2dbaa2abfe97b0a391415e22ad1326d9c54e3c4" [[package]] -name = "tracing-subscriber" -version = "0.3.17" +name = "yoke" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30a651bc37f915e81f087d86e62a18eec5f79550c7faff886f7090b4ea757c77" +checksum = "120e6aef9aa629e3d4f52dc8cc43a015c7724194c97dfaf45180d2daf2b77f40" dependencies = [ - "sharded-slab", - "thread_local", - "tracing-core", + "serde", + "stable_deref_trait", + "yoke-derive", + "zerofrom", ] [[package]] -name = "typenum" -version = "1.17.0" +name = "yoke-derive" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42ff0bf0c66b8238c6f3b578df37d0b7848e55df8577b3f74f92a69acceeb825" +checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", + "synstructure", +] [[package]] -name = "unicode-ident" -version = "1.0.12" +name = "zbus" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" +checksum = "bb97012beadd29e654708a0fdb4c84bc046f537aecfde2c3ee0a9e4b4d48c725" +dependencies = [ + "async-broadcast", + "async-executor", + "async-fs", + "async-io", + "async-lock", + "async-process", + "async-recursion", + "async-task", + "async-trait", + "blocking", + "enumflags2", + "event-listener", + "futures-core", + "futures-sink", + "futures-util", + "hex", + "nix", + "ordered-stream", + "rand", + "serde", + "serde_repr", + "sha1", + "static_assertions", + "tracing", + "uds_windows", + "windows-sys 0.52.0", + "xdg-home", + "zbus_macros", + "zbus_names", + "zvariant", +] [[package]] -name = "unicode-xid" -version = "0.2.4" +name = "zbus-lockstep" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +checksum = "4ca2c5dceb099bddaade154055c926bb8ae507a18756ba1d8963fd7b51d8ed1d" +dependencies = [ + "zbus_xml", + "zvariant", +] [[package]] -name = "utf8parse" -version = "0.2.1" +name = "zbus-lockstep-macros" +version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" +checksum = "709ab20fc57cb22af85be7b360239563209258430bccf38d8b979c5a2ae3ecce" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", + "zbus-lockstep", + "zbus_xml", + "zvariant", +] [[package]] -name = "valuable" -version = "0.1.0" +name = "zbus_macros" +version = "4.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b7e5d4d90034032940e4ace0d9a9a057e7a45cd94e6c007832e39edb82f6d" +checksum = "267db9407081e90bbfa46d841d3cbc60f59c0351838c4bc65199ecd79ab1983e" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.96", + "zvariant_utils", +] [[package]] -name = "version_check" -version = "0.9.4" +name = "zbus_names" +version = "3.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f" +checksum = "4b9b1fef7d021261cc16cba64c351d291b715febe0fa10dc3a443ac5a5022e6c" +dependencies = [ + "serde", + "static_assertions", + "zvariant", +] [[package]] -name = "walkdir" -version = "2.4.0" +name = "zbus_xml" +version = "4.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71d857dc86794ca4c280d616f7da00d2dbfd8cd788846559a6813e6aa4b54ee" +checksum = "ab3f374552b954f6abb4bd6ce979e6c9b38fb9d0cd7cc68a7d796e70c9f3a233" dependencies = [ - "same-file", - "winapi-util", + "quick-xml 0.30.0", + "serde", + "static_assertions", + "zbus_names", + "zvariant", ] [[package]] -name = "wasm-bindgen" -version = "0.2.88" +name = "zerocopy" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7daec296f25a1bae309c0cd5c29c4b260e510e6d813c286b19eaadf409d40fce" +checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ - "cfg-if", - "wasm-bindgen-macro", + "byteorder", + "zerocopy-derive", ] [[package]] -name = "wasm-bindgen-backend" -version = "0.2.88" +name = "zerocopy-derive" +version = "0.7.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e397f4664c0e4e428e8313a469aaa58310d302159845980fd23b0f22a847f217" +checksum = "fa4f8080344d4671fb4e831a13ad1e68092748387dfc4f55e356242fae12ce3e" dependencies = [ - "bumpalo", - "log", - "once_cell", "proc-macro2", "quote", - "syn", - "wasm-bindgen-shared", + "syn 2.0.96", ] [[package]] -name = "wasm-bindgen-macro" -version = "0.2.88" +name = "zerofrom" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5961017b3b08ad5f3fe39f1e79877f8ee7c23c5e5fd5eb80de95abc41f1f16b2" +checksum = "cff3ee08c995dee1859d998dea82f7374f2826091dd9cd47def953cae446cd2e" dependencies = [ - "quote", - "wasm-bindgen-macro-support", + "zerofrom-derive", ] [[package]] -name = "wasm-bindgen-macro-support" -version = "0.2.88" +name = "zerofrom-derive" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c5353b8dab669f5e10f5bd76df26a9360c748f054f862ff5f3f8aae0c7fb3907" +checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn", - "wasm-bindgen-backend", - "wasm-bindgen-shared", + "syn 2.0.96", + "synstructure", ] [[package]] -name = "wasm-bindgen-shared" -version = "0.2.88" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0d046c5d029ba91a1ed14da14dca44b68bf2f124cfbaf741c54151fdb3e0750b" - -[[package]] -name = "wax" -version = "0.5.0" +name = "zeroize" +version = "1.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06c7a3bac6110ac062b7b422a442b7ee23e07209e2784a036654cab1e71bbafc" +checksum = "ced3678a2879b30306d323f4542626697a464a97c0a07c9aebf7ebca65cd4dde" dependencies = [ - "bstr", - "const_format", - "itertools", - "nom", - "nom-supreme", - "pori", - "regex", - "smallvec", - "thiserror", - "walkdir", + "zeroize_derive", ] [[package]] -name = "widestring" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "653f141f39ec16bba3c5abe400a0c60da7468261cc2cbf36805022876bc721a8" - -[[package]] -name = "winapi" -version = "0.3.9" +name = "zeroize_derive" +version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "ce36e65b0d2999d2aafac989fb249189a141aee1f53c612c1f37d72631959f69" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "proc-macro2", + "quote", + "syn 2.0.96", ] [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" +name = "zerovec" +version = "0.10.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +checksum = "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079" +dependencies = [ + "yoke", + "zerofrom", + "zerovec-derive", +] [[package]] -name = "winapi-util" -version = "0.1.6" +name = "zerovec-derive" +version = "0.10.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f29e6f9198ba0d26b4c9f07dbe6f9ed633e1f3d5b8b414090084349e46a52596" +checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ - "winapi", + "proc-macro2", + "quote", + "syn 2.0.96", ] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "zip" +version = "2.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "ae9c1ea7b3a5e1f4b922ff856a129881167511563dc219869afe3787fc0c1a45" +dependencies = [ + "aes", + "arbitrary", + "bzip2", + "constant_time_eq", + "crc32fast", + "crossbeam-utils", + "deflate64", + "displaydoc", + "flate2", + "hmac", + "indexmap 2.7.1", + "lzma-rs", + "memchr", + "pbkdf2", + "rand", + "sha1", + "thiserror 2.0.11", + "time", + "zeroize", + "zopfli", + "zstd", +] [[package]] -name = "windows-core" -version = "0.51.1" +name = "zopfli" +version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1f8cf84f35d2db49a46868f947758c7a1138116f7fac3bc844f43ade1292e64" +checksum = "e5019f391bac5cf252e93bbcc53d039ffd62c7bfb7c150414d61369afe57e946" dependencies = [ - "windows-targets", + "bumpalo", + "crc32fast", + "lockfree-object-pool", + "log", + "once_cell", + "simd-adler32", ] [[package]] -name = "windows-sys" -version = "0.48.0" +name = "zstd" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +checksum = "fcf2b778a664581e31e389454a7072dab1647606d44f7feea22cd5abb9c9f3f9" dependencies = [ - "windows-targets", + "zstd-safe", ] [[package]] -name = "windows-targets" -version = "0.48.5" +name = "zstd-safe" +version = "7.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +checksum = "54a3ab4db68cea366acc5c897c7b4d4d1b8994a9cd6e6f841f8964566a419059" dependencies = [ - "windows_aarch64_gnullvm", - "windows_aarch64_msvc", - "windows_i686_gnu", - "windows_i686_msvc", - "windows_x86_64_gnu", - "windows_x86_64_gnullvm", - "windows_x86_64_msvc", + "zstd-sys", ] [[package]] -name = "windows_aarch64_gnullvm" -version = "0.48.5" +name = "zstd-sys" +version = "2.0.13+zstd.1.5.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" +checksum = "38ff0f21cfee8f97d94cef41359e0c89aa6113028ab0291aa8ca0038995a95aa" +dependencies = [ + "cc", + "pkg-config", +] [[package]] -name = "windows_aarch64_msvc" -version = "0.48.5" +name = "zune-core" +version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" +checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" [[package]] -name = "windows_i686_gnu" -version = "0.48.5" +name = "zune-inflate" +version = "0.2.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" +checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02" +dependencies = [ + "simd-adler32", +] [[package]] -name = "windows_i686_msvc" -version = "0.48.5" +name = "zune-jpeg" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" +checksum = "99a5bab8d7dedf81405c4bb1f2b83ea057643d9cb28778cea9eecddeedd2e028" +dependencies = [ + "zune-core", +] [[package]] -name = "windows_x86_64_gnu" -version = "0.48.5" +name = "zvariant" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" +checksum = "2084290ab9a1c471c38fc524945837734fbf124487e105daec2bb57fd48c81fe" +dependencies = [ + "endi", + "enumflags2", + "serde", + "static_assertions", + "zvariant_derive", +] [[package]] -name = "windows_x86_64_gnullvm" -version = "0.48.5" +name = "zvariant_derive" +version = "4.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" +checksum = "73e2ba546bda683a90652bac4a279bc146adad1386f25379cf73200d2002c449" +dependencies = [ + "proc-macro-crate", + "proc-macro2", + "quote", + "syn 2.0.96", + "zvariant_utils", +] [[package]] -name = "windows_x86_64_msvc" -version = "0.48.5" +name = "zvariant_utils" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" +checksum = "c51bcff7cc3dbb5055396bcf774748c3dab426b4b8659046963523cee4808340" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.96", +] diff --git a/Cargo.toml b/Cargo.toml index 105e49e..0869868 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,17 +1,53 @@ +[workspace] +members = ["crates/*"] + [package] edition = "2021" -name = "modhook" -version = "1.0.3" +name = "discord-modloader" +description = "An in-memory mod loader for Discord" +version = "0.2.0-beta.1" +license = "LGPL-3.0" +authors = ["Megumin "] +repository = "https://github.com/MeguminSama/Discord-ModLoader/" [lib] -crate-type = ["cdylib"] -name = "libmodhook" +crate-type = ["cdylib", "rlib"] [dependencies] -clap = { version = "4.4.7", features = ["derive"] } -detours-sys = {version = "0.1.2"} -widestring = "1.0.2" -winapi = {version = "0.3.9", features = ["winbase", "handleapi", "minwindef", "consoleapi", "winnt", "memoryapi", "namedpipeapi"]} +clap = { version = "4.5.20", features = ["derive"] } +gui = { path = "crates/gui" } +electron-hook = {version = "0.2.0-beta.4", features = ["self-executable"] } +modloader-core = { path = "./crates/modloader-core" } +libc = "0.2.167" [build-dependencies] -asar = "0.2.0" +electron-hook-build-scripts = { path = "../electron-hook/crates/electron-hook-build-scripts" } + +[package.metadata.packager] +product-name = "Discord Modloader" +identifier = "dev.megu.discord.modloader" +authors = ["Megumin + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + + This version of the GNU Lesser General Public License incorporates +the terms and conditions of version 3 of the GNU General Public +License, supplemented by the additional permissions listed below. + + 0. Additional Definitions. + + As used herein, "this License" refers to version 3 of the GNU Lesser +General Public License, and the "GNU GPL" refers to version 3 of the GNU +General Public License. + + "The Library" refers to a covered work governed by this License, +other than an Application or a Combined Work as defined below. + + An "Application" is any work that makes use of an interface provided +by the Library, but which is not otherwise based on the Library. +Defining a subclass of a class defined by the Library is deemed a mode +of using an interface provided by the Library. + + A "Combined Work" is a work produced by combining or linking an +Application with the Library. The particular version of the Library +with which the Combined Work was made is also called the "Linked +Version". + + The "Minimal Corresponding Source" for a Combined Work means the +Corresponding Source for the Combined Work, excluding any source code +for portions of the Combined Work that, considered in isolation, are +based on the Application, and not on the Linked Version. + + The "Corresponding Application Code" for a Combined Work means the +object code and/or source code for the Application, including any data +and utility programs needed for reproducing the Combined Work from the +Application, but excluding the System Libraries of the Combined Work. + + 1. Exception to Section 3 of the GNU GPL. + + You may convey a covered work under sections 3 and 4 of this License +without being bound by section 3 of the GNU GPL. + + 2. Conveying Modified Versions. + + If you modify a copy of the Library, and, in your modifications, a +facility refers to a function or data to be supplied by an Application +that uses the facility (other than as an argument passed when the +facility is invoked), then you may convey a copy of the modified +version: + + a) under this License, provided that you make a good faith effort to + ensure that, in the event an Application does not supply the + function or data, the facility still operates, and performs + whatever part of its purpose remains meaningful, or + + b) under the GNU GPL, with none of the additional permissions of + this License applicable to that copy. + + 3. Object Code Incorporating Material from Library Header Files. + + The object code form of an Application may incorporate material from +a header file that is part of the Library. You may convey such object +code under terms of your choice, provided that, if the incorporated +material is not limited to numerical parameters, data structure +layouts and accessors, or small macros, inline functions and templates +(ten or fewer lines in length), you do both of the following: + + a) Give prominent notice with each copy of the object code that the + Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the object code with a copy of the GNU GPL and this license + document. + + 4. Combined Works. + + You may convey a Combined Work under terms of your choice that, +taken together, effectively do not restrict modification of the +portions of the Library contained in the Combined Work and reverse +engineering for debugging such modifications, if you also do each of +the following: + + a) Give prominent notice with each copy of the Combined Work that + the Library is used in it and that the Library and its use are + covered by this License. + + b) Accompany the Combined Work with a copy of the GNU GPL and this license + document. + + c) For a Combined Work that displays copyright notices during + execution, include the copyright notice for the Library among + these notices, as well as a reference directing the user to the + copies of the GNU GPL and this license document. + + d) Do one of the following: + + 0) Convey the Minimal Corresponding Source under the terms of this + License, and the Corresponding Application Code in a form + suitable for, and under terms that permit, the user to + recombine or relink the Application with a modified version of + the Linked Version to produce a modified Combined Work, in the + manner specified by section 6 of the GNU GPL for conveying + Corresponding Source. + + 1) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (a) uses at run time + a copy of the Library already present on the user's computer + system, and (b) will operate properly with a modified version + of the Library that is interface-compatible with the Linked + Version. + + e) Provide Installation Information, but only if you would otherwise + be required to provide such information under section 6 of the + GNU GPL, and only to the extent that such information is + necessary to install and execute a modified version of the + Combined Work produced by recombining or relinking the + Application with a modified version of the Linked Version. (If + you use option 4d0, the Installation Information must accompany + the Minimal Corresponding Source and Corresponding Application + Code. If you use option 4d1, you must provide the Installation + Information in the manner specified by section 6 of the GNU GPL + for conveying Corresponding Source.) + + 5. Combined Libraries. + + You may place library facilities that are a work based on the +Library side by side in a single library together with other library +facilities that are not Applications and are not covered by this +License, and convey such a combined library under terms of your +choice, if you do both of the following: + + a) Accompany the combined library with a copy of the same work based + on the Library, uncombined with any other library facilities, + conveyed under the terms of this License. + + b) Give prominent notice with the combined library that part of it + is a work based on the Library, and explaining where to find the + accompanying uncombined form of the same work. + + 6. Revised Versions of the GNU Lesser General Public License. + + The Free Software Foundation may publish revised and/or new versions +of the GNU Lesser General Public License from time to time. Such new +versions will be similar in spirit to the present version, but may +differ in detail to address new problems or concerns. + + Each version is given a distinguishing version number. If the +Library as you received it specifies that a certain numbered version +of the GNU Lesser General Public License "or any later version" +applies to it, you have the option of following the terms and +conditions either of that published version or of any later version +published by the Free Software Foundation. If the Library as you +received it does not specify a version number of the GNU Lesser +General Public License, you may choose any version of the GNU Lesser +General Public License ever published by the Free Software Foundation. + + If the Library as you received it specifies that a proxy can decide +whether future versions of the GNU Lesser General Public License shall +apply, that proxy's public statement of acceptance of any version is +permanent authorization for you to choose that version for the +Library. diff --git a/asar/index.js b/asar/index.js deleted file mode 100644 index 87fa2f8..0000000 --- a/asar/index.js +++ /dev/null @@ -1,30 +0,0 @@ -/// BEGIN MODHOOK INFO /// -const CUSTOM_DATA_DIR = process.env.MODHOOK_CUSTOM_DATA_DIR; -const MOD_ENTRYPOINT = process.env.MODHOOK_MOD_ENTRYPOINT; -const IS_MOONLIGHT = process.env.MODHOOK_IS_MOONLIGHT; -/// END MODHOOK INFO /// - -if (CUSTOM_DATA_DIR) { - const { app } = require("electron"); - const customAppDir = - app.getPath("appData") + "\\DiscordModHook\\AppData\\" + CUSTOM_DATA_DIR; - const _setPath = app.setPath; - - app.setPath = function (name, path) { - if (name === "userData") { - _setPath.call(app, name, customAppDir); - } else { - _setPath.call(app, name, path); - } - }; - - app.setPath("userData", customAppDir); -} - -if (IS_MOONLIGHT) { - require(MOD_ENTRYPOINT).inject( - require("path").resolve(__dirname, "..\\_app.asar") - ); -} else { - require(MOD_ENTRYPOINT); -} diff --git a/assets/favicons/1024x1024.png b/assets/favicons/1024x1024.png new file mode 100644 index 0000000..26b67ea Binary files /dev/null and b/assets/favicons/1024x1024.png differ diff --git a/assets/favicons/128x128.png b/assets/favicons/128x128.png new file mode 100644 index 0000000..67834d6 Binary files /dev/null and b/assets/favicons/128x128.png differ diff --git a/assets/favicons/256x256.png b/assets/favicons/256x256.png new file mode 100644 index 0000000..27fbc05 Binary files /dev/null and b/assets/favicons/256x256.png differ diff --git a/assets/favicons/32x32.png b/assets/favicons/32x32.png new file mode 100644 index 0000000..afbdf83 Binary files /dev/null and b/assets/favicons/32x32.png differ diff --git a/assets/favicons/512x512.png b/assets/favicons/512x512.png new file mode 100644 index 0000000..c565240 Binary files /dev/null and b/assets/favicons/512x512.png differ diff --git a/assets/favicons/64x64.png b/assets/favicons/64x64.png new file mode 100644 index 0000000..37f6d64 Binary files /dev/null and b/assets/favicons/64x64.png differ diff --git a/assets/favicons/logo_development.ico b/assets/favicons/logo_development.ico new file mode 100644 index 0000000..e69de29 diff --git a/assets/favicons/logo_development.png b/assets/favicons/logo_development.png new file mode 100644 index 0000000..1e196a8 Binary files /dev/null and b/assets/favicons/logo_development.png differ diff --git a/assets/favicons/logo_development.svg b/assets/favicons/logo_development.svg new file mode 100644 index 0000000..171599c --- /dev/null +++ b/assets/favicons/logo_development.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/fonts/Inter-Variable.ttf b/assets/fonts/Inter-Variable.ttf new file mode 100644 index 0000000..e31b51e Binary files /dev/null and b/assets/fonts/Inter-Variable.ttf differ diff --git a/assets/icons/chevron-down.svg b/assets/icons/chevron-down.svg new file mode 100644 index 0000000..d32fbd6 --- /dev/null +++ b/assets/icons/chevron-down.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/chevron-up.svg b/assets/icons/chevron-up.svg new file mode 100644 index 0000000..b372875 --- /dev/null +++ b/assets/icons/chevron-up.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/circle-help.svg b/assets/icons/circle-help.svg new file mode 100644 index 0000000..404fd48 --- /dev/null +++ b/assets/icons/circle-help.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/discord.svg b/assets/icons/discord.svg new file mode 100644 index 0000000..12ed677 --- /dev/null +++ b/assets/icons/discord.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/floppy-disk.svg b/assets/icons/floppy-disk.svg new file mode 100644 index 0000000..bd841f9 --- /dev/null +++ b/assets/icons/floppy-disk.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/folder.svg b/assets/icons/folder.svg new file mode 100644 index 0000000..6638d58 --- /dev/null +++ b/assets/icons/folder.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/gear.svg b/assets/icons/gear.svg new file mode 100644 index 0000000..cfae911 --- /dev/null +++ b/assets/icons/gear.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/github.svg b/assets/icons/github.svg new file mode 100644 index 0000000..52cbe4f --- /dev/null +++ b/assets/icons/github.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/assets/icons/home.svg b/assets/icons/home.svg new file mode 100644 index 0000000..8bba66c --- /dev/null +++ b/assets/icons/home.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/pen-to-square.svg b/assets/icons/pen-to-square.svg new file mode 100644 index 0000000..9ef4237 --- /dev/null +++ b/assets/icons/pen-to-square.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/pen.svg b/assets/icons/pen.svg new file mode 100644 index 0000000..760c655 --- /dev/null +++ b/assets/icons/pen.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/play.svg b/assets/icons/play.svg new file mode 100644 index 0000000..1ac3283 --- /dev/null +++ b/assets/icons/play.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/plus.svg b/assets/icons/plus.svg new file mode 100644 index 0000000..1ca2e58 --- /dev/null +++ b/assets/icons/plus.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/refresh-arrows.svg b/assets/icons/refresh-arrows.svg new file mode 100644 index 0000000..57662aa --- /dev/null +++ b/assets/icons/refresh-arrows.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/star-filled.svg b/assets/icons/star-filled.svg new file mode 100644 index 0000000..7a44769 --- /dev/null +++ b/assets/icons/star-filled.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/star-hollow.svg b/assets/icons/star-hollow.svg new file mode 100644 index 0000000..f8619cc --- /dev/null +++ b/assets/icons/star-hollow.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/stop.svg b/assets/icons/stop.svg new file mode 100644 index 0000000..e712216 --- /dev/null +++ b/assets/icons/stop.svg @@ -0,0 +1 @@ + diff --git a/assets/icons/trash.svg b/assets/icons/trash.svg new file mode 100644 index 0000000..38d1df4 --- /dev/null +++ b/assets/icons/trash.svg @@ -0,0 +1 @@ + diff --git a/assets/workflows/build.yml b/assets/workflows/build.yml new file mode 100644 index 0000000..93313d8 --- /dev/null +++ b/assets/workflows/build.yml @@ -0,0 +1,53 @@ +name: Publish + +on: + push: + tags: + - "v*" + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + package: + if: ${{ !startsWith(github.head_ref, 'renovate/') }} + strategy: + fail-fast: false + matrix: + platform: [ubuntu-latest, windows-latest] # no macos-latest yet + + runs-on: ${{ matrix.platform }} + + steps: + - uses: actions/checkout@v4 + - uses: dtolnay/rust-toolchain@nightly + - run: cargo +nightly install cargo-packager --locked + - run: cargo +nightly release + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: discord-modloader-${{ matrix.platform }} + path: dist/* + + release: + needs: package + runs-on: ubuntu-latest + permissions: + contents: write + steps: + - name: Download artifacts + uses: actions/download-artifact@v4 + with: + path: dist/ + merge-multiple: true + - name: Release + uses: svenstaro/upload-release-action@2.9.0 + with: + repo_token: ${{ secrets.GITHUB_TOKEN }} + file: dist/{PKGBUILD,discord-modloader*} + tag: ${{ github.ref }} + overwrite: true + file_glob: true + draft: true + prerelease: ${{ contains(github.ref, 'beta') }} diff --git a/build.rs b/build.rs index 1e8ee8c..ae2ed92 100644 --- a/build.rs +++ b/build.rs @@ -1,26 +1,3 @@ -use asar::AsarWriter; -use std::env; -use std::error::Error; - -use std::fs::File; -use std::path::PathBuf; - -static ASAR_INDEX: &[u8] = include_bytes!("asar/index.js"); -static ASAR_PACKAGE: &[u8] = include_bytes!("asar/package.json"); - -fn main() -> std::result::Result<(), Box> { - let mut asar = AsarWriter::new(); - - asar.write_file("index.js", ASAR_INDEX, true)?; - asar.write_file("package.json", ASAR_PACKAGE, true)?; - - let mut out_dir = PathBuf::from("target"); - - out_dir.push(env::var("PROFILE")?); - - out_dir.push("app.asar"); - - asar.finalize(File::create(out_dir.to_str().unwrap())?)?; - - Ok(()) +fn main() { + electron_hook_build_scripts::build(); } diff --git a/configs/icons/moonlight.png b/configs/icons/moonlight.png new file mode 100644 index 0000000..ca7ec31 Binary files /dev/null and b/configs/icons/moonlight.png differ diff --git a/configs/icons/vencord.png b/configs/icons/vencord.png new file mode 100644 index 0000000..cccf5b9 Binary files /dev/null and b/configs/icons/vencord.png differ diff --git a/configs/templates/moonlight.toml b/configs/templates/moonlight.toml new file mode 100644 index 0000000..dd6554e --- /dev/null +++ b/configs/templates/moonlight.toml @@ -0,0 +1,18 @@ +[mod] +name = "moonlight" +entrypoint = "injector.js" +icon = "moonlight.png" + +[mod.loader] +require = """ +require("$ENTRYPOINT").inject( + require("path").resolve(__dirname, "../_app.asar") +); +""" + +[mod.updater] +github_org = "moonlight-mod" +github_repo = "moonlight" +dist_file_name = "dist.tar.gz" +dist_file_type = "TarGz" +icon_url = "https://raw.githubusercontent.com/moonlight-mod/moonlight-mod.github.io/main/src/img/logo.png" diff --git a/configs/templates/vencord.toml b/configs/templates/vencord.toml new file mode 100644 index 0000000..b196962 --- /dev/null +++ b/configs/templates/vencord.toml @@ -0,0 +1,5 @@ +[mod] +name = "Vencord" +path = "/path/to/vencord/dist" +entrypoint = "patcher.js" +icon = "vencord.png" diff --git a/crates/gui/Cargo.toml b/crates/gui/Cargo.toml new file mode 100644 index 0000000..93a43f7 --- /dev/null +++ b/crates/gui/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "gui" +version = "0.2.0-beta.1" +edition = "2021" + +[lib] +crate-type = ["rlib"] + +[dependencies] +dirs = "6.0.0" +freya = { git = "https://github.com/marc2332/freya.git", rev = "72022bdb3275cdd4b1c007196b8e6de28dbaae31" } +modloader-core = { path = "../modloader-core" } +open = "5.3.1" +strum = "0.26.3" +sysinfo = "0.33.0" +tokio = { version = "1.43.0", features = ["rt-multi-thread"] } +toml = "0.8.19" +uuid = { version = "1.11.0", features = ["v4"] } diff --git a/crates/gui/src/app.rs b/crates/gui/src/app.rs new file mode 100644 index 0000000..b79290b --- /dev/null +++ b/crates/gui/src/app.rs @@ -0,0 +1,207 @@ +use freya::prelude::*; +use modloader_core::utils::{find_running_instances, kill_pids, launch_detached_instance}; +use modloader_core::{config, paths}; + +use crate::{AppPage, CONFIG, CURRENT_PAGE, POPUP_STATE, THEME}; +use crate::{PopupState, components}; + +#[component] +pub fn app() -> Element { + use components::sidebar::Sidebar; + + rsx!( + rect { + height: "100%", + width: "100%", + background: THEME.read().bg_secondary, + color: THEME.read().text_primary, + direction: "horizontal", + + match POPUP_STATE() { + PopupState::ConfirmDeleteMod(mod_id) => rsx!(DeleteModPopup { mod_id }), + PopupState::ConfirmDeleteProfile(profile_id) => rsx!(DeleteProfilePopup { profile_id }), + PopupState::InstanceAlreadyRunning(profile_id, new_instance_id, args) => rsx!(InstanceAlreadyRunning { profile_id, new_instance_id, args }), + PopupState::ConfirmDeleteInstance(profile_id, instance_id) => rsx!(DeleteInstancePopup { profile_id, instance_id }), + PopupState::ConfirmKillProfile(profile_id) => rsx!(KillInstancesPopup { profile_id }), + _ => rsx!(), + } + + Sidebar {}, + + Router {} + } + ) +} + +#[component] +pub fn Router() -> Element { + use components::home_page::HomePage; + use components::mod_from_template_page::ModFromTemplatePage; + use components::mod_page::ModPage; + use components::profile_page::ProfilePage; + use components::settings_page::SettingsPage; + + match CURRENT_PAGE.read().clone() { + AppPage::Home => rsx!(HomePage {}), + AppPage::Mod(mod_id) => rsx!(ModPage { mod_id }), + AppPage::ModFromTemplate => rsx!(ModFromTemplatePage {}), + AppPage::Profile(profile_id) => rsx!(ProfilePage { profile_id }), + AppPage::Settings => rsx!(SettingsPage {}), + } +} + +#[component] +fn DeleteModPopup(mod_id: String) -> Element { + let dependents: Vec = CONFIG + .read() + .profiles + .values() + .filter(|profile| { + profile + .instances + .values() + .any(|instance| instance.mod_id == mod_id) + }) + .map(|profile| profile.profile.name.clone()) + .collect(); + + let dependent_str = if dependents.is_empty() { + "No profiles depend on this mod.".to_string() + } else { + format!( + "Some profiles depend on this mod and will need updating:\n - {}", + dependents.join("\n - ") + ) + }; + + rsx!(components::popup::ConfirmationPopup { + title: "Delete Mod", + body: format!("Are you sure you want to delete this mod?\n{dependent_str}"), + on_confirm: move |_| { + *POPUP_STATE.write() = PopupState::None; + + // Delete mod + let mod_path = paths::config_mods_dir().join(format!("{mod_id}.toml")); + let _ = std::fs::remove_file(mod_path); + + *CONFIG.write() = config::Config::init(); + + *CURRENT_PAGE.write() = AppPage::Home; + }, + on_cancel: move |_| { + *POPUP_STATE.write() = PopupState::None; + } + }) +} + +#[component] +fn DeleteProfilePopup(profile_id: String) -> Element { + rsx!(components::popup::ConfirmationPopup { + title: "Delete Mod", + body: format!( + "Are you sure you want to delete this profile?\nAny active instances of this profile will be stopped." + ), + on_confirm: move |_| { + *POPUP_STATE.write() = PopupState::None; + + let pids = find_running_instances( + &profile_id, + &CONFIG.read().profiles.get(&profile_id).unwrap().clone(), + ); + + kill_pids(pids); + + // Delete mod + let profile_path = paths::config_profile_dir().join(format!("{profile_id}.toml")); + let _ = std::fs::remove_file(profile_path); + + *CONFIG.write() = config::Config::init(); + + *CURRENT_PAGE.write() = AppPage::Home; + }, + on_cancel: move |_| { + *POPUP_STATE.write() = PopupState::None; + } + }) +} + +#[component] +fn DeleteInstancePopup(profile_id: String, instance_id: String) -> Element { + rsx!(components::popup::ConfirmationPopup { + title: "Delete Instance", + body: format!( + "Are you sure you want to delete this instance?\nAny active instances of the current profile will be stopped." + ), + on_confirm: move |_| { + *POPUP_STATE.write() = PopupState::None; + + let pids = find_running_instances( + &profile_id, + &CONFIG.read().profiles.get(&profile_id).unwrap().clone(), + ); + + kill_pids(pids); + + // Delete instance + + if let Some(profile) = CONFIG.write().profiles.get_mut(&profile_id) { + profile.instances.remove(&instance_id); + let Ok(profile_toml) = toml::to_string::(profile) else { + return; + }; + + let profile_path = paths::config_profile_dir().join(format!("{profile_id}.toml")); + let _ = std::fs::write(profile_path, profile_toml); + } + + *CONFIG.write() = config::Config::init(); + }, + on_cancel: move |_| { + *POPUP_STATE.write() = PopupState::None; + } + }) +} + +#[component] +fn KillInstancesPopup(profile_id: String) -> Element { + rsx!(components::popup::ConfirmationPopup { + title: "Stop Running Instances", + body: "Are you sure you want to stop all running instances of this profile?", + on_confirm: move |_| { + *POPUP_STATE.write() = PopupState::None; + + let pids = find_running_instances( + &profile_id, + &CONFIG.read().profiles.get(&profile_id).unwrap().clone(), + ); + + kill_pids(pids); + }, + on_cancel: move |_| { + *POPUP_STATE.write() = PopupState::None; + } + }) +} + +#[component] +fn InstanceAlreadyRunning(profile_id: String, new_instance_id: String, args: String) -> Element { + rsx!(components::popup::ConfirmationPopup { + title: "Instance already running", + body: "An instance of this profile is already running.\nDo you want to stop the running instance and start a new one?", + on_confirm: move |_| { + *POPUP_STATE.write() = PopupState::None; + + let pids = find_running_instances( + &profile_id, + &CONFIG.read().profiles.get(&profile_id).unwrap().clone(), + ); + + kill_pids(pids); + + launch_detached_instance(&profile_id, &new_instance_id, &args, false).unwrap(); + }, + on_cancel: move |_| { + *POPUP_STATE.write() = PopupState::None; + } + }) +} diff --git a/crates/gui/src/assets.rs b/crates/gui/src/assets.rs new file mode 100644 index 0000000..d942a1b --- /dev/null +++ b/crates/gui/src/assets.rs @@ -0,0 +1,132 @@ +use modloader_core::config; + +pub const CHEVRON_DOWN_ICON: &[u8] = include_bytes!("../../../assets/icons/chevron-down.svg"); +pub const CHEVRON_UP_ICON: &[u8] = include_bytes!("../../../assets/icons/chevron-up.svg"); +pub const CIRCLE_HELP_ICON: &[u8] = include_bytes!("../../../assets/icons/circle-help.svg"); +pub const DISCORD_ICON: &[u8] = include_bytes!("../../../assets/icons/discord.svg"); +pub const FLOPPY_DISK_ICON: &[u8] = include_bytes!("../../../assets/icons/floppy-disk.svg"); +pub const FOLDER_ICON: &[u8] = include_bytes!("../../../assets/icons/folder.svg"); +pub const GEAR_ICON: &[u8] = include_bytes!("../../../assets/icons/gear.svg"); +pub const GITHUB_ICON: &[u8] = include_bytes!("../../../assets/icons/github.svg"); +pub const HOME_ICON: &[u8] = include_bytes!("../../../assets/icons/home.svg"); +pub const PENCIL_ICON: &[u8] = include_bytes!("../../../assets/icons/pen.svg"); +pub const PEN_TO_SQUARE_ICON: &[u8] = include_bytes!("../../../assets/icons/pen-to-square.svg"); +pub const PLAY_ICON: &[u8] = include_bytes!("../../../assets/icons/play.svg"); +pub const PLUS_ICON: &[u8] = include_bytes!("../../../assets/icons/plus.svg"); +pub const REFRESH_ARROWS_ICON: &[u8] = include_bytes!("../../../assets/icons/refresh-arrows.svg"); +pub const STAR_FILLED_ICON: &[u8] = include_bytes!("../../../assets/icons/star-filled.svg"); +pub const STAR_HOLLOW_ICON: &[u8] = include_bytes!("../../../assets/icons/star-hollow.svg"); +pub const STOP_ICON: &[u8] = include_bytes!("../../../assets/icons/stop.svg"); +pub const TRASH_ICON: &[u8] = include_bytes!("../../../assets/icons/trash.svg"); + +pub const FONT_INTER: &[u8] = include_bytes!("../../../assets/fonts/Inter-Variable.ttf"); + +pub const DISCORD_INVITE_LINK: &str = "https://discord.gg/r5bmSXBEPC"; +pub const GITHUB_REPO_LINK: &str = "https://github.com/meguminsama/discord-modloader"; + +pub const MOONLIGHT_LOGO: &[u8] = include_bytes!("../../../configs/icons/moonlight.png"); +pub const VENCORD_LOGO: &[u8] = include_bytes!("../../../configs/icons/vencord.png"); + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum ModTemplateIcon { + Svg(&'static [u8]), + Image(&'static [u8]), +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct ModTemplate { + pub config: config::Mod, + pub icon: Option, + pub support_link: Option, +} + +pub struct ModTemplates; + +impl ModTemplates { + pub fn get_all() -> Vec { + vec![Self::blank(), Self::moonlight(), Self::vencord()] + } + + pub fn blank() -> ModTemplate { + let config = config::Mod { + name: "New Blank Template".to_string(), + entrypoint: "injector.js".to_string(), + + updater: None, + + icon: None, + loader: None, + path: None, + }; + + ModTemplate { + config, + icon: Some(ModTemplateIcon::Svg(PLUS_ICON)), + support_link: None, + } + } + + pub fn moonlight() -> ModTemplate { + let config = config::Mod { + name: "New Moonlight Template".to_string(), + entrypoint: "injector.js".to_string(), + + updater: Some(config::ModUpdater { + github_org: "moonlight-mod".to_string(), + github_repo: "moonlight".to_string(), + dist_file_names: vec!["dist.tar.gz".to_string()], + dist_file_type: config::DistFileType::TarGz, + icon_url: Some( + "https://raw.githubusercontent.com/moonlight-mod/moonlight-mod.github.io/main/src/img/logo.png".to_string(), + ), + ask_before_update: true, + auto_update: true, + }), + + icon: None, + loader: None, + path: None, + }; + + ModTemplate { + config, + icon: Some(ModTemplateIcon::Image(MOONLIGHT_LOGO)), + support_link: Some("https://moonlight-mod.github.io".to_string()), + } + } + + pub fn vencord() -> ModTemplate { + let config = config::Mod { + name: "New Vencord Template".to_string(), + entrypoint: "patcher.js".to_string(), + + updater: Some(config::ModUpdater { + github_org: "vendicated".to_string(), + github_repo: "vencord".to_string(), + // TODO: Force vencord to have a single dist file because RAAAAAAAAAA + dist_file_names: vec!["patcher.js", "preload.js", "renderer.js", "renderer.css"] + .into_iter() + .map(|s| s.to_string()) + .collect(), + dist_file_type: config::DistFileType::Raw, + icon_url: Some( + "https://raw.githubusercontent.com/Vencord/Vesktop/main/static/icon.png" + .to_string(), + ), + ask_before_update: true, + // TODO: Check this works properly, otherwise, let vencord use it's own updater. + auto_update: true, + }), + + icon: None, + loader: None, + path: None, + }; + + ModTemplate { + config, + icon: Some(ModTemplateIcon::Image(VENCORD_LOGO)), + support_link: Some("https://vencord.dev".to_string()), + } + } +} diff --git a/crates/gui/src/components/button.rs b/crates/gui/src/components/button.rs new file mode 100644 index 0000000..b1fd1a9 --- /dev/null +++ b/crates/gui/src/components/button.rs @@ -0,0 +1,117 @@ +use freya::prelude::*; + +use crate::{THEME, utils::hoverable::hoverable}; + +#[component] +pub fn Button( + children: Element, + onpress: Option>, + selected: Option, + base_color: Option, + target_color: Option, + selected_color: Option, + width: Option, + height: Option, + corner_radius: Option, + padding: Option, + main_align: Option, + cross_align: Option, + onmouseenter: Option>, + onmouseleave: Option>, + shadow: Option, + stretch: Option, + margin: Option, + direction: Option, + spacing: Option, +) -> Element { + let mut animation = hoverable!(move |_conf| { + let base_color = base_color.as_deref().unwrap_or(THEME.read().bg_tertiary); + let target_color = target_color.as_deref().unwrap_or(THEME.read().blurple); + AnimColor::new(base_color, target_color) + .ease(Ease::InOut) + .time(100) + }); + + let background = match selected { + Some(true) => selected_color + .as_deref() + .unwrap_or(THEME.read().blurple) + .to_string(), + _ => animation.animation.get().read().read(), + }; + + let corner_radius = match corner_radius { + Some(corner_radius) => corner_radius, + None => "8".to_string(), + }; + + let width = match width { + Some(width) => width, + None => "auto".to_string(), + }; + + let height = match height { + Some(height) => height, + None => "40".to_string(), + }; + + let padding = match padding { + Some(padding) => padding, + None => "8 16".to_string(), + }; + + let shadow = match shadow { + Some(shadow) => shadow, + None => freya::hooks::DARK_THEME.button.shadow.into(), + }; + + let stretch = stretch.unwrap_or(true); + let button_width = if stretch { "100%" } else { "auto" }; + let button_height = if stretch { "auto" } else { "100%" }; + + rsx!(rect { + width: width, + height: height, + margin: margin, + onmouseenter: move |e| { + (animation.onmouseenter)(e.clone()); + if let Some(onmouseenter) = onmouseenter { + onmouseenter(e); + } + }, + onmouseleave: move |e| { + (animation.onmouseleave)(e.clone()); + if let Some(onmouseleave) = onmouseleave { + onmouseleave(e); + } + }, + freya::prelude::Button { + onpress: onpress, + theme: theme_with!(ButtonTheme { + background: background.clone().into(), + hover_background: background.clone().into(), + border_fill: background.clone().into(), + focus_border_fill: THEME.read().blurple.into(), + padding: padding.clone().into(), + shadow: shadow.into(), + width: button_width.into(), + height: button_height.into(), + corner_radius: corner_radius.clone().into(), + }), + rect { + // content: "fit", + corner_radius: corner_radius.clone(), + direction: direction.as_deref().unwrap_or("horizontal"), + cross_align: cross_align.as_deref().unwrap_or("center"), + main_align: main_align.as_deref().unwrap_or("start"), + spacing: spacing.as_deref().unwrap_or("8"), + width: if stretch { "calc(100% - 4)" } else { "auto" }, + height: if stretch { "calc(100% - 4)" } else { "auto" }, + color: THEME.read().text_primary, + background: background, + + {children} + } + } + }) +} diff --git a/crates/gui/src/components/home_page.rs b/crates/gui/src/components/home_page.rs new file mode 100644 index 0000000..52b372b --- /dev/null +++ b/crates/gui/src/components/home_page.rs @@ -0,0 +1,140 @@ +use freya::prelude::*; +use modloader_core::config; + +use crate::{CONFIG, THEME, utils::icons::GetIcon}; + +fn get_starred_profiles() -> Vec<(String, config::Profile, Vec<(String, config::Instance)>)> { + CONFIG + .read() + .profiles + .clone() + .into_iter() + .filter_map(|(profile_id, profile)| { + let instances = profile + .instances + .into_iter() + .filter(|(_, instance)| instance.starred) + .collect::>(); + + if instances.is_empty() { + None + } else { + Some((profile_id, profile.profile, instances)) + } + }) + .collect::>() +} + +#[component] +pub fn HomePage() -> Element { + let starred = use_signal(get_starred_profiles); + + dbg!(&starred); + + rsx!( + rect { + direction: "vertical", + padding: "8", + spacing: "8", + + Header { }, + + ScrollView { + direction: "vertical", + spacing: "8", + + for (profile_id, profile, instances) in starred() { + ProfileBlock { profile_id, profile, instances } + } + } + } + ) +} + +#[component] +fn Header() -> Element { + rsx!(rect { + width: "100%", + padding: "12 8 12 12", + height: "{48 + 12 + 12}", + corner_radius: "8", + border: "0 0 2 0 outer {THEME.read().bg_secondary}", + + background: THEME.read().bg_primary, + + direction: "horizontal", + cross_align: "center", + + spacing: "12", + + label { + font_size: "18", + + "Home" + } + }) +} + +#[component] +fn ProfileBlock( + profile_id: String, + profile: config::Profile, + instances: Vec<(String, config::Instance)>, +) -> Element { + rsx!( + rect { + padding: "8", + background: THEME.read().bg_primary, + corner_radius: "8", + + label { + "{profile.name}" + } + + ScrollView { + direction: "horizontal", + spacing: "8", + padding: "8", + width: "auto", + height: "auto", + + for (instance_id, instance) in instances { + InstanceBlock { instance_id, instance } + } + } + } + ) +} + +#[component] +fn InstanceBlock(instance_id: String, instance: config::Instance) -> Element { + let icon = instance.get_icon().map(dynamic_bytes); + + rsx!( + rect { + padding: "8", + corner_radius: "8", + width: "128", + background: THEME.read().bg_secondary, + direction: "vertical", + cross_align: "center", + main_align: "center", + overflow: "clip", + + + if icon.is_some() { + image { + width: "48", + height: "48", + image_data: icon, + } + } + + label { + max_lines: "1", + text_overflow: "ellipsis", + "{instance.name}" + } + } + ) +} diff --git a/crates/gui/src/components/mod.rs b/crates/gui/src/components/mod.rs new file mode 100644 index 0000000..458485b --- /dev/null +++ b/crates/gui/src/components/mod.rs @@ -0,0 +1,10 @@ +pub mod home_page; +pub mod mod_from_template_page; +pub mod mod_page; +pub mod profile_page; +pub mod settings_page; + +pub mod sidebar; + +pub mod button; +pub mod popup; diff --git a/crates/gui/src/components/mod_from_template_page.rs b/crates/gui/src/components/mod_from_template_page.rs new file mode 100644 index 0000000..f6f9c94 --- /dev/null +++ b/crates/gui/src/components/mod_from_template_page.rs @@ -0,0 +1,161 @@ +use freya::prelude::*; +use modloader_core::{config, paths, updater}; + +use crate::components::button::Button; +use crate::{AppPage, CONFIG, CURRENT_PAGE, THEME, assets}; + +#[component] +pub fn ModFromTemplatePage() -> Element { + let templates = assets::ModTemplates::get_all(); + + rsx!(rect { + direction: "vertical", + padding: "8", + + Header { } + + ScrollView { + direction: "vertical", + scrollbar_theme: theme_with!(ScrollBarTheme { + background: THEME.read().bg_tertiary.into(), + }), + + for template in templates { + rect { + direction: "horizontal", + spacing: "8", + content: "fit", + + if let Some(ref link) = template.support_link { + SupportButton { link } + } + + ModTemplateCard { template: template.clone() } + } + } + } + }) +} + +#[component] +fn SupportButton(link: String) -> Element { + let support_icon = static_bytes(assets::CIRCLE_HELP_ICON); + + rsx!( + Button { + width: "64", + height: "64", + padding: "8", + margin: "8 0", + corner_radius: "8", + base_color: THEME.read().bg_primary, + + onpress: move |_| { let _ = open::that(&link); }, + + svg { + width: "48", + height: "48", + + fill: THEME.read().text_primary, + + svg_data: support_icon, + } + } + ) +} + +#[component] +fn ModTemplateCard(template: assets::ModTemplate) -> Element { + let config_template = template.config.clone(); + + let onpress = move || { + let uuid = uuid::Uuid::new_v4().to_string(); + let mod_path = paths::config_mods_dir().join(format!("{}.toml", uuid)); + + let mod_toml = toml::to_string(&config::ModConfig { + r#mod: config_template.clone(), + }) + .unwrap(); + + let _ = std::fs::write(mod_path, mod_toml); + + *CONFIG.write() = config::Config::init(); + + *CURRENT_PAGE.write() = AppPage::Mod(uuid); + + if let Some(updater) = &config_template.updater { + let _ = updater::update(updater, true); + } + }; + + // BUG: The button stretches off the screen for some reason. + // Possibly an issue with layout calculation since the button is rendered in a for loop? + // It's not harmful, but just a bit weird. + rsx!(Button { + height: "64", + padding: "8", + margin: "8 0", + corner_radius: "8", + base_color: THEME.read().bg_primary, + + onpress: move |_| { onpress(); }, + + cross_align: "center", + + spacing: "12", + + if let Some(icon) = template.icon { + match icon { + assets::ModTemplateIcon::Svg(svg) => { + let image = static_bytes(svg); + rsx!(svg { + width: "48", + height: "48", + + fill: THEME.read().text_primary, + + svg_data: image, + }) + } + assets::ModTemplateIcon::Image(image) => { + let image = static_bytes(image); + rsx!(image { + width: "48", + height: "48", + + image_data: image, + }) + } + } + } + + label { + font_size: "18", + + "{template.config.name}" + } + }) +} + +#[component] +fn Header() -> Element { + rsx!(rect { + width: "100%", + padding: "12", + corner_radius: "8", + border: "0 0 2 0 outer {THEME.read().bg_secondary}", + + background: THEME.read().bg_primary, + + direction: "horizontal", + cross_align: "center", + + spacing: "12", + + label { + font_size: "18", + + "Create a new mod from a template" + } + }) +} diff --git a/crates/gui/src/components/mod_page.rs b/crates/gui/src/components/mod_page.rs new file mode 100644 index 0000000..ed0f74a --- /dev/null +++ b/crates/gui/src/components/mod_page.rs @@ -0,0 +1,666 @@ +use freya::prelude::*; +use strum::IntoEnumIterator as _; + +use crate::components::button::Button; +use crate::utils::icons::GetIcon; +use crate::{CONFIG, POPUP_STATE, PopupState, THEME}; +use modloader_core::{config, paths}; + +type ModContext = Signal<(String, config::Mod)>; + +fn get_mod_by_id(mod_id: &str) -> (String, config::Mod) { + CONFIG + .read() + .mods + .get(mod_id) + .map(|mod_| (mod_id.to_string(), mod_.clone())) + .unwrap_or_else(|| { + let mod_ = config::Mod { + name: "New Mod".into(), + ..Default::default() + }; + (mod_id.to_string(), mod_) + }) +} + +#[component] +pub fn ModPage(mod_id: String) -> Element { + let mut mod_: ModContext = use_signal(|| get_mod_by_id(&mod_id)); + + use_context_provider(|| mod_); + + use_effect(use_reactive(&mod_id, move |mod_id| { + *mod_.write() = get_mod_by_id(&mod_id); + })); + + rsx!(rect { + direction: "vertical", + + padding: "8", + + Header { + on_delete_pressed: move |_| { + *POPUP_STATE.write() = PopupState::ConfirmDeleteMod(mod_id.clone()); + } + } + + Form { } + }) +} + +#[component] +fn Header(on_delete_pressed: EventHandler<()>) -> Element { + let ctx = use_context::(); + let (_mod_id, mod_) = &*ctx.read(); + + let mod_icon = mod_.get_icon().map(dynamic_bytes); + + rsx!(rect { + width: "100%", + padding: "12 8 12 12", + corner_radius: "8", + border: "0 0 2 0 outer {THEME.read().bg_secondary}", + + background: THEME.read().bg_primary, + + direction: "horizontal", + cross_align: "center", + + spacing: "12", + + if mod_icon.is_some() { + image { + width: "48", + height: "48", + image_data: mod_icon, + } + } + + label { + font_size: "18", + + "{mod_.name}" + } + + rect { + width: "fill", + direction: "horizontal", + main_align: "end", + spacing: "8", + + Button { + width: "48", + height: "48", + stretch: false, + padding: "{48/4}", + corner_radius: "{48/2}", + main_align: "center", + cross_align: "center", + + onpress: move |_| { + *CONFIG.write() = config::Config::init(); + }, + + svg { + width: "24", + height: "24", + svg_data: static_bytes(crate::assets::REFRESH_ARROWS_ICON), + fill: THEME.read().text_primary, + } + } + + Button { + width: "48", + height: "48", + stretch: false, + padding: "{48/4}", + corner_radius: "{48/2}", + main_align: "center", + cross_align: "center", + + onpress: move |_| { + let mod_path = paths::config_mods_dir().join(format!("{}.toml", &ctx.read().0)); + + if mod_path.exists() { + if let Err(e) = open::that(mod_path) { + eprintln!("Failed to open directory: {}", e); + } + } else if let Some(parent) = mod_path.parent() { + if !parent.exists() { + if let Err(e) = std::fs::create_dir_all(parent) { + eprintln!("Failed to create directory: {}", e); + } + } + if let Err(e) = open::that(parent) { + eprintln!("Failed to open directory: {}", e); + } + } + }, + + svg { + width: "24", + height: "24", + svg_data: static_bytes(crate::assets::PEN_TO_SQUARE_ICON), + fill: THEME.read().text_primary, + } + } + + Button { + width: "48", + height: "48", + stretch: false, + padding: "{48/4}", + corner_radius: "{48/2}", + main_align: "center", + cross_align: "center", + + target_color: THEME.read().bg_danger, + + onpress: move |_| on_delete_pressed.call(()), + + svg { + width: "24", + height: "24", + svg_data: static_bytes(crate::assets::TRASH_ICON), + fill: THEME.read().text_primary, + } + } + } + }) +} + +#[component] +fn Form() -> Element { + let ctx = use_context::(); + + let mut mod_id = use_signal(|| ctx.read().0.clone()); + let mut mod_form = use_signal(|| ctx.read().1.clone()); + let mut dist_files_form = use_signal(|| { + ctx.read() + .1 + .updater + .clone() + .map(|u| u.dist_file_names.join(", ")) + .unwrap_or_default() + }); + + use_effect(move || { + *mod_form.write() = ctx.read().1.clone(); + *mod_id.write() = ctx.read().0.clone(); + *dist_files_form.write() = ctx + .read() + .1 + .updater + .clone() + .map(|u| u.dist_file_names.join(", ")) + .unwrap_or_default(); + }); + + let update = move || { + let mod_id = ctx.read().0.clone(); + let mod_ = mod_form.read().clone(); + + let mod_toml = toml::to_string::(&config::ModConfig { r#mod: mod_ }); + + if let Ok(mod_toml) = mod_toml { + let mod_path = paths::config_mods_dir().join(format!("{}.toml", mod_id)); + let _ = std::fs::write(mod_path, mod_toml); + } + + *CONFIG.write() = config::Config::init(); + }; + + rsx!(rect { + margin: "8 8 0 0", + + Button { + corner_radius: "8", + stretch: false, + + selected_color: THEME.read().bg_success, + selected: *mod_id.read() == ctx.read().0 && *mod_form.read() != ctx.read().1, + + onpress: move |_| { update(); }, + + svg { + width: "20", + height: "20", + svg_data: static_bytes(crate::assets::FLOPPY_DISK_ICON), + fill: THEME.read().text_primary, + } + + label { + "Save" + } + } + + ScrollView { + scrollbar_theme: theme_with!(ScrollBarTheme { + background: THEME.read().bg_tertiary.into(), + }), + + label { + margin: "8", + font_size: "18", + "Basic Mod Configuration" + } + + // Basic Mod Configuration + rect { + background: THEME.read().bg_primary, + corner_radius: "8", + padding: "8", + + spacing: "8", + + // Mod Name + rect { + width: "100%", + label { + margin: "4", + "Mod Name" + } + + Input { + value: "{mod_form().name}", + onvalidate: |v: InputValidator| v.set_valid(v.text().len() <= 32), + onchange: move |e| mod_form.write().name = e, + placeholder: "Mod Name", + theme: theme_with!(InputTheme { + width: "100%".into(), + corner_radius: "8".into(), + border_fill: THEME.read().bg_secondary.into(), + font_theme: theme_with!(FontTheme { + color: THEME.read().text_primary.into(), + }), + background: THEME.read().bg_tertiary.into(), + hover_background: THEME.read().bg_tertiary.into(), + }) + } + } + + // Mod Entrypoint + rect { + width: "100%", + label { + margin: "4", + "Entrypoint File" + } + + label { + margin: "4", + color: THEME.read().text_secondary, + "This is the file that initialises your mod. Usually called something like injector.js or patcher.js" + } + + Input { + value: "{mod_form().entrypoint}", + onchange: move |e: String| { + if e.len() > 64 { + return; + } + mod_form.write().entrypoint = e; + }, + placeholder: "injector.js", + theme: theme_with!(InputTheme { + width: "100%".into(), + corner_radius: "8".into(), + border_fill: THEME.read().bg_secondary.into(), + font_theme: theme_with!(FontTheme { + color: THEME.read().text_primary.into(), + }), + background: THEME.read().bg_tertiary.into(), + hover_background: THEME.read().bg_tertiary.into(), + }) + } + } + + // Mod Path + rect { + width: "100%", + label { + margin: "4", + "Path to the mod's build folder (leave empty to use the auto-updater)" + } + + Input { + value: "{mod_form().path.unwrap_or_default()}", + onchange: move |e: String| { + if e.is_empty() { + mod_form.write().path = None; + } else { + mod_form.write().path = Some(e); + } + }, + placeholder: "/path/to/moonlight", + theme: theme_with!(InputTheme { + width: "100%".into(), + corner_radius: "8".into(), + border_fill: THEME.read().bg_secondary.into(), + font_theme: theme_with!(FontTheme { + color: THEME.read().text_primary.into(), + }), + background: THEME.read().bg_tertiary.into(), + hover_background: THEME.read().bg_tertiary.into(), + }) + } + } + + // Icon Name + rect { + width: "100%", + label { + margin: "4", + "Icon Filename (leave empty to use the auto-updater's icon)" + } + + label { + margin: "4", + color: THEME.read().text_secondary, + "Place your icon in the root of the icons folder and enter it's name here." + } + + Input { + value: "{mod_form().icon.unwrap_or_default()}", + onchange: move |e: String| { + if e.is_empty() { + mod_form.write().icon = None; + } else { + mod_form.write().icon = Some(e); + } + }, + placeholder: "moonlight.png", + theme: theme_with!(InputTheme { + width: "100%".into(), + corner_radius: "8".into(), + border_fill: THEME.read().bg_secondary.into(), + font_theme: theme_with!(FontTheme { + color: THEME.read().text_primary.into(), + }), + background: THEME.read().bg_tertiary.into(), + hover_background: THEME.read().bg_tertiary.into(), + }) + } + + + rect { + width: "fill", + direction: "horizontal", + main_align: "end", + + Button { + stretch: false, + padding: "8 16", + margin: "8 0 0 0", + corner_radius: "8", + main_align: "center", + height: "42", + + onpress: move |_| { + let _ = open::that(paths::config_icons_dir()); + }, + + label { + font_size: "16", + + "Open Icons Folder" + } + } + } + } + } + + label { + margin: "8", + font_size: "18", + "Auto Updater (optional)" + } + + // Auto Updater Configuration + rect { + background: THEME.read().bg_primary, + corner_radius: "8", + padding: "8", + spacing: "8", + + // Auto Update + rect { + width: "100%", + label { + margin: "4", + "Enable automatic updater?" + } + + label { + margin: "4", + color: THEME.read().text_secondary, + "If disabled, you will need to use your mod's auto-updater instead." + } + + Tile { + onselect: move |_| { + let mut new_updater = mod_form().updater.unwrap_or_default(); + new_updater.auto_update = !new_updater.auto_update; + mod_form.write().updater = Some(new_updater); + }, + leading: rsx!(Checkbox { + selected: mod_form().updater.map(|u| u.auto_update).unwrap_or_default() + }), + label { + "Enable auto-updater" + } + } + } + + if mod_form().updater.unwrap_or_default().auto_update { + // Always Update + rect { + width: "100%", + label { + margin: "4", + "Ask before applying updates?" + } + + Tile { + onselect: move |_| { + let mut new_updater = mod_form().updater.unwrap_or_default(); + new_updater.ask_before_update = !new_updater.ask_before_update; + mod_form.write().updater = Some(new_updater); + }, + leading: rsx!(Checkbox { + selected: mod_form().updater.map(|u| u.ask_before_update).unwrap_or_default() + }), + label { + "Always ask before updating" + } + } + } + } + + // GitHub Organisation + rect { + width: "100%", + label { + margin: "4", + "GitHub Organisation" + } + + Input { + value: "{mod_form().updater.map(|u| u.github_org).unwrap_or_default()}", + onchange: move |e: String| { + let mut new_updater = mod_form().updater.unwrap_or_default(); + new_updater.github_org = e.clone(); + + mod_form.write().updater = Some(new_updater); + + }, + placeholder: "moonlight-mod", + theme: theme_with!(InputTheme { + width: "100%".into(), + corner_radius: "8".into(), + border_fill: THEME.read().bg_secondary.into(), + font_theme: theme_with!(FontTheme { + color: THEME.read().text_primary.into(), + }), + background: THEME.read().bg_tertiary.into(), + hover_background: THEME.read().bg_tertiary.into(), + }) + } + } + + // GitHub Repository + rect { + width: "100%", + label { + margin: "4", + "GitHub Repository" + } + + Input { + value: "{mod_form().updater.map(|u| u.github_repo).unwrap_or_default()}", + onchange: move |e: String| { + let mut new_updater = mod_form().updater.unwrap_or_default(); + new_updater.github_repo = e.clone(); + + mod_form.write().updater = Some(new_updater); + + }, + placeholder: "moonlight", + theme: theme_with!(InputTheme { + width: "100%".into(), + corner_radius: "8".into(), + border_fill: THEME.read().bg_secondary.into(), + font_theme: theme_with!(FontTheme { + color: THEME.read().text_primary.into(), + }), + background: THEME.read().bg_tertiary.into(), + hover_background: THEME.read().bg_tertiary.into(), + }) + } + } + + // Release File Type + rect { + width: "100%", + label { + margin: "4", + "Release File Type" + } + + Dropdown { + value: "{mod_form().updater.map(|u| u.dist_file_type).unwrap_or_default()}", + + theme: theme_with!(DropdownTheme { + border_fill: THEME.read().bg_secondary.into(), + focus_border_fill: THEME.read().blurple.into(), + font_theme: theme_with!(FontTheme { + color: THEME.read().text_primary.into(), + }), + background_button: THEME.read().bg_tertiary.into(), + hover_background: THEME.read().bg_tertiary.into(), + dropdown_background: THEME.read().bg_tertiary.into(), + }), + + for i in config::DistFileType::iter() { + DropdownItem { + value: i.to_string(), + + theme: theme_with!(DropdownItemTheme { + font_theme: theme_with!(FontTheme { + color: THEME.read().text_primary.into(), + }), + background: THEME.read().bg_tertiary.into(), + select_background: THEME.read().bg_primary.into(), + hover_background: THEME.read().bg_primary.into(), + }), + + onpress: { + to_owned![i]; + move |_| { + let mut new_updater = mod_form().updater.unwrap_or_default(); + new_updater.dist_file_type = i.clone(); + + mod_form.write().updater = Some(new_updater); + } + }, + + label { + "{i}" + } + } + } + } + } + + // Release File Name + rect { + width: "100%", + label { + margin: "4", + "Release File Names (The file(s) to download from GitHub Releases) Separate with commas if downloading multiple." + } + + Input { + value: "{dist_files_form()}", + onchange: move |e: String| { + *dist_files_form.write() = e.clone(); + + let split = e.split(", ").map(|s| s.trim().to_string()); + + let mut new_updater = mod_form().updater.unwrap_or_default(); + new_updater.dist_file_names = split.collect(); + + mod_form.write().updater = Some(new_updater); + + }, + placeholder: "dist.tar.gz", + theme: theme_with!(InputTheme { + width: "100%".into(), + corner_radius: "8".into(), + border_fill: THEME.read().bg_secondary.into(), + font_theme: theme_with!(FontTheme { + color: THEME.read().text_primary.into(), + }), + background: THEME.read().bg_tertiary.into(), + hover_background: THEME.read().bg_tertiary.into(), + }) + } + } + + // Icon URL + rect { + width: "100%", + label { + margin: "4", + "Icon URL (Optional, automatically download the latest icon from a URL)" + } + + Input { + value: "{mod_form().updater.map(|u| u.icon_url.unwrap_or_default()).unwrap_or_default()}", + onchange: move |e: String| { + let mut new_updater = mod_form().updater.unwrap_or_default(); + if e.is_empty() { + new_updater.icon_url = None; + } else { + new_updater.icon_url = Some(e.clone()); + } + mod_form.write().updater = Some(new_updater); + + }, + placeholder: "https://raw.githubusercontent.com/moonlight-mod/moonlight-mod.github.io/main/src/img/logo.png", + theme: theme_with!(InputTheme { + width: "100%".into(), + corner_radius: "8".into(), + border_fill: THEME.read().bg_secondary.into(), + font_theme: theme_with!(FontTheme { + color: THEME.read().text_primary.into(), + }), + background: THEME.read().bg_tertiary.into(), + hover_background: THEME.read().bg_tertiary.into(), + }) + } + } + } + } + }) +} diff --git a/crates/gui/src/components/popup.rs b/crates/gui/src/components/popup.rs new file mode 100644 index 0000000..5c4f1d1 --- /dev/null +++ b/crates/gui/src/components/popup.rs @@ -0,0 +1,128 @@ +use freya::prelude::*; + +use crate::components::button::Button; +use crate::{THEME, utils::hoverable::hoverable}; + +#[component] +pub fn ConfirmationPopup( + title: String, + body: String, + on_confirm: EventHandler<()>, + on_cancel: EventHandler<()>, +) -> Element { + use_focus().focus(); + + rsx!(Popup { + theme: theme_with!(PopupTheme { + background: THEME.read().bg_primary.into(), + color: THEME.read().text_primary.into(), + height: "auto".into(), + }), + close_on_escape_key: true, + + oncloserequest: move |_| { + on_cancel.call(()); + }, + PopupTitle { + label { + "{title}" + }, + }, + PopupContent { + label { + "{body}" + }, + + rect { + margin: "8", + spacing: "8", + direction: "horizontal", + width: "fill", + height: "auto", + cross_align: "end", + main_align: "end", + + Button { + stretch: false, + onpress: move |_| { + on_cancel.call(()); + }, + label { + "Cancel" + }, + }, + + Button { + stretch: false, + base_color: THEME.read().bg_danger, + onpress: move |_| { + on_confirm.call(()); + }, + + label { + "Confirm" + } + } + }, + }, + }) +} + +#[component] +fn PopupButton( + name: String, + onclick: EventHandler, + icon: Option<&'static [u8]>, + color: Option, +) -> Element { + let animation = hoverable!(move |_conf| { + AnimColor::new( + if let Some(ref color) = color { + color + } else { + THEME.read().bg_secondary + }, + THEME.read().blurple, + ) + .ease(Ease::InOut) + .time(100) + }); + + let bg_color = animation.animation.get().read().read(); + + let icon = icon.map(static_bytes); + + rsx!(rect { + // width: "100%", + padding: "8 16", + corner_radius: "8", + direction: "horizontal", + cross_align: "center", + spacing: "8", + height: "42", + color: THEME.read().text_primary, + background: bg_color, + onmouseenter: animation.onmouseenter, + onmouseleave: animation.onmouseleave, + + onclick: { + #[allow(clippy::redundant_closure)] + move |e| onclick(e) + }, + + if icon.is_some() { + svg { + width: "24", + height: "24", + svg_data: icon, + fill: "#ffffff", + } + } + + label { + font_size: "16", + + "{name}" + } + }) +} diff --git a/crates/gui/src/components/profile_page.rs b/crates/gui/src/components/profile_page.rs new file mode 100644 index 0000000..cae1f99 --- /dev/null +++ b/crates/gui/src/components/profile_page.rs @@ -0,0 +1,735 @@ +use freya::prelude::*; +use modloader_core::utils::{find_running_instances, launch_detached_instance}; +use modloader_core::{config, paths}; + +use crate::components::button::Button; +use crate::utils::icons::GetIcon; +use crate::{CONFIG, POPUP_STATE, PopupState, REFRESH_PIDS, THEME}; + +type ProfileContext = Memo<(String, config::ProfileConfig)>; + +fn get_profile_by_id(profile_id: &str) -> (String, config::ProfileConfig) { + CONFIG + .read() + .profiles + .get(profile_id) + .map(|profile| (profile_id.to_string(), profile.clone())) + .unwrap_or_else(|| { + let profile = config::Profile { + name: "New Profile".to_string(), + ..Default::default() + }; + (profile_id.to_string(), config::ProfileConfig { + profile, + ..Default::default() + }) + }) +} + +#[component] +pub fn ProfilePage(profile_id: String) -> Element { + let profile_id = use_memo(use_reactive!(|(profile_id,)| profile_id)); + let profile: ProfileContext = use_memo(move || get_profile_by_id(&profile_id())); + use_context_provider(|| profile); + + let mut active_pids = use_signal(Vec::new); + + // When the PIDs refresh, update the active PIDs list in the current component. + use_effect(move || { + REFRESH_PIDS.read(); + let (profile_id_, profile_) = profile(); + spawn(async move { + if profile().0 == profile_id_ { + *active_pids.write() = find_running_instances(&profile_id_, &profile_); + } + }); + }); + + rsx!(rect { + direction: "vertical", + padding: "8", + spacing: "8", + Header { + on_delete_pressed: move |_| { + *POPUP_STATE.write() = PopupState::ConfirmDeleteProfile(profile_id()); + } + } + + if active_pids.len() > 0 { + ActiveInstance { + active_pids: active_pids, + } + } + + ScrollView { + direction: "vertical", + spacing: "8", + scrollbar_theme: theme_with!(ScrollBarTheme { + background: THEME.read().bg_tertiary.into(), + }), + + for (instance_id, instance) in profile().1.instances.iter() { + InstanceCard { + instance_id: instance_id.clone(), + instance: instance.clone() + } + } + + rect { + width: "fill", + direction: "horizontal", + main_align: "end", + + Button { + stretch: false, + + onpress: move |_| { + if let Some(profile) = CONFIG.write().profiles.get_mut(&profile_id()) { + profile.instances.insert(uuid::Uuid::new_v4().to_string(), config::Instance { + mod_id: "Unknown Mod".to_string(), + name: "New Instance".to_string(), + icon: None, + starred: false, + }); + } + }, + + svg { + width: "24", + height: "24", + svg_data: static_bytes(crate::assets::PLUS_ICON), + fill: THEME.read().text_primary, + } + + label { + font_size: "16", + "Add New Instance" + } + } + } + } + }) +} + +#[component] +fn ActiveInstance(active_pids: Signal>) -> Element { + let ctx: ProfileContext = use_context(); + let profile_id = ctx().0.to_string(); + + rsx!(rect { + height: "64", + width: "fill", + padding: "8", + corner_radius: "8", + + background: THEME.read().bg_primary, + + direction: "horizontal", + spacing: "8", + main_align: "start", + cross_align: "center", + + label { + "There are active instances using this profile." + } + + rect { + width: "fill", + direction: "horizontal", + spacing: "8", + main_align: "end", + + Button { + height: "48", + stretch: false, + corner_radius: "8", + direction: "horizontal", + main_align: "center", + cross_align: "center", + + target_color: THEME.read().bg_danger, + + onpress: move |_| { + *POPUP_STATE.write() = PopupState::ConfirmKillProfile(profile_id.clone()); + }, + + svg { + width: "24", + height: "24", + svg_data: static_bytes(crate::assets::STOP_ICON), + fill: THEME.read().text_primary, + } + + label { + font_size: "16", + "Kill Active Instance" + } + } + } + }) +} + +#[component] +fn InstanceCard(instance_id: String, instance: config::Instance) -> Element { + let instance_id = use_memo(use_reactive(&instance_id, |i| i)); + let instance = use_memo(use_reactive(&instance, |i| i)); + + let ctx: ProfileContext = use_context(); + let profile_id = use_memo(move || ctx().0); + let profile = use_memo(move || ctx().1); + let icon = use_memo(move || instance().get_icon().map(dynamic_bytes)); + + let mut edit_mode = use_signal(|| false); + let mut starred = use_signal(|| instance().starred); + + use_effect(move || { + instance_id.read(); + edit_mode.set(false); + }); + + // use_effect(use_reactive(&instance_id, move |instance_id| { + // let profile = ctx.read().1.clone(); + // let instance = profile.instances.get(&instance_id).cloned(); + // *edit_mode.write() = false; + // if let Some(instance) = instance { + // *starred.write() = instance.starred; + // } + // })); + + rsx!(rect { + direction: "vertical", + spacing: "8", + corner_radius: "8", + + background: THEME.read().bg_primary, + padding: "8", + + rect { + height: "64", + width: "fill", + + direction: "horizontal", + spacing: "8", + main_align: "start", + cross_align: "center", + + if let Some(icon) = icon() { + image { + width: "48", + height: "48", + image_data: icon, + } + } + + label { + "{instance().name}" + } + + rect { + width: "fill", + direction: "horizontal", + spacing: "8", + main_align: "end", + + Button { + width: "48", + height: "48", + stretch: false, + padding: "{48/4}", + corner_radius: "{48/2}", + main_align: "center", + cross_align: "center", + + target_color: THEME.read().bg_success, + + onpress: { + move |_| { + let args = profile().discord.args; + + if !find_running_instances(&profile_id(), &profile()).is_empty() { + *POPUP_STATE.write() = PopupState::InstanceAlreadyRunning(profile_id(), instance_id(), args); + } else { + let _ = launch_detached_instance(&profile_id(), &instance_id(), &args, false); + } + } + }, + + svg { + width: "24", + height: "24", + svg_data: static_bytes(crate::assets::PLAY_ICON), + fill: THEME.read().text_primary, + } + } + + Button { + width: "48", + height: "48", + stretch: false, + padding: "{48/4}", + corner_radius: "{48/2}", + main_align: "center", + cross_align: "center", + + target_color: THEME.read().blurple, + + onpress: move |_| { + let current = *edit_mode.read(); + *edit_mode.write() = !current; + }, + + svg { + width: "24", + height: "24", + svg_data: if *edit_mode.read() { + static_bytes(crate::assets::CHEVRON_UP_ICON) + } else { + static_bytes(crate::assets::PENCIL_ICON) + }, + fill: THEME.read().text_primary, + } + } + + Button { + width: "48", + height: "48", + stretch: false, + padding: "{48/4}", + corner_radius: "{48/2}", + main_align: "center", + cross_align: "center", + + target_color: THEME.read().bg_danger, + + // TODO: Really need to standardise writing/updating/loading configs. This is messy. + onpress: move |_| { + starred.set(!starred()); + + let Some(mut profile) = CONFIG.read().profiles.get(&profile_id()).cloned() else { + return; + }; + + if let Some(instance) = profile.instances.get_mut(&instance_id()) { instance.starred = starred(); } + + let config_toml = toml::to_string::(&profile); + + if let Ok(config_toml) = config_toml { + let instance_path = paths::config_profile_dir().join(format!("{}.toml", &profile_id)); + let _ = std::fs::write(instance_path, config_toml); + } + + *CONFIG.write() = config::Config::init(); + }, + + svg { + width: "28", + height: "28", + svg_data: if starred() { + static_bytes(crate::assets::STAR_FILLED_ICON) + } else { + static_bytes(crate::assets::STAR_HOLLOW_ICON) + }, + fill: if starred() { + THEME.read().star_yellow + } else { + THEME.read().text_primary + }, + } + } + } + } + + if edit_mode() { + InstanceEdit { + instance_id: instance_id(), + instance: instance() + } + } + }) +} + +// Dropdown doesn't let you use a custom string - only showing the value (key). +// So we made a struct that implements Display to render the name rather than the key. +#[derive(Clone, PartialEq)] +struct SelectedId(String, String); + +impl std::fmt::Display for SelectedId { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.1) + } +} + +#[component] +fn InstanceEdit(instance_id: String, instance: config::Instance) -> Element { + let profile: ProfileContext = use_context(); + let profile_id = use_signal(|| profile().0); + + // make instance_id and instance reactive + let instance_id = use_memo(use_reactive(&instance_id, |instance_id| instance_id)); + let instance = use_memo(use_reactive(&instance, |instance| instance)); + + let mods = use_signal(|| CONFIG.read().mods.clone()); + + let mut name = use_signal(|| instance().name); + let mut icon = use_signal(|| instance().icon); + let mut selected_mod = use_signal(|| { + mods.read() + .get(&instance().mod_id) + .map(|m| SelectedId(instance().mod_id, m.name.to_string())) + .unwrap_or(SelectedId( + "Unknown Mod".to_string(), + "Unknown Mod".to_string(), + )) + }); + + let update = { + to_owned![instance_id]; + move || { + let mut profile = profile.read().1.clone(); + let mod_id = selected_mod.read().0.clone(); + let icon = icon().clone(); + let name = name().clone(); + + profile + .instances + .entry(instance_id()) + .and_modify(|instance| { + instance.name = name.clone(); + instance.mod_id = mod_id.clone(); + instance.icon = icon.clone(); + }) + .or_insert(config::Instance { + name, + mod_id, + icon, + starred: false, + }); + + profile.save(&profile_id()); + + CONFIG.write().sync(); + } + }; + + rsx!(rect { + width: "fill", + direction: "vertical", + spacing: "8", + + label { + font_size: "16", + "Name of this instance" + } + + Input { + value: name(), + onchange: move |e| { name.set(e) }, + placeholder: "New Instance", + theme: theme_with!(InputTheme { + width: "100%".into(), + corner_radius: "8".into(), + border_fill: THEME.read().bg_secondary.into(), + font_theme: theme_with!(FontTheme { + color: THEME.read().text_primary.into(), + }), + background: THEME.read().bg_tertiary.into(), + hover_background: THEME.read().bg_tertiary.into(), + }) + } + + label { + font_size: "16", + "Mod to use for this instance" + } + + Dropdown { + value: selected_mod(), + + theme: theme_with!(DropdownTheme { + border_fill: THEME.read().bg_secondary.into(), + focus_border_fill: THEME.read().blurple.into(), + font_theme: theme_with!(FontTheme { + color: THEME.read().text_primary.into(), + }), + background_button: THEME.read().bg_tertiary.into(), + hover_background: THEME.read().bg_tertiary.into(), + dropdown_background: THEME.read().bg_tertiary.into(), + }), + + for (key, value) in mods() {{ + let id = SelectedId(key.clone(), value.name.clone()); + + rsx!(DropdownItem { + value: id.clone(), + + theme: theme_with!(DropdownItemTheme { + font_theme: theme_with!(FontTheme { + color: THEME.read().text_primary.into(), + }), + background: THEME.read().bg_tertiary.into(), + select_background: THEME.read().bg_primary.into(), + hover_background: THEME.read().bg_primary.into(), + }), + + onpress: move |_| { selected_mod.set(id.clone()) }, + + label { + "{value.name}" + } + }) + }} + } + + // Icon Name + rect { + width: "100%", + label { + margin: "4", + "Icon Filename (leave empty to use the mod's icon)" + } + + label { + margin: "4", + color: THEME.read().text_secondary, + "Place your icon in the root of the icons folder and enter it's name here." + } + + Input { + value: icon().unwrap_or_default(), + onchange: move |e: String| { + icon.set(if e.is_empty() { None } else { Some(e) }); + }, + placeholder: "moonlight.png", + theme: theme_with!(InputTheme { + width: "100%".into(), + corner_radius: "8".into(), + border_fill: THEME.read().bg_secondary.into(), + font_theme: theme_with!(FontTheme { + color: THEME.read().text_primary.into(), + }), + background: THEME.read().bg_tertiary.into(), + hover_background: THEME.read().bg_tertiary.into(), + }) + } + + Button { + stretch: false, + padding: "8 16", + margin: "8 0 0 0", + corner_radius: "8", + main_align: "center", + height: "42", + + onpress: move |_| { + let _ = open::that(paths::config_icons_dir()); + }, + + label { + font_size: "16", + + "Open Icons Folder" + } + } + } + + rect { + width: "fill", + direction: "horizontal", + main_align: "end", + spacing: "8", + + Button { + stretch: false, + height: "48", + corner_radius: "8", + target_color: THEME.read().bg_success, + + selected_color: THEME.read().bg_success, + selected: { + let icon_match = icon().as_deref() != instance().icon.as_deref(); + let mod_match = selected_mod().0.ne(&instance().mod_id); + icon_match || mod_match + }, + + onpress: move |_| { update(); }, + + svg { + width: "20", + height: "20", + svg_data: static_bytes(crate::assets::FLOPPY_DISK_ICON), + fill: THEME.read().text_primary, + } + + label { + font_size: "16", + "Save Changes" + } + } + + Button { + stretch: false, + height: "48", + corner_radius: "8", + target_color: THEME.read().bg_danger, + + onpress: { + move |_| { + *POPUP_STATE.write() = PopupState::ConfirmDeleteInstance(profile_id(), instance_id()); + } + }, + + svg { + width: "20", + height: "20", + svg_data: static_bytes(crate::assets::TRASH_ICON), + fill: THEME.read().text_primary, + } + + label { + font_size: "16", + "Delete Instance" + } + } + } + }) +} + +#[component] +fn Header(on_delete_pressed: EventHandler<()>) -> Element { + let ctx = use_context::(); + let (profile_id, profile) = ctx.read().clone(); + + let mut edit_mode = use_signal(|| false); + use_effect(use_reactive(&profile_id, move |_profile_id| { + *edit_mode.write() = false; + })); + + rsx!(rect { + width: "100%", + padding: "12 8 12 12", + corner_radius: "8", + border: "0 0 2 0 outer {THEME.read().bg_secondary}", + + background: THEME.read().bg_primary, + + direction: "horizontal", + cross_align: "center", + + spacing: "12", + + label { + font_size: "18", + + "{profile.profile.name}" + } + + rect { + width: "fill", + direction: "horizontal", + main_align: "end", + spacing: "8", + + Button { + width: "48", + height: "48", + stretch: false, + padding: "{48/4}", + corner_radius: "{48/2}", + main_align: "center", + cross_align: "center", + + onpress: move |_| { + *CONFIG.write() = config::Config::init(); + }, + + svg { + width: "24", + height: "24", + svg_data: static_bytes(crate::assets::REFRESH_ARROWS_ICON), + fill: THEME.read().text_primary, + } + } + + Button { + width: "48", + height: "48", + stretch: false, + padding: "{48/4}", + corner_radius: "{48/2}", + main_align: "center", + cross_align: "center", + + onpress: move |_| { + *edit_mode.write() = !edit_mode(); + }, + + svg { + width: "24", + height: "24", + svg_data: if edit_mode() { + static_bytes(crate::assets::CHEVRON_UP_ICON) + } else { + static_bytes(crate::assets::PENCIL_ICON) + }, + fill: THEME.read().text_primary, + } + } + + Button { + width: "48", + height: "48", + stretch: false, + padding: "{48/4}", + corner_radius: "{48/2}", + main_align: "center", + cross_align: "center", + + onpress: move |_| { + let profile_path = paths::config_profile_dir().join(format!("{}.toml", &profile_id)); + if profile_path.exists() { + if let Err(e) = open::that(profile_path) { + eprintln!("Failed to open directory: {}", e); + } + } else if let Some(parent) = profile_path.parent() { + if !parent.exists() { + if let Err(e) = std::fs::create_dir_all(parent) { + eprintln!("Failed to create directory: {}", e); + } + } + if let Err(e) = open::that(parent) { + eprintln!("Failed to open directory: {}", e); + } + } + }, + + svg { + width: "24", + height: "24", + svg_data: static_bytes(crate::assets::PEN_TO_SQUARE_ICON), + fill: THEME.read().text_primary, + } + } + + Button { + width: "48", + height: "48", + stretch: false, + padding: "{48/4}", + corner_radius: "{48/2}", + main_align: "center", + cross_align: "center", + + target_color: THEME.read().bg_danger, + + onpress: move |_| on_delete_pressed.call(()), + + svg { + width: "24", + height: "24", + svg_data: static_bytes(crate::assets::TRASH_ICON), + fill: THEME.read().text_primary, + } + } + } + }) +} diff --git a/crates/gui/src/components/settings_page.rs b/crates/gui/src/components/settings_page.rs new file mode 100644 index 0000000..3b4fce3 --- /dev/null +++ b/crates/gui/src/components/settings_page.rs @@ -0,0 +1,10 @@ +use freya::prelude::*; + +#[component] +pub fn SettingsPage() -> Element { + rsx!( + label { + "SettingsPage Not Implemented" + } + ) +} diff --git a/crates/gui/src/components/sidebar.rs b/crates/gui/src/components/sidebar.rs new file mode 100644 index 0000000..6c1573e --- /dev/null +++ b/crates/gui/src/components/sidebar.rs @@ -0,0 +1,486 @@ +use freya::prelude::*; +use modloader_core::{config, paths}; + +use crate::components::button::Button; +use crate::{AppPage, CONFIG, CURRENT_PAGE, THEME, utils::hoverable::hoverable}; + +const SIDEBAR_WIDTH: &str = "256"; + +#[component] +pub fn Sidebar() -> Element { + let mut display_mode = use_signal(|| SidebarDisplayMode::Profiles); + + rsx!(rect { + width: SIDEBAR_WIDTH, + height: "100%", + direction: "vertical", + margin: "8 0 8 8", + color: THEME.read().text_primary, + + spacing: "8", + + rect { + width: "100%", + height: "{48+12+12}", + padding: "12", + direction: "horizontal", + spacing: "8", + corner_radius: "8", + background: THEME.read().bg_primary, + + + cross_align: "center", + main_align: "space-between", + + Button { + width: "48", + height: "48", + stretch: false, + padding: "{48/4}", + corner_radius: "{48/2}", + cross_align: "center", + main_align: "center", + + onpress: move |_| { + *CURRENT_PAGE.write() = AppPage::Home; + }, + + svg { + width: "24", + height: "24", + svg_data: static_bytes(crate::assets::HOME_ICON), + fill: THEME.read().text_primary, + } + } + + Button { + width: "48", + height: "48", + stretch: false, + padding: "{48/4}", + corner_radius: "{48/2}", + cross_align: "center", + main_align: "center", + + onpress: move |_| { + *CURRENT_PAGE.write() = AppPage::Settings; + }, + + svg { + width: "24", + height: "24", + svg_data: static_bytes(crate::assets::GEAR_ICON), + fill: THEME.read().text_primary, + } + } + + Button { + width: "48", + height: "48", + stretch: false, + padding: "{48/4}", + corner_radius: "{48/2}", + cross_align: "center", + main_align: "center", + + onpress: move |_| { + let _ = open::that(crate::assets::DISCORD_INVITE_LINK); + }, + + svg { + width: "24", + height: "24", + svg_data: static_bytes(crate::assets::DISCORD_ICON), + fill: THEME.read().text_primary, + } + } + + Button { + width: "48", + height: "48", + stretch: false, + padding: "{48/4}", + corner_radius: "{48/2}", + cross_align: "center", + main_align: "center", + + onpress: move |_| { + let _ = open::that(crate::assets::GITHUB_REPO_LINK); + }, + + svg { + width: "24", + height: "24", + svg_data: static_bytes(crate::assets::GITHUB_ICON), + fill: THEME.read().text_primary, + } + } + } + + rect { + background: THEME.read().bg_primary, + padding: "8", + corner_radius: "8", + ScrollView { + spacing: "8", + scrollbar_theme: theme_with!(ScrollBarTheme { + background: THEME.read().bg_tertiary.into(), + }), + + TabBar { + selected: display_mode(), + onclick: move |mode| { + *display_mode.write() = mode; + }, + } + + match display_mode() { + SidebarDisplayMode::Profiles => { + rsx!( + {CONFIG.read().profiles.clone().into_iter().map(|(profile_id, profile)| { + rsx!(Button { + width: "100%", + selected: *CURRENT_PAGE.read() == AppPage::Profile(profile_id.clone()), + + onpress: move |_| { + *CURRENT_PAGE.write() = AppPage::Profile(profile_id.clone()); + }, + + label { + font_size: "16", + "{profile.profile.name}" + } + }) + })} + + Button { + width: "100%", + onpress: |_| { + let uuid = uuid::Uuid::new_v4().to_string(); + let new_profile = config::ProfileConfig { + profile: config::Profile { + name: "New Profile".to_string(), + use_default_profile: false, + }, + discord: Default::default(), + instances: Default::default(), + }; + + let toml_path = paths::config_profile_dir().join(format!("{uuid}.toml")); + if let Ok(toml) = toml::to_string(&new_profile) { + let _ = std::fs::write(&toml_path, toml); + } + + *CONFIG.write() = config::Config::init(); + + *CURRENT_PAGE.write() = AppPage::Profile(uuid); + }, + + svg { + width: "24", + height: "24", + svg_data: static_bytes(crate::assets::PLUS_ICON), + fill: "#ffffff", + } + + label { + font_size: "16", + "Add New Profile" + } + } + ) + } + SidebarDisplayMode::Mods => { + rsx!( + {CONFIG.read().mods.clone().into_iter().map(|(mod_id, mod_)| { + rsx!(Button { + width: "100%", + selected: *CURRENT_PAGE.read() == AppPage::Mod(mod_id.clone()), + + onpress: move |_| { + *CURRENT_PAGE.write() = AppPage::Mod(mod_id.clone()); + }, + + label { + font_size: "16", + "{mod_.name}" + } + + }) + })} + + Button { + width: "100%", + onpress: |_| { + *CURRENT_PAGE .write() = AppPage::ModFromTemplate; + }, + + svg { + width: "24", + height: "24", + svg_data: static_bytes(crate::assets::PLUS_ICON), + fill: "#ffffff", + } + + label { + font_size: "16", + "Add New Mod" + } + } + ) + } + } + } + } + }) +} + +#[derive(Clone, Copy, PartialEq, Eq)] +enum SidebarDisplayMode { + Profiles, + Mods, +} + +#[component] +fn TabBar(selected: SidebarDisplayMode, onclick: EventHandler) -> Element { + let anim_underscore_offset = + use_animation(move |_conf| AnimNum::new(0.0, 50.0).ease(Ease::InOut).time(100)); + + let anim_underscore_width = + use_animation(move |_conf| AnimNum::new(50.0, 100.0).ease(Ease::InOut).time(100)); + + rsx!(rect { + direction: "vertical", + padding: "0 0 1 0", + + // The two tab buttons + rect { + direction: "horizontal", + + Button { + width: "50%", + padding: "8", + main_align: "center", + cross_align: "center", + base_color: THEME.read().bg_primary, + target_color: THEME.read().bg_primary, + shadow: "0", + + onmouseenter: move |_e| { + if selected == SidebarDisplayMode::Mods { + anim_underscore_width.run(AnimDirection::Forward); + anim_underscore_offset.run(AnimDirection::Reverse); + } + }, + onmouseleave: move |_e| { + if selected == SidebarDisplayMode::Mods { + anim_underscore_width.run(AnimDirection::Reverse); + anim_underscore_offset.run(AnimDirection::Forward); + } + }, + onpress: move |_e| { + if anim_underscore_width.get().read().read() == 100.0 { + anim_underscore_width.run(AnimDirection::Reverse); + } else if selected == SidebarDisplayMode::Mods { + anim_underscore_width.run(AnimDirection::Reverse); + anim_underscore_offset.run(AnimDirection::Reverse); + } + onclick(SidebarDisplayMode::Profiles); + }, + label { + font_size: "18", + + "Profiles" + } + } + Button { + width: "50%", + padding: "8", + main_align: "center", + cross_align: "center", + base_color: THEME.read().bg_primary, + target_color: THEME.read().bg_primary, + shadow: "0", + + onmouseenter: move |_e| { + if selected == SidebarDisplayMode::Profiles { + anim_underscore_width.run(AnimDirection::Forward); + } + }, + onmouseleave: move |_e| { + if selected == SidebarDisplayMode::Profiles { + anim_underscore_width.run(AnimDirection::Reverse); + } + }, + onpress: move |_| { + if selected == SidebarDisplayMode::Profiles || anim_underscore_width.get().read().read() != 50.0 { + anim_underscore_offset.run(AnimDirection::Forward); + anim_underscore_width.run(AnimDirection::Reverse); + } + onclick(SidebarDisplayMode::Mods); + }, + label { + font_size: "18", + + "Mods" + } + } + } + + rect { + direction: "horizontal", + // This moves the underline left/right + rect { + width: "{anim_underscore_offset.get().read().read()}%", + height: "2", + } + + // The underline + rect { + width: "{anim_underscore_width.get().read().read()}%", + height: "2", + background: THEME.read().blurple, + } + } + }) +} + +#[component] +fn HomeIconButton() -> Element { + let home_icon = static_bytes(crate::assets::HOME_ICON); + + let animation = hoverable!(move |_conf| { + AnimColor::new(THEME.read().bg_tertiary, THEME.read().blurple) + .ease(Ease::InOut) + .time(100) + }); + + let bg_color = animation.animation.get().read().read(); + + rsx!(rect { + width: "48", + height: "48", + corner_radius: "{48/2}", + + background: bg_color, + onmouseenter: animation.onmouseenter, + onmouseleave: animation.onmouseleave, + + main_align: "center", + cross_align: "center", + + svg { + width: "24", + height: "24", + svg_data: home_icon, + fill: "#ffffff", + } + }) +} + +#[component] +fn SettingsButton() -> Element { + let gear_icon = static_bytes(crate::assets::GEAR_ICON); + + let animation = hoverable!(move |_conf| { + AnimColor::new(THEME.read().bg_tertiary, THEME.read().blurple) + .ease(Ease::InOut) + .time(100) + }); + + let bg_color = animation.animation.get().read().read(); + + rsx!(rect { + width: "48", + height: "48", + corner_radius: "{48/2}", + + background: bg_color, + onmouseenter: animation.onmouseenter, + onmouseleave: animation.onmouseleave, + + main_align: "center", + cross_align: "center", + + svg { + width: "24", + height: "24", + svg_data: gear_icon, + fill: "#ffffff", + } + }) +} + +#[component] +fn GithubButton() -> Element { + let github_icon = static_bytes(crate::assets::GITHUB_ICON); + + let animation = hoverable!(move |_conf| { + AnimColor::new(THEME.read().bg_tertiary, THEME.read().blurple) + .ease(Ease::InOut) + .time(100) + }); + + let bg_color = animation.animation.get().read().read(); + + rsx!(rect { + width: "48", + height: "48", + corner_radius: "{48/2}", + + background: bg_color, + onmouseenter: animation.onmouseenter, + onmouseleave: animation.onmouseleave, + + main_align: "center", + cross_align: "center", + + onclick: move |_| { + let _ = open::that(crate::assets::GITHUB_REPO_LINK); + }, + + svg { + width: "24", + height: "24", + svg_data: github_icon, + fill: "#ffffff", + } + }) +} + +#[component] +fn DiscordButton() -> Element { + let discord_icon = static_bytes(crate::assets::DISCORD_ICON); + + let animation = hoverable!(move |_conf| { + AnimColor::new(THEME.read().bg_tertiary, THEME.read().blurple) + .ease(Ease::InOut) + .time(100) + }); + + let bg_color = animation.animation.get().read().read(); + + rsx!(rect { + width: "48", + height: "48", + corner_radius: "{48/2}", + + background: bg_color, + onmouseenter: animation.onmouseenter, + onmouseleave: animation.onmouseleave, + + main_align: "center", + cross_align: "center", + + onclick: move |_| { + let _ = open::that(crate::assets::DISCORD_INVITE_LINK); + }, + + svg { + width: "26", + height: "26", + svg_data: discord_icon, + fill: "#ffffff", + } + }) +} diff --git a/crates/gui/src/constants.rs b/crates/gui/src/constants.rs new file mode 100644 index 0000000..67d4360 --- /dev/null +++ b/crates/gui/src/constants.rs @@ -0,0 +1,60 @@ +// TODO: Implement theme switching. +// #[derive(Clone)] +// pub enum Theme { +// Dark, +// // Light, +// } + +// impl Theme { +// pub fn get(&self) -> &'static ThemeDef { +// match self { +// Theme::Dark => &DARK_THEME, +// // Theme::Light => &LIGHT_THEME, +// } +// } +// } + +// TODO: Use proper freya themeing. + +// use freya::hooks::cow_borrowed; + +// pub const DISCORD_DARK: freya::hooks::Theme = freya::hooks::Theme { +// name: "discord-dark", +// colors: freya::hooks::ColorsSheet { +// primary: cow_borrowed!("rgb(46, 46, 52)"), +// secondary: cow_borrowed!("rgb(52, 52, 58)"), +// tertiary: cow_borrowed!("rgb(56, 56, 62)"), +// ..freya::hooks::DARK_THEME.colors +// }, +// ..freya::hooks::DARK_THEME +// }; + +pub const DARK_THEME: ThemeDef = ThemeDef { + bg_primary: "rgb(46, 46, 52)", + bg_secondary: "rgb(52, 52, 58)", + bg_tertiary: "rgb(56, 56, 62)", + + text_primary: "white", + text_secondary: "rgb(219, 219, 219)", + + bg_success: "rgb(68, 162, 91)", + bg_danger: "rgb(210, 45, 57)", + + blurple: "rgb(88, 101, 242)", + star_yellow: "rgb(255, 172, 51)", +}; + +pub struct ThemeDef { + pub bg_primary: &'static str, + pub bg_secondary: &'static str, + pub bg_tertiary: &'static str, + + pub text_primary: &'static str, + pub text_secondary: &'static str, + + pub bg_success: &'static str, + pub bg_danger: &'static str, + + pub blurple: &'static str, + pub star_yellow: &'static str, +} diff --git a/crates/gui/src/lib.rs b/crates/gui/src/lib.rs new file mode 100644 index 0000000..994ca5f --- /dev/null +++ b/crates/gui/src/lib.rs @@ -0,0 +1,66 @@ +pub(crate) mod app; +pub(crate) mod assets; +pub(crate) mod components; +pub(crate) mod constants; +pub(crate) mod utils; + +use constants::ThemeDef; + +use freya::prelude::*; + +pub static CURRENT_PAGE: GlobalSignal = Signal::global(|| AppPage::Home); +pub static THEME: GlobalSignal = Signal::global(|| constants::DARK_THEME); +pub static CONFIG: GlobalSignal = + Signal::global(modloader_core::config::Config::init); +pub static POPUP_STATE: GlobalSignal = Signal::global(|| PopupState::None); +pub static REFRESH_PIDS: GlobalSignal<()> = Signal::global(|| ()); + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum AppPage { + Home, + Mod(String), + ModFromTemplate, + Profile(String), + Settings, +} + +#[derive(Debug, Clone, PartialEq, Eq)] +pub enum PopupState { + None, + ConfirmDeleteMod(String), + ConfirmDeleteProfile(String), + ConfirmDeleteInstance(String, String), + ConfirmKillProfile(String), + InstanceAlreadyRunning(String, String, String), +} + +pub enum AppTheme { + Dark, + Light, +} + +pub fn start_gui() { + let cfg: LaunchConfig<()> = LaunchConfig::new() + .with_title("Discord Mod Launcher") + .with_size(1080., 720.) + .with_font("Inter", assets::FONT_INTER) + .with_default_font("Inter") + .with_decorations(true) + .with_transparency(true); + + launch_cfg(app, cfg); +} + +fn app() -> Element { + // A task that runs every 1s that re-scans processes to update the PID list + use_hook(|| { + spawn(async move { + loop { + tokio::time::sleep(std::time::Duration::from_secs(1)).await; + *REFRESH_PIDS.write() = (); + } + }) + }); + + app::app() +} diff --git a/crates/gui/src/utils/hoverable.rs b/crates/gui/src/utils/hoverable.rs new file mode 100644 index 0000000..ae7bfb3 --- /dev/null +++ b/crates/gui/src/utils/hoverable.rs @@ -0,0 +1,54 @@ +use freya::prelude::*; + +pub struct Hoverable { + #[allow( + unused, + reason = "Consumers don't always need to know the hover state." + )] + pub hovered: Signal, + pub animation: UseAnimation, + pub onmouseenter: Box)>, + pub onmouseleave: Box)>, +} + +/// A macro to create a hoverable animation. +/// +/// # Example +/// +/// ```rust +/// let bg_anim = hoverable!(|_conf| { +/// AnimColor::new("rgb(52, 52, 58)", "rgb(88, 101, 242)") +/// .ease(Ease::InOut) +/// .time(100) +/// }); +macro_rules! hoverable { + ($anim:expr) => {{ + use freya::prelude::*; + + let mut hovered = use_signal(|| false); + let animation = use_animation($anim); + + let onmouseenter = move |_: Event| { + if !hovered() { + animation.run(AnimDirection::Forward); + hovered.set(true); + } + }; + + let onmouseleave = move |_: Event| { + if hovered() { + hovered.set(false); + animation.run(AnimDirection::Reverse); + } + }; + + $crate::utils::hoverable::Hoverable { + hovered, + animation, + onmouseenter: Box::new(onmouseenter), + onmouseleave: Box::new(onmouseleave), + } + }}; +} + +pub(crate) use hoverable; diff --git a/crates/gui/src/utils/icons.rs b/crates/gui/src/utils/icons.rs new file mode 100644 index 0000000..ad64c30 --- /dev/null +++ b/crates/gui/src/utils/icons.rs @@ -0,0 +1,89 @@ +use freya::prelude::Readable as _; +use modloader_core::{config, paths}; + +use crate::CONFIG; + +#[cfg(target_os = "linux")] +const LIB_PATH: &str = "/usr/lib/discord-modloader"; + +pub trait GetIcon { + fn get_icon(&self) -> Option>; +} + +// TODO: Should profiles have icons? + +impl GetIcon for config::Instance { + fn get_icon(&self) -> Option> { + let name = { + if let Some(ref name) = self.icon { + Some(name.clone()) + } else if let Some(mod_) = CONFIG.peek().mods.get(&self.mod_id) { + return mod_.get_icon(); + } else { + None + } + }?; + + scan_icon_by_name(&name) + } +} + +impl GetIcon for config::Mod { + fn get_icon(&self) -> Option> { + let name = { + if let Some(ref name) = self.icon { + Some(name.to_string()) + } else { + self.updater.as_ref().map(|updater| { + format!("{}/{}/icon.png", updater.github_org, updater.github_repo) + }) + } + }?; + + scan_icon_by_name(&name) + } +} + +fn scan_icon_by_name(name: &str) -> Option> { + let config_path = dirs::config_local_dir().unwrap().join("discord-modloader"); + let icon_path = config_path.join("icons").join(name); + if icon_path.exists() { + return std::fs::read(icon_path).ok(); + } + + // TODO: Get windows/macos libpath + #[cfg(target_os = "linux")] + { + let lib_path = std::path::PathBuf::from(LIB_PATH); + + let icon_path = lib_path.join("icons").join(name); + if icon_path.exists() { + return std::fs::read(icon_path).ok(); + } + } + + let metadata_dir = paths::cache_mod_metadata_dir(); + let icon_path = metadata_dir.join(name); + if icon_path.exists() { + return std::fs::read(icon_path).ok(); + } + + let current_exe = std::env::current_exe().unwrap(); + let icon_path = current_exe.with_file_name("icons").join(name); + if icon_path.exists() { + return std::fs::read(icon_path).ok(); + } + + let cwd = std::env::current_dir().unwrap(); + let icon_path = cwd.join("icons").join(name); + if icon_path.exists() { + return std::fs::read(icon_path).ok(); + } + + let icon_path = cwd.join("configs").join("icons").join(name); + if icon_path.exists() { + return std::fs::read(icon_path).ok(); + } + + None +} diff --git a/crates/gui/src/utils/mod.rs b/crates/gui/src/utils/mod.rs new file mode 100644 index 0000000..b6bb843 --- /dev/null +++ b/crates/gui/src/utils/mod.rs @@ -0,0 +1,2 @@ +pub mod hoverable; +pub mod icons; diff --git a/crates/modloader-core/Cargo.toml b/crates/modloader-core/Cargo.toml new file mode 100644 index 0000000..0e24798 --- /dev/null +++ b/crates/modloader-core/Cargo.toml @@ -0,0 +1,19 @@ +[package] +name = "modloader-core" +version = "0.2.0-beta.1" +edition = "2021" + +[dependencies] +dialog = "0.3.0" +dirs = "6.0.0" +flate2 = "1.0.35" +octocrab = "0.43.0" +serde = { version = "1.0.217", features = ["derive"] } +electron-hook = {version = "0.2.0-beta.4", features = ["self-executable"] } +serde_json = "1.0.137" +strum = { version = "0.26.3", features = ["derive"] } +sysinfo = { version = "0.33.1", default-features = false, features = ["system", "user"] } +tar = "0.4.43" +toml = "0.8.19" +ureq = { version = "3.0.1", features = ["json"] } +zip = "2.2.2" diff --git a/crates/modloader-core/src/asar/custom_profile.js b/crates/modloader-core/src/asar/custom_profile.js new file mode 100644 index 0000000..31f3390 --- /dev/null +++ b/crates/modloader-core/src/asar/custom_profile.js @@ -0,0 +1,14 @@ +const { app } = require("electron"); +const customAppDir = "$PROFILE"; + +const _setPath = app.setPath; + +app.setPath = function (name, path) { + if (name === "userData") { + _setPath.call(app, name, customAppDir); + } else { + _setPath.call(app, name, path); + } +}; + +app.setPath("userData", customAppDir); diff --git a/crates/modloader-core/src/asar/index.js b/crates/modloader-core/src/asar/index.js new file mode 100644 index 0000000..0d034f8 --- /dev/null +++ b/crates/modloader-core/src/asar/index.js @@ -0,0 +1,10 @@ +// This is how the index.js is laid out. +// This file is not directly used in the code, and is just a reference. + +__PREFIX__ + +__CUSTOM_PROFILE__ + +__REQUIRE__ + +__SUFFIX__ \ No newline at end of file diff --git a/asar/package.json b/crates/modloader-core/src/asar/package.json similarity index 100% rename from asar/package.json rename to crates/modloader-core/src/asar/package.json diff --git a/crates/modloader-core/src/asar/require.js b/crates/modloader-core/src/asar/require.js new file mode 100644 index 0000000..6105496 --- /dev/null +++ b/crates/modloader-core/src/asar/require.js @@ -0,0 +1,11 @@ +const mod_entrypoint = require("$ENTRYPOINT"); + +// Hard-code support for Moonlight, since it doesn't auto-inject. +if ( + mod_entrypoint && + mod_entrypoint["inject"] && + typeof mod_entrypoint["inject"] === "function" +) { + let asar = require("path").resolve(__dirname, "../_app.asar"); + mod_entrypoint.inject(asar); +} diff --git a/crates/modloader-core/src/cache.rs b/crates/modloader-core/src/cache.rs new file mode 100644 index 0000000..3c17135 --- /dev/null +++ b/crates/modloader-core/src/cache.rs @@ -0,0 +1,93 @@ +use crate::{config, paths, updater}; + +pub fn create_instance_cache( + cfg: &crate::config::Config, + profile_id: &str, + instance_id: &str, + force_update: bool, +) -> Result { + static ASAR_CUSTOM_PROFILE_JS: &str = include_str!("./asar/custom_profile.js"); + + let profile = cfg + .profiles + .get(profile_id) + .unwrap_or_else(|| panic!("Failed to find profile '{}'.", profile_id)); + + let instance = profile + .instances + .get(instance_id) + .unwrap_or_else(|| panic!("Failed to find instance '{}'.", instance_id)); + + let r#mod = cfg + .mods + .get(&instance.mod_id) + .unwrap_or_else(|| panic!("Failed to find mod '{}'.", instance.mod_id)) + .to_owned(); + + // TODO: Make this more robust + if let Some(ref updater) = r#mod.updater { + let _ = updater::update(updater, force_update); + } + + let asar_cache_path = paths::cache_asar_path(profile_id, instance_id, &instance.mod_id); + + paths::ensure_dir(asar_cache_path.parent().unwrap().to_path_buf()); + + let profile_dir = if !profile.profile.use_default_profile { + let profile_dir = paths::ensure_dir(paths::data_profiles_dir().join(profile_id)); + let profile_dir = profile_dir.to_str().unwrap().replace("\\", "\\\\"); + Some(profile_dir) + } else { + None + }; + + // If path is not provided, try to use the updater instead. + let mod_path = if let Some(path) = r#mod.path { + path + } else if let Some(updater) = r#mod.updater { + paths::cache_mod_files_dir() + .join(&updater.github_org) + .join(&updater.github_repo) + .to_string_lossy() + .to_string() + } else { + panic!("Mod '{}' does not have a path or updater.", r#mod.name); + }; + + let mod_entrypoint = std::path::Path::new(&mod_path).join(&r#mod.entrypoint); + let mod_entrypoint = mod_entrypoint.to_str().unwrap().replace("\\", "\\\\"); + + let mut custom_loader = String::from("console.log(\"Launching with Discord Modloader.\");\n"); + + // If using a custom profile directory, insert this. + if profile_dir.is_some() { + custom_loader.push_str(ASAR_CUSTOM_PROFILE_JS); + } + + let loader = if let Some(ref loader) = r#mod.loader { + [ + loader.prefix.clone().unwrap_or_default(), + loader + .require + .clone() + .unwrap_or(config::ModLoader::default_require().unwrap()), + loader.suffix.clone().unwrap_or_default(), + ] + .join("\n") + } else { + config::ModLoader::default_require().unwrap() + }; + + custom_loader.push_str(&loader); + + let mut asar = electron_hook::asar::Asar::new() + .with_id(&instance.mod_id) + .with_template(&custom_loader) + .with_mod_entrypoint(&mod_entrypoint); + + if let Some(profile_dir) = profile_dir { + asar = asar.with_profile_dir(&profile_dir); + } + + asar.create() +} diff --git a/crates/modloader-core/src/config.rs b/crates/modloader-core/src/config.rs new file mode 100644 index 0000000..1889f09 --- /dev/null +++ b/crates/modloader-core/src/config.rs @@ -0,0 +1,296 @@ +use std::collections::BTreeMap; + +use serde::{Deserialize, Serialize}; + +use crate::paths::{self, ensure_dir}; + +#[derive(Debug, Deserialize)] +#[serde(rename_all = "lowercase")] +pub enum ConfigFile { + Instance(Instance), + Mod(Mod), +} + +#[derive(Debug, Default, Clone, Serialize, Deserialize)] +pub struct Config { + pub profiles: BTreeMap, + pub mods: BTreeMap, + pub settings: Settings, +} + +impl Config { + pub fn add_profile( + &mut self, + profile_id: &str, + profile: ProfileConfig, + ) -> Option { + self.profiles.insert(profile_id.to_string(), profile) + } + + pub fn remove_profile(&mut self, profile_id: &str) -> Option { + self.profiles.remove(profile_id) + } + + pub fn add_mod(&mut self, mod_id: &str, r#mod: Mod) -> Option { + self.mods.insert(mod_id.to_string(), r#mod) + } + + pub fn remove_mod(&mut self, mod_id: &str) -> Option { + self.mods.remove(mod_id) + } + + pub fn sync(&mut self) { + self.sync_profiles(); + self.sync_mods(); + self.sync_settings(); + } + + pub fn sync_profiles(&mut self) { + let profiles_config_dir = ensure_dir(paths::config_profile_dir()); + + let profile_files = paths::read_dir(&profiles_config_dir); + for profile in profile_files + .iter() + .filter(|p| p.path().to_string_lossy().ends_with(".toml")) + { + let id = profile.file_name().to_string_lossy().replace(".toml", ""); + + match std::fs::read_to_string(profile.path()) { + Ok(profile_content) => match toml::from_str::(&profile_content) { + Ok(profile) => { + self.profiles.insert(id, profile); + } + Err(_) => eprintln!("Failed to parse profile config: {:?}", profile), + }, + Err(_) => eprintln!("Failed to read profile config: {:?}", profile), + } + } + + self.profiles.retain(|prof_id, _| { + profile_files.iter().any(|file_name| { + file_name.file_name().to_string_lossy().replace(".toml", "") == *prof_id + }) + }); + } + + pub fn sync_mods(&mut self) { + let mods_config_dir = ensure_dir(paths::config_mods_dir()); + + let mod_files = paths::read_dir(&mods_config_dir); + for mod_ in mod_files + .iter() + .filter(|m| m.path().to_string_lossy().ends_with(".toml")) + { + let id = mod_.file_name().to_string_lossy().replace(".toml", ""); + + match std::fs::read_to_string(mod_.path()) { + Ok(mod_content) => match toml::from_str::(&mod_content) { + Ok(mod_) => { + self.mods.insert(id, mod_.r#mod); + } + Err(_) => eprintln!("Failed to parse mod config: {:?}", mod_), + }, + Err(_) => eprintln!("Failed to read mod config: {:?}", mod_), + } + } + + self.mods.retain(|mod_id, _| { + mod_files.iter().any(|mod_file| { + mod_file.file_name().to_string_lossy().replace(".toml", "") == *mod_id + }) + }); + } + + pub fn sync_settings(&mut self) { + let config_dir = ensure_dir(paths::configs_dir()); + let settings_file = config_dir.join("settings.toml"); + + self.settings = if let Ok(settings) = std::fs::read_to_string(&settings_file) { + toml::from_str::(&settings).unwrap_or_default() + } else { + Settings::default() + }; + } + + pub fn init() -> Config { + let mut config = Config::default(); + + let config_dir = ensure_dir(paths::configs_dir()); + let settings_file = config_dir.join("settings.toml"); + + config.settings = if let Ok(settings) = std::fs::read_to_string(&settings_file) { + toml::from_str::(&settings).unwrap_or_default() + } else { + Settings::default() + }; + + config.sync_mods(); + config.sync_profiles(); + + config + } +} + +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq)] +pub struct Instance { + // pub id: String, + /// The display name of the instance. (e.g. "Vencord", "Moonlight", "My Custom Instance") + /// + /// Can be duplicate, but not recommended for clarity. + pub name: String, + + /// A path to the icon to use for the mod. + pub icon: Option, + + /// The identifier of the mod to use for this instance. + /// + /// Internal mods (ones with build-in updaters) will be prefixed with "Internal::" + pub mod_id: String, + + /// Whether the profile has been starred/favourited. + /// + /// Starred profiles will be shown on the homepage of the GUI. + #[serde(default)] + pub starred: bool, +} + +#[derive(Debug, Default, Serialize, Deserialize, Clone)] +pub struct ModConfig { + pub r#mod: Mod, +} + +impl ModConfig { + pub fn save(&self, mod_id: &str) { + let mods_config_dir = ensure_dir(paths::config_mods_dir()); + let mod_file = mods_config_dir.join(format!("{mod_id}.toml")); + + if let Ok(mod_content) = toml::to_string_pretty(self) { + if let Err(e) = std::fs::write(mod_file, mod_content) { + eprintln!("Failed to write mod config: {:?}", e); + } + } + } +} + +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)] +pub struct Mod { + /// The display name of the mod. (e.g. "Vencord", "Moonlight", "BetterDiscord") + /// + /// Can be duplicate, but not recommended for clarity. + pub name: String, + + /// The path to the mod's dist folder. (e.g. "/path/to/moonlight") + /// If none, there should be an updater. + pub path: Option, + + /// The entrypoint of the mod. (e.g. "injector.js", "patcher.js") + pub entrypoint: String, + + /// A path to the icon to use for the mod. + pub icon: Option, + + /// Provide custom loader JS to be injected into the ASAR index.js. + pub loader: Option, + + /// The updater configuration for the mod. + pub updater: Option, +} + +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)] +pub struct ModUpdater { + pub github_org: String, + pub github_repo: String, + pub dist_file_names: Vec, + pub dist_file_type: DistFileType, + pub icon_url: Option, + #[serde(default = "default_bool_true")] + pub ask_before_update: bool, + #[serde(default = "default_bool_true")] + pub auto_update: bool, +} + +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq, strum::EnumIter, Hash)] +pub enum DistFileType { + #[default] + Raw, + TarGz, + Zip, +} + +impl std::fmt::Display for DistFileType { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + DistFileType::Raw => write!(f, "plain file"), + DistFileType::TarGz => write!(f, "tar.gz"), + DistFileType::Zip => write!(f, "zip"), + } + } +} + +/// The loader configuration for the mod. +/// You can use this to specify custom JS to be in your ASAR's index.js. +/// +/// The following variables can be used: +/// +/// - `$PROFILE`: The directory of the custom profile. +/// - `$ENTRYPOINT`: The entrypoint file of the mod. +#[derive(Debug, Serialize, Deserialize, Clone, PartialEq, Eq, Hash)] +pub struct ModLoader { + pub prefix: Option, + #[serde(default = "ModLoader::default_require")] + pub require: Option, + pub suffix: Option, +} + +impl ModLoader { + pub fn default_require() -> Option { + Some(include_str!("./asar/require.js").to_string()) + } +} + +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)] +pub struct ProfileConfig { + pub profile: Profile, + + #[serde(rename = "instance", default)] + pub instances: BTreeMap, + + pub discord: Discord, +} + +impl ProfileConfig { + pub fn save(&self, profile_id: &str) { + let profiles_config_dir = ensure_dir(paths::config_profile_dir()); + let profile_file = profiles_config_dir.join(format!("{profile_id}.toml")); + + if let Ok(profile_content) = toml::to_string_pretty(self) { + if let Err(e) = std::fs::write(profile_file, profile_content) { + eprintln!("Failed to write profile config: {:?}", e); + } + } + } +} + +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)] +pub struct Profile { + pub name: String, + #[serde(default)] + pub use_default_profile: bool, +} + +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)] +pub struct Discord { + pub executable: String, + #[serde(default)] + pub args: String, +} + +#[derive(Debug, Default, Serialize, Deserialize, Clone, PartialEq, Eq)] +pub struct Settings { + #[serde(default = "default_bool_true")] + pub hide_window_on_launch: bool, +} + +fn default_bool_true() -> bool { + true +} diff --git a/crates/modloader-core/src/discord.rs b/crates/modloader-core/src/discord.rs new file mode 100644 index 0000000..d1315ca --- /dev/null +++ b/crates/modloader-core/src/discord.rs @@ -0,0 +1,57 @@ +/// Returns the path to the Discord executable based on the Discord folder. +/// +/// e.g. "C:\Users\User\AppData\Local\discordptb" -> "C:\Users\User\AppData\Local\discordptb\app-1.0.9023\Discord.exe" +#[cfg(target_os = "windows")] +pub fn get_discord_exe(path: &str) -> Result> { + let mut new_path = std::path::PathBuf::from(path); + + let new_path_executable = new_path.clone(); + let executable_name = new_path_executable + .file_name() + .and_then(|name| name.to_str()) + .ok_or("Failed to read executable name")?; + + let versions: Vec<_> = new_path + .read_dir()? + .filter_map(|p| { + let file_name = p.ok()?.file_name(); + let file_name_str = file_name.to_str()?; + if file_name_str.starts_with("app-") { + Some(file_name_str.to_string()) + } else { + None + } + }) + .collect(); + + if versions.is_empty() { + return Err("No discord versions found".into()); + } + + // In the format of: app-1.0.9023 + let mut sorted: Vec<(String, u32)> = versions + .into_iter() + .filter_map(|v| { + let mut split = v.split('-'); + let version = split.nth(1)?; + let version = version.replace('.', ""); + let version = version.parse::().ok()?; + Some((v, version)) + }) + .collect(); + + sorted.sort_by(|(_, a), (_, b)| a.cmp(b)); + + let Some(latest_version) = sorted.last().cloned() else { + return Err("Failed to get latest version".into()); + }; + + new_path.push(latest_version.0); + new_path.push(format!("{}.exe", executable_name)); + + if !new_path.exists() { + return Err("Discord.exe not found".into()); + } + + Ok(new_path) +} diff --git a/crates/modloader-core/src/lib.rs b/crates/modloader-core/src/lib.rs new file mode 100644 index 0000000..50b50b2 --- /dev/null +++ b/crates/modloader-core/src/lib.rs @@ -0,0 +1,6 @@ +pub mod cache; +pub mod config; +pub mod discord; +pub mod paths; +pub mod updater; +pub mod utils; diff --git a/crates/modloader-core/src/paths.rs b/crates/modloader-core/src/paths.rs new file mode 100644 index 0000000..050d310 --- /dev/null +++ b/crates/modloader-core/src/paths.rs @@ -0,0 +1,80 @@ +static APP_NAME: &str = "discord-modloader"; + +/// Create a directory if it doesn't exist. +pub fn ensure_dir(dir: std::path::PathBuf) -> std::path::PathBuf { + if !dir.exists() { + std::fs::create_dir_all(&dir).unwrap_or_else(|_| { + panic!( + r#"Failed to create path. Make sure you have permissions. {}"#, + dir.display() + ) + }); + } + dir +} + +/// The config directory for this application. +pub fn configs_dir() -> std::path::PathBuf { + dirs::config_local_dir() + .expect("Failed to get config dir") + .join(APP_NAME) +} + +/// The profiles config directory. +pub fn config_profile_dir() -> std::path::PathBuf { + configs_dir().join("profiles") +} + +/// The mods config directory. +pub fn config_mods_dir() -> std::path::PathBuf { + configs_dir().join("mods") +} + +/// The mods config directory. +pub fn config_icons_dir() -> std::path::PathBuf { + configs_dir().join("icons") +} + +/// The data directory for this application. +pub fn data_dir() -> std::path::PathBuf { + dirs::data_dir() + .expect("Failed to get data dir") + .join(APP_NAME) +} + +/// The profiles data directory. +pub fn data_profiles_dir() -> std::path::PathBuf { + data_dir().join("profiles") +} + +pub fn cache_dir() -> std::path::PathBuf { + dirs::cache_dir() + .expect("Failed to get cache dir") + .join(APP_NAME) +} + +/// The asar cache directory. +pub fn cache_asar_dir() -> std::path::PathBuf { + cache_dir().join("asar") +} + +pub fn cache_mod_files_dir() -> std::path::PathBuf { + cache_dir().join("mod_files") +} + +pub fn cache_mod_metadata_dir() -> std::path::PathBuf { + cache_dir().join("mod_metadata") +} + +/// The asar path for a profile, instance, and mod. +pub fn cache_asar_path(profile_id: &str, instance_id: &str, mod_id: &str) -> std::path::PathBuf { + cache_asar_dir().join(format!("{}-{}-{}.asar", profile_id, instance_id, mod_id)) +} + +/// Read a directory and return a vector of DirEntry. Panic if it fails. +pub fn read_dir(path: &std::path::Path) -> Vec { + std::fs::read_dir(path) + .unwrap_or_else(|_| panic!("Failed to read directory: {}", path.display())) + .collect::, _>>() + .unwrap_or_else(|_| panic!("Failed to read directory: {}", path.display())) +} diff --git a/crates/modloader-core/src/updater.rs b/crates/modloader-core/src/updater.rs new file mode 100644 index 0000000..7a90d00 --- /dev/null +++ b/crates/modloader-core/src/updater.rs @@ -0,0 +1,142 @@ +use dialog::{Choice, DialogBox}; + +pub fn update(config: &crate::config::ModUpdater, force: bool) -> Result<(), String> { + use octocrab::models::repos::Release; + + println!("Checking for mod updates..."); + + let metadata_dir = crate::paths::cache_mod_metadata_dir() + .join(&config.github_org) + .join(&config.github_repo); + + if !metadata_dir.exists() { + std::fs::create_dir_all(&metadata_dir) + .map_err(|e| format!("Failed to create metadata directory: {}", e))?; + } + + let output_dir = crate::paths::cache_mod_files_dir() + .join(&config.github_org) + .join(&config.github_repo); + + if !output_dir.exists() { + std::fs::create_dir_all(&output_dir) + .map_err(|e| format!("Failed to create mod cache directory: {}", e))?; + } + + let release_url = format!( + "https://api.github.com/repos/{}/{}/releases/latest", + &config.github_org, &config.github_repo + ); + + let release_info: Release = ureq::get(&release_url) + .call() + .map_err(|e| format!("Failed to get release: {}", e))? + .body_mut() + .read_json() + .map_err(|e| format!("Failed to parse release: {}", e))?; + + // If the version IDs are the same, and force is not enabled, then return early. + let release_version_file = metadata_dir.join("release.json"); + if !force && release_version_file.exists() { + if let Ok(file) = std::fs::File::open(&release_version_file) { + if let Ok(release) = serde_json::from_reader::<_, Release>(file) { + // Vencord only changes the name, and doesn't create new releases... + if release.tag_name == release_info.tag_name && release.name == release_info.name { + println!( + "Mod is already the latest version... ({})", + release.tag_name + ); + return Ok(()); + } + } + } + } + + if !force && config.ask_before_update { + if let Ok(resp) = dialog::Question::new(format!( + "An update is available for the mod {}/{}. Would you like to update?", + config.github_org, config.github_repo + )) + .title("Update Available!") + .show() + { + if let Choice::Yes = resp { + // Continue + } else { + println!("Update cancelled."); + return Ok(()); + } + } + } + + for dist_file in &config.dist_file_names { + // TODO: Should the download URL be pulled from the release assets? + // Will be a bit annoying if there are multiple assets though. + let download_url = format!( + "https://github.com/{}/{}/releases/download/{}/{}", + config.github_org, config.github_repo, release_info.tag_name, &dist_file + ); + + let file_data: Vec = ureq::get(&download_url) + .call() + .map_err(|e| format!("Failed to download release tarball: {}", e))? + .body_mut() + .read_to_vec() + .map_err(|e| format!("Failed to download release tarball: {}", e))?; + + match config.dist_file_type { + crate::config::DistFileType::TarGz => { + use flate2::read::GzDecoder; + use tar::Archive; + + let tar = GzDecoder::new(file_data.as_slice()); + let mut archive = Archive::new(tar); + + // TODO: If files are already present, should they be deleted? + + archive + .unpack(&output_dir) + .map_err(|e| format!("Failed to unpack tarball: {}", e))?; + } + crate::config::DistFileType::Zip => { + use std::io::Cursor; + use zip::read::ZipArchive; + + let reader = Cursor::new(file_data.as_slice()); + + let mut zip = ZipArchive::new(reader) + .map_err(|e| format!("Failed to read zip file: {}", e))?; + + zip.extract(&output_dir) + .map_err(|e| format!("Failed to extract zip file: {}", e))?; + } + crate::config::DistFileType::Raw => { + std::fs::write(output_dir.join(dist_file), file_data.as_slice()) + .map_err(|e| format!("Failed to write raw file: {}", e))?; + } + } + } + + let version_content = serde_json::to_string(&release_info) + .map_err(|e| format!("Failed to serialize mod release version: {}", e))?; + + std::fs::write(&release_version_file, version_content) + .map_err(|e| format!("Failed to write version to file: {}", e))?; + + if let Some(ref icon_url) = config.icon_url { + let icon_file_bytes = ureq::get(icon_url) + .call() + .map_err(|e| format!("Failed to download mod icon: {}", e))? + .body_mut() + .read_to_vec() + .map_err(|e| format!("Failed to read mod icon: {}", e))?; + + let icon_file_path = metadata_dir.join("icon.png"); + + std::fs::write(&icon_file_path, icon_file_bytes.as_slice()) + .map_err(|e| format!("Failed to write mod icon: {}", e))?; + } + + println!("Finished updating mod!"); + Ok(()) +} diff --git a/crates/modloader-core/src/utils.rs b/crates/modloader-core/src/utils.rs new file mode 100644 index 0000000..20c4214 --- /dev/null +++ b/crates/modloader-core/src/utils.rs @@ -0,0 +1,82 @@ +use crate::{config, paths}; + +use sysinfo::{Pid, ProcessRefreshKind, RefreshKind, System}; + +pub fn kill_pids(pids: Vec) { + let system = System::new_all(); + for pid in pids { + if let Some(proc) = system.process(pid) { + let _ = proc.kill(); + } + } +} + +pub fn find_running_instances(profile_id: &str, profile: &config::ProfileConfig) -> Vec { + let system = System::new_with_specifics( + RefreshKind::nothing().with_processes(ProcessRefreshKind::everything()), + ); + + println!("Finding instances for {profile_id}"); + + system + .processes() + .iter() + .filter_map(|(_pid, proc)| { + let cmd = proc.cmd().join(std::ffi::OsStr::new(" ")); + let cmd = cmd.to_str().unwrap().to_string(); + + let should_kill = match !profile.profile.use_default_profile { + false => { + // If using the default discord profile (doesn't use our discord-modloader profiles dir) + // and the command contains the discord executable path, it's a duplicate profile. + !cmd.contains(paths::data_profiles_dir().to_str().unwrap()) + && cmd.contains(&profile.discord.executable) + } + true => { + let profile_path = paths::data_profiles_dir().join(profile_id); + let profile_path = profile_path.to_str().unwrap(); + cmd.contains(&format!("--user-data-dir={}", profile_path)) + } + }; + + if should_kill { + Some(proc.pid()) + } else { + None + } + }) + .collect() +} + +pub fn launch_detached_instance( + profile_id: &str, + instance_id: &str, + args: &str, + force_update: bool, +) -> Result> { + let mut target = std::process::Command::new(std::env::current_exe().unwrap()); + + target + .arg("--profile") + .arg(profile_id) + .arg("--instance") + .arg(instance_id); + + if force_update { + target.arg("--force-update"); + } + + target + .arg("--") + .arg(args) + .stdout(std::process::Stdio::null()) + .stderr(std::process::Stdio::null()) + .stdin(std::process::Stdio::null()); + + #[allow(clippy::zombie_processes, reason = "Process detaches from parent.")] + let target = target.spawn().expect("Failed to launch instance."); + + let pid = target.id(); + + Ok(pid) +} diff --git a/rust-toolchain.toml b/rust-toolchain.toml new file mode 100644 index 0000000..5d56faf --- /dev/null +++ b/rust-toolchain.toml @@ -0,0 +1,2 @@ +[toolchain] +channel = "nightly" diff --git a/src/detours/mod.rs b/src/detours/mod.rs deleted file mode 100644 index 26363af..0000000 --- a/src/detours/mod.rs +++ /dev/null @@ -1,2 +0,0 @@ -pub mod utils; -pub mod win32; diff --git a/src/detours/utils.rs b/src/detours/utils.rs deleted file mode 100644 index 0eb80ee..0000000 --- a/src/detours/utils.rs +++ /dev/null @@ -1,44 +0,0 @@ -use std::path::PathBuf; - -static mut MOD_DONE_LOADING: bool = false; - -const KNOWN_ASAR_NAMES: &[&str] = &["_app.asar", "app.orig.asar"]; - -/// Map original filenames to our modified filenames. -pub unsafe fn file_name_handler(path: &str) -> String { - let asar_toggle_query = std::env::var("MODHOOK_TOGGLE_QUERY") - .unwrap() - .to_lowercase(); - let pathbuf = PathBuf::from(&path); - - if path.to_lowercase().contains(&asar_toggle_query) { - MOD_DONE_LOADING = true; - } - - if let Some(filename) = pathbuf.file_name() { - let filename = filename.to_str().unwrap(); - - // If we have a custom asar filename, replace it with the original. - if let Ok(asar) = std::env::var("MODHOOK_MOD_ASAR_FILENAME") { - if filename.eq(&asar.to_lowercase()) { - return path.replace(&asar, "app.asar"); - } - } - - // Otherwise, default to the known asar names and replace them with the original. - if let Some(asar) = KNOWN_ASAR_NAMES.iter().find(|&&x| filename.eq(x)) { - let new_path = path.replace(asar, "app.asar"); - return new_path; - } - } - - if MOD_DONE_LOADING { - return path.to_string(); - } - - if path.ends_with("app.asar") { - return std::env::var("MODHOOK_ASAR_PATH").unwrap(); - } - - path.to_string() -} diff --git a/src/detours/win32.rs b/src/detours/win32.rs deleted file mode 100644 index cb831c8..0000000 --- a/src/detours/win32.rs +++ /dev/null @@ -1,155 +0,0 @@ -use std::ffi::{c_void, CString}; - -use detours_sys::{DetourAttach, DetourCreateProcessWithDllW}; -use widestring::U16CString; -use winapi::{ - shared::{ - minwindef::{BOOL, DWORD, LPVOID}, - ntdef::LPCWSTR, - }, - um::{ - fileapi::{CreateFileW, GetFileAttributesW}, - minwinbase::LPSECURITY_ATTRIBUTES, - processthreadsapi::{CreateProcessW, ResumeThread, LPPROCESS_INFORMATION, LPSTARTUPINFOW}, - winnt::{HANDLE, LPWSTR}, - }, -}; - -use super::utils::file_name_handler; - -/// Path to the libmodhook DLL. -static DLL_PATH: &str = "libmodhook.dll"; - -static mut O_CREATE_FILE_W: *mut c_void = 0 as _; -static mut O_GET_FILE_ATTRIBUTES_W: *mut c_void = 0 as _; -static mut O_CREATE_PROCESS_W: *mut c_void = 0 as _; - -pub unsafe fn init_detours() { - O_CREATE_FILE_W = CreateFileW as *mut c_void; - DetourAttach(&raw mut O_CREATE_FILE_W, create_file_w as _); - - O_GET_FILE_ATTRIBUTES_W = GetFileAttributesW as *mut c_void; - DetourAttach(&raw mut O_GET_FILE_ATTRIBUTES_W, get_file_attributes_w as _); - - O_CREATE_PROCESS_W = CreateProcessW as *mut c_void; - DetourAttach(&raw mut O_CREATE_PROCESS_W, create_process_w as _); -} - -unsafe fn create_file_w( - lp_file_name: LPCWSTR, - dw_desired_access: DWORD, - dw_share_mode: DWORD, - lp_security_attributes: LPSECURITY_ATTRIBUTES, - dw_creation_disposition: DWORD, - dw_flags_and_attributes: DWORD, - h_template_file: HANDLE, -) -> HANDLE { - let file_name = U16CString::from_ptr_str(lp_file_name).to_string().unwrap(); - - let new_file_name = file_name_handler(&file_name); - let new_file_name = U16CString::from_str(new_file_name).unwrap(); - - let create_file_w: extern "C" fn( - lp_file_name: LPCWSTR, - dw_desired_access: DWORD, - dw_share_mode: DWORD, - lp_security_attributes: LPSECURITY_ATTRIBUTES, - dw_creation_disposition: DWORD, - dw_flags_and_attributes: DWORD, - h_template_file: HANDLE, - ) -> HANDLE = std::mem::transmute(O_CREATE_FILE_W); - - create_file_w( - new_file_name.as_ptr(), - dw_desired_access, - dw_share_mode, - lp_security_attributes, - dw_creation_disposition, - dw_flags_and_attributes, - h_template_file, - ) -} - -unsafe fn get_file_attributes_w(lp_file_name: LPCWSTR) -> DWORD { - let get_file_attributes_w: extern "C" fn(lp_file_name: LPCWSTR) -> DWORD = - std::mem::transmute(O_GET_FILE_ATTRIBUTES_W); - - let file_name = U16CString::from_ptr_str(lp_file_name).to_string().unwrap(); - - let new_file_name = file_name_handler(&file_name); - let new_file_name = U16CString::from_str(new_file_name).unwrap(); - - get_file_attributes_w(new_file_name.as_ptr()) -} - -#[allow(clippy::too_many_arguments)] -unsafe fn create_process_w( - lp_application_name: LPCWSTR, - lp_command_line: LPWSTR, - lp_process_attributes: LPSECURITY_ATTRIBUTES, - lp_thread_attributes: LPSECURITY_ATTRIBUTES, - b_inherit_handles: BOOL, - dw_creation_flags: DWORD, - lp_environment: LPVOID, - lp_current_directory: LPCWSTR, - lp_startup_info: LPSTARTUPINFOW, - lp_process_information: LPPROCESS_INFORMATION, -) -> BOOL { - let create_process_w: unsafe extern "C" fn( - lp_application_name: LPCWSTR, - lp_command_line: LPWSTR, - lp_process_attributes: LPSECURITY_ATTRIBUTES, - lp_thread_attributes: LPSECURITY_ATTRIBUTES, - b_inherit_handles: BOOL, - dw_creation_flags: DWORD, - lp_environment: LPVOID, - lp_current_directory: LPCWSTR, - lp_startup_info: LPSTARTUPINFOW, - lp_process_information: LPPROCESS_INFORMATION, - ) -> BOOL = std::mem::transmute(O_CREATE_PROCESS_W); - - let command_line = U16CString::from_ptr_str(lp_command_line) - .to_string() - .unwrap(); - - if !command_line.contains("--type=renderer") { - return create_process_w( - lp_application_name, - lp_command_line, - lp_process_attributes, - lp_thread_attributes, - b_inherit_handles, - dw_creation_flags, - lp_environment, - lp_current_directory, - lp_startup_info, - lp_process_information, - ); - } - - let dll_path = CString::new(DLL_PATH.to_string()).unwrap(); - - let success = DetourCreateProcessWithDllW( - lp_application_name, - lp_command_line, - lp_process_attributes as _, - lp_thread_attributes as _, - b_inherit_handles, - dw_creation_flags, - lp_environment as _, - lp_current_directory, - lp_startup_info as _, - lp_process_information as _, - dll_path.as_ptr(), - Some(std::mem::transmute(O_CREATE_PROCESS_W)), - ); - - if success != 1 { - println!("[ModHook] Failed to create process"); - return success; - } - - ResumeThread((*lp_process_information).hThread as _); - - success -} diff --git a/src/discord.rs b/src/discord.rs deleted file mode 100644 index 786f5c4..0000000 --- a/src/discord.rs +++ /dev/null @@ -1,59 +0,0 @@ -use std::{error::Error, path::PathBuf}; - -/// Returns the path to the Discord executable based on the Discord folder. -/// -/// e.g. "C:\Users\Megu\AppData\Local\discordptb" -> "C:\Users\Megu\AppData\Local\discordptb\app-1.0.9023\Discord.exe" -pub fn get_discord_executable(path: &str) -> Result> { - let mut new_path = PathBuf::from(path); - - let new_path_executable = new_path.clone(); - let executable_name = new_path_executable.file_name().unwrap().to_str().unwrap(); - - let versions: Vec<_> = new_path - .read_dir()? - .filter_map(|p| { - let path = p.unwrap(); - let file_name = path.file_name(); - let file_name_str = file_name.to_str().unwrap(); - if file_name_str.starts_with("app-") { - Some(file_name_str.to_string()) - } else { - None - } - }) - .collect(); - - if versions.is_empty() { - return Err("No discord versions found".into()); - } - - // app-1.0.9023 - - let mut sorted: Vec<(&String, u32)> = versions - .iter() - .filter_map(|v| { - let mut split = v.split('-'); - let version = split.nth(1).unwrap(); - let version = version.replace('.', ""); - let version = version.parse::(); - if let Ok(version) = version { - Some((v, version)) - } else { - None - } - }) - .collect(); - - sorted.sort_by(|(_, a), (_, b)| a.cmp(b)); - - let latest_version = sorted.last().unwrap().0; - - new_path.push(latest_version); - new_path.push(format!("{}.exe", executable_name)); - - if !new_path.exists() { - return Err("Discord.exe not found".into()); - } - - Ok(new_path) -} diff --git a/src/env.rs b/src/env.rs deleted file mode 100644 index 58482c4..0000000 --- a/src/env.rs +++ /dev/null @@ -1,118 +0,0 @@ -use std::path::Path; - -#[derive(Debug)] -pub struct Environment { - /// Path to the mods' JS entrypoint. - /// - /// e.g. "c:\users\megu\vencord\patcher.js" - pub mod_entrypoint: String, - - /// Path to check for to revert to default app.asar behaviour after the mod has loaded. - /// - /// e.g. "vencord\patcher.js" - pub toggle_query: Option, - - /// Custom name for AppData profile. - /// - /// e.g. "MyCustomProfile" - pub custom_data_dir: Option, - - /// ModHook ASAR replacement. - /// - /// e.g. "c:\users\megu\vencord\app.asar" - pub asar_path: Option, - - /// Modded ASAR filename. - /// - /// This is the file that the mod (e.g. Vencord) loads - /// to return to the original Discord context. - /// - /// ModHook will redirect calls to this file to the original app.asar (e.g. _app.asar -> app.asar) - /// - /// e.g. "_app.asar" - pub modded_asar_filename: Option, - - /// Whether or not the mod is the moonlight mod. - /// - /// Moonlight uses `require(entrypoint).inject(asarPath);` - /// instead of the usual `require(entrypoint);` - pub is_moonlight: bool, -} - -#[allow(dead_code)] -impl Environment { - /// Creates a new Environment struct from the current environment variables. - pub fn from_env() -> Self { - let mut env = Environment { - asar_path: None, - mod_entrypoint: std::env::var("MODHOOK_MOD_ENTRYPOINT").unwrap(), - toggle_query: None, - custom_data_dir: None, - modded_asar_filename: None, - is_moonlight: false, - }; - - if let Ok(path) = std::env::var("MODHOOK_ASAR_PATH") { - env.asar_path = Some(path); - } else { - let absolute_path = Path::new("app.asar").canonicalize().unwrap(); - env.asar_path = Some(absolute_path.to_str().unwrap().to_string()); - } - - if let Ok(path) = std::env::var("MODHOOK_TOGGLE_QUERY") { - env.toggle_query = Some(path.to_lowercase()); - } else { - env.toggle_query = Some(env.mod_entrypoint.clone()); - } - - if let Ok(path) = std::env::var("MODHOOK_CUSTOM_DATA_DIR") { - env.custom_data_dir = Some(path); - } - - if let Ok(file) = std::env::var("MODHOOK_MOD_ASAR_FILENAME") { - env.modded_asar_filename = Some(file); - } else { - env.modded_asar_filename = Some("_app.asar".to_string()); - } - - if let Ok(is_moonlight) = std::env::var("MODHOOK_IS_MOONLIGHT") { - env.is_moonlight = is_moonlight == "true"; - } - - env - } - - /// Applies the environment variables to the current process. - pub fn apply(&self) { - if let Some(path) = &self.asar_path { - std::env::set_var("MODHOOK_ASAR_PATH", path); - } - - std::env::set_var("MODHOOK_MOD_ENTRYPOINT", &self.mod_entrypoint); - - if let Some(path) = &self.custom_data_dir { - std::env::set_var("MODHOOK_CUSTOM_DATA_DIR", path); - } - - if let Some(query) = &self.toggle_query { - std::env::set_var("MODHOOK_TOGGLE_QUERY", query.to_lowercase()); - } else { - std::env::set_var("MODHOOK_TOGGLE_QUERY", &self.mod_entrypoint); - } - - if let Some(file) = &self.modded_asar_filename { - std::env::set_var("MODHOOK_MOD_ASAR_FILENAME", file); - } else { - std::env::set_var("MODHOOK_MOD_ASAR_FILENAME", "_app.asar"); - } - - // Disable auto patching of the Discord client. - // Currently supported mods: - // - Vencord - std::env::set_var("DISABLE_UPDATER_AUTO_PATCHING", "true"); - - if self.is_moonlight { - std::env::set_var("MODHOOK_IS_MOONLIGHT", "true"); - } - } -} diff --git a/src/lib.rs b/src/lib.rs index a92ae8f..fc073c4 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,62 +1,207 @@ -use std::{ffi::c_void, ptr::null_mut}; +// Since we're making a shared object, to make it executable, +// we need to create the .interp section. +electron_hook::make_shared_executable!(); -use detours_sys::{ - DetourAttach, DetourGetEntryPoint, DetourIsHelperProcess, DetourRestoreAfterWith, - DetourTransactionBegin, DetourTransactionCommit, DetourUpdateThread, -}; +use clap::Parser; -use winapi::{ - shared::minwindef::{BOOL, DWORD, HINSTANCE, LPVOID}, - um::{processthreadsapi::GetCurrentThread, winnt::DLL_PROCESS_ATTACH}, -}; +use modloader_core::config; -#[cfg(debug_assertions)] -use winapi::um::consoleapi::AllocConsole; -// use crate::detours::fs::create_asar_in_memory; +// Include the electron_hook shared library into this executable's exports. +pub use electron_hook::*; -mod detours; -mod env; +#[derive(clap::Parser, Debug)] +struct Args { + #[clap(short, long)] + pub profile: Option, -static mut O_ENTRYPOINT: *mut c_void = 0 as _; + #[clap(short, long)] + pub instance: Option, -#[no_mangle] -extern "cdecl" fn ExportedFunction() {} + #[clap(allow_hyphen_values = true, last = true)] + pub launch_args: Vec, + + #[clap(short, long, default_value = "false")] + pub force_update: bool, +} +#[cfg(target_os = "macos")] +pub fn main() { + println!("macOS is not supported yet. Feel free to submit a PR."); + println!("https://github.com/MeguminSama/Discord-Modloader"); +} + +#[cfg(not(target_os = "macos"))] #[no_mangle] -unsafe extern "stdcall" fn DllMain( - _hinst_dll: HINSTANCE, - fwd_reason: DWORD, - _lpv_reserved: LPVOID, -) -> BOOL { - if DetourIsHelperProcess() == 1 { - return 1; - } +fn main() { + println!("test"); + // TODO: Check if args are provided. If yes, don't load GUI. + let args: Args = Args::parse(); + + if let (Some(profile_id), Some(instance_id)) = (args.profile, args.instance) { + // When spawned from the GUI, the process is a child of the GUI process. + // We need to detach it from the GUI process to prevent it from being killed when the GUI is closed. + #[cfg(target_os = "linux")] + unsafe { + libc::setsid() + }; - if fwd_reason != DLL_PROCESS_ATTACH { - return 1; + let config = config::Config::init(); + + unsafe { + load_profile( + &config, + &profile_id, + &instance_id, + args.launch_args, + args.force_update, + ) + }; + } else { + gui::start_gui(); } +} + +#[cfg(target_os = "linux")] +unsafe fn load_profile( + config: &config::Config, + profile_id: &str, + instance_id: &str, + args: Vec, + force_update: bool, +) { + use modloader_core::{cache::create_instance_cache, paths}; + + let profile = config + .profiles + .get(profile_id) + .unwrap_or_else(|| panic!("Failed to find profile '{}'.", profile_id)); - DetourRestoreAfterWith(); + let asar_path = create_instance_cache(config, profile_id, instance_id, force_update).unwrap(); + let asar_path = asar_path.to_str().unwrap(); - DetourTransactionBegin(); - DetourUpdateThread(GetCurrentThread() as _); + let profile_directory = profile.profile.use_default_profile.then(|| { + paths::data_profiles_dir() + .join(profile_id) + .to_str() + .unwrap() + .to_string() + }); - O_ENTRYPOINT = DetourGetEntryPoint(null_mut()); - DetourAttach(&raw mut O_ENTRYPOINT, main as _); + let exit_code = electron_hook::launch_with_self( + &profile.discord.executable, + asar_path, + args, + profile_directory, + true, + ); - detours::win32::init_detours(); + dbg!(&exit_code); - DetourTransactionCommit(); + // let working_dir = if profile.profile.use_default_profile { + // std::path::Path::new(&profile.discord.executable) + // .parent() + // .unwrap() + // .to_str() + // .unwrap() + // .to_string() + // } else { + // let profile_dir = paths::ensure_dir(paths::data_profiles_dir().join(profile_id)); + // profile_dir.to_str().unwrap().to_string() + // }; - 1 + // // Thanks to linker magic (I went through hell to get this working), the executable can act as a shared-object in LD_PRELOAD + // let shared_object = std::env::current_exe().unwrap(); + + // let mut target = std::process::Command::new(&profile.discord.executable) + // .current_dir(working_dir) + // .env("LD_PRELOAD", shared_object) + // .env("MODLOADER_ASAR_PATH", asar_path) + // .args(args) + // .spawn() + // .expect("Failed to launch instance."); + + // target + // .wait() + // .expect("Failed to wait for instance to finish."); } -unsafe fn main() { - #[cfg(debug_assertions)] - { - AllocConsole(); - println!("[ModHook] Process Hooked"); +#[cfg(target_os = "windows")] +unsafe fn load_profile( + config: &config::Config, + instance: &config::Instance, + args: Vec, + force_update: bool, +) { + // TODO: Implement args on windows + use detours_sys::{DetourCreateProcessWithDllExA, _PROCESS_INFORMATION, _STARTUPINFOA}; + use libdiscordmodloader::discord::get_discord_exe; + use winapi::um::{ + handleapi::CloseHandle, + processthreadsapi::ResumeThread, + winbase::CREATE_SUSPENDED, + winuser::{MessageBoxA, MB_ICONERROR}, + }; + + println!("Loading Instance: {}", instance.name); + if let Some(ref profile_path) = instance.profile_path { + println!("On profile: {}", profile_path) } - let start_discord: extern "C" fn() = std::mem::transmute(O_ENTRYPOINT); - start_discord(); + + let asar_path = init_current_cache(instance, config.mods.get(&instance.r#mod).unwrap()); + + let current_exe = std::env::current_exe().unwrap(); + let lp_current_directory = current_exe.parent().unwrap().to_str().unwrap(); + let dll = current_exe.with_file_name("libdiscordmodloader.dll"); + + if !dll.exists() { + MessageBoxA( + std::ptr::null_mut(), + c"libdiscordmodloader.dll not found.\nPlease verify your installation.".as_ptr(), + c"Error loading modloader".as_ptr(), + MB_ICONERROR, + ); + panic!("libdiscordmodloader.dll not found."); + } + + let discord_exe = get_discord_exe(&instance.path).expect("Failed to get Discord executable."); + + std::env::set_var("MODLOADER_ASAR_PATH", asar_path); + std::env::set_var("MODLOADER_DLL_PATH", &dll); + + let dll = std::ffi::CString::new(dll.to_str().unwrap()).unwrap(); + let lp_current_directory = std::ffi::CString::new(lp_current_directory).unwrap(); + + let mut process_info: _PROCESS_INFORMATION = unsafe { std::mem::zeroed() }; + let mut startup_info: _STARTUPINFOA = unsafe { std::mem::zeroed() }; + let discord_exe = std::ffi::CString::new(discord_exe.to_str().unwrap()).unwrap(); + + let result = DetourCreateProcessWithDllExA( + std::ptr::null_mut(), + discord_exe.as_ptr() as *mut i8, + std::ptr::null_mut(), + std::ptr::null_mut(), + 0, + CREATE_SUSPENDED, + std::ptr::null_mut(), + lp_current_directory.as_ptr(), + &raw mut startup_info, + &raw mut process_info, + dll.as_ptr(), + None, + ); + + if result == 0 { + MessageBoxA( + std::ptr::null_mut(), + c"Failed to inject DLL into Discord".as_ptr(), + c"Error launching Discord".as_ptr(), + MB_ICONERROR, + ); + panic!("Failed to create process with DLL."); + } + + ResumeThread(process_info.hThread); + + CloseHandle(process_info.hProcess); + CloseHandle(process_info.hThread); } diff --git a/src/main.rs b/src/main.rs index f4f627a..1992cb4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,138 +1,2 @@ -use std::{ffi::CString, mem::MaybeUninit, ptr::null_mut}; - -use clap::Parser; -use detours_sys::{DetourCreateProcessWithDllExA, _PROCESS_INFORMATION, _STARTUPINFOA}; -use env::Environment; -use winapi::um::{ - handleapi::CloseHandle, processthreadsapi::ResumeThread, winbase::CREATE_SUSPENDED, -}; - -mod discord; -mod env; - -static DLL_PATH: &str = "libmodhook.dll"; - -#[derive(Parser, Debug)] -#[command(name = "modhook", verbatim_doc_comment)] -/// Discord ModHook -/// For more information, visit: -pub struct Args { - /// Path to the Discord folder. - /// Example: --discord-path "c:\users\megu\appdata\roaming\discordptb" - #[arg(short, long, verbatim_doc_comment)] - pub discord_path: String, - - /// Path to the mods' JS entrypoint. - /// Example: --mod-entrypoint "c:\users\megu\vencord\patcher.js" - #[arg(short, long, verbatim_doc_comment)] - pub mod_entrypoint: String, - - /// Modded ASAR filename. - /// - /// This is the file that the mod (e.g. Vencord) loads - /// to return to the original Discord context. - /// - /// This is NOT required for Vencord or Replugged. - /// - /// ModHook will redirect calls to this file to the original app.asar (e.g. _app.asar -> app.asar) - /// - /// example: --modded-asar-filename "_app.asar" - #[arg(short = 'f', long, verbatim_doc_comment)] - pub modded_asar_filename: Option, - - /// Path to check for to revert to default app.asar behaviour after the mod has loaded. - /// Example: --toggle-query "vencord\patcher.js" - #[arg(short, long, verbatim_doc_comment)] - pub toggle_query: Option, - - /// Custom name for AppData profile. - /// Example: --custom-data-dir "MyCustomProfile" - #[arg(short, long, verbatim_doc_comment)] - pub custom_data_dir: Option, - - /// ModHook ASAR replacement. - /// Example: --asar-path "c:\users\megu\vencord\app.asar" - #[arg(short, long, verbatim_doc_comment)] - pub asar_path: Option, - - /// Whether or not the mod is the moonlight mod. - /// Example: --moonlight - /// Moonlight uses `require(entrypoint).inject(asarPath);` - /// instead of the usual `require(entrypoint);` - #[arg(value_enum, long = "moonlight", verbatim_doc_comment)] - pub is_moonlight: bool, -} - -fn main() { - let args: Args = Args::parse(); - - let target_exe = discord::get_discord_executable(&args.discord_path).unwrap(); - let target_exe = target_exe.to_str().unwrap(); - - let mut asar_path = std::env::current_dir().unwrap(); - - asar_path.push("app.asar"); - - let asar_path = asar_path.to_str().unwrap().to_string(); - - let mut dll_path = std::env::current_dir().unwrap(); - dll_path.push(DLL_PATH); - - let environment = Environment { - asar_path: Some(asar_path), - mod_entrypoint: args.mod_entrypoint, - toggle_query: args.toggle_query, - custom_data_dir: args.custom_data_dir, - modded_asar_filename: args.modded_asar_filename, - is_moonlight: args.is_moonlight, - }; - - unsafe { - inject(dll_path.to_str().unwrap(), target_exe, &environment).unwrap(); - } -} - -/// # Safety -/// This function is unsafe because it calls the WinAPI. -pub unsafe fn inject( - dll_path: &str, - target_exe: &str, - environment: &Environment, -) -> std::io::Result<()> { - let cstr_target_exe = CString::new(target_exe)?; - let cstr_dll_path = CString::new(dll_path)?; - - let mut process_info: _PROCESS_INFORMATION = MaybeUninit::zeroed().assume_init(); - let mut startup_info: _STARTUPINFOA = MaybeUninit::zeroed().assume_init(); - - environment.apply(); - - let result = DetourCreateProcessWithDllExA( - null_mut(), - cstr_target_exe.as_ptr() as *mut i8, - null_mut(), - null_mut(), - 0, - CREATE_SUSPENDED, - null_mut(), - null_mut(), - &mut startup_info as *mut _, - &mut process_info as *mut _, - cstr_dll_path.as_ptr(), - None, - ); - - if result == 0 { - return Err(std::io::Error::new( - std::io::ErrorKind::Other, - "Failed to create process", - )); - } - - ResumeThread(process_info.hThread as _); - - CloseHandle(process_info.hProcess as _); - CloseHandle(process_info.hThread as _); - - Ok(()) -} +#![no_main] +pub use discord_modloader::*;